Merge changes from topic "choreo"
* changes:
Choreographer: add new NDK APIs for callback data.
Plumb expected present time to Choreographer.
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index be2c702..8f1c01a 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -52,7 +52,7 @@
}
struct SecurityContext_Delete {
- void operator()(security_context_t p) const {
+ void operator()(char* p) const {
freecon(p);
}
};
@@ -108,7 +108,7 @@
}
if (is_selinux_enabled() && seLinuxContext.size() > 0) {
String8 seLinuxContext8(seLinuxContext);
- security_context_t tmp = nullptr;
+ char* tmp = nullptr;
getfilecon(fullPath.string(), &tmp);
Unique_SecurityContext context(tmp);
if (checkWrite) {
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index fa63db5..677d6c7 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -60,6 +60,7 @@
MOCK_METHOD1(isDeclared, bool(const String16&));
MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&));
+ MOCK_METHOD1(getConnectionInfo, std::optional<ConnectionInfo>(const String16&));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 0c3fb96..863490d 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -82,6 +82,7 @@
continue;
}
out->write(buffer, n);
+ continue;
}
if (pfd[0].revents & POLLHUP) {
break;
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 6f08f74..cba7c4b 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -22,9 +22,10 @@
#include <thread>
#include <vector>
-#include <gtest/gtest.h>
-#include <gmock/gmock.h>
+#include <android-base/parseint.h>
#include <android/hardware/tests/inheritance/1.0/IChild.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
#include <hidl/HidlTransportSupport.h>
#include <vintf/parse_xml.h>
@@ -77,6 +78,13 @@
content += "\n";
content += option.c_str();
}
+ if (options.size() > 0) {
+ uint64_t len;
+ if (android::base::ParseUint(options[0], &len)) {
+ content += "\n";
+ content += std::string(len, 'X');
+ }
+ }
ssize_t written = write(fd, content.c_str(), content.size());
if (written != (ssize_t)content.size()) {
LOG(WARNING) << "SERVER(Child) debug writes " << written << " bytes < "
@@ -189,6 +197,16 @@
EXPECT_THAT(err.str(), HasSubstr("does not exist"));
}
+TEST_F(DebugTest, DebugLarge) {
+ EXPECT_EQ(0u, callMain(lshal, {
+ "lshal", "debug", "android.hardware.tests.inheritance@1.0::IChild/default", "10000"
+ }));
+ EXPECT_THAT(out.str(),
+ StrEq("android.hardware.tests.inheritance@1.0::IChild\n10000\n" +
+ std::string(10000, 'X')));
+ EXPECT_THAT(err.str(), IsEmpty());
+}
+
TEST_F(DebugTest, DebugParent) {
EXPECT_EQ(0u, callMain(lshal, {
"lshal", "debug", "android.hardware.tests.inheritance@1.0::IParent", "calling parent"
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 90db509..4e44ac7 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -121,6 +121,35 @@
return updatableViaApex;
}
+static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& name) {
+ AidlName aname;
+ if (!AidlName::fill(name, &aname)) return std::nullopt;
+
+ std::optional<std::string> ip;
+ std::optional<uint64_t> port;
+ forEachManifest([&](const ManifestWithDescription& mwd) {
+ mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
+ if (manifestInstance.format() != vintf::HalFormat::AIDL) return true;
+ if (manifestInstance.package() != aname.package) return true;
+ if (manifestInstance.interface() != aname.iface) return true;
+ if (manifestInstance.instance() != aname.instance) return true;
+ ip = manifestInstance.ip();
+ port = manifestInstance.port();
+ return false; // break (libvintf uses opposite convention)
+ });
+ return false; // continue
+ });
+
+ if (ip.has_value() && port.has_value()) {
+ ConnectionInfo info;
+ info.ipAddress = *ip;
+ info.port = *port;
+ return std::make_optional<ConnectionInfo>(info);
+ } else {
+ return std::nullopt;
+ }
+}
+
static std::vector<std::string> getVintfInstances(const std::string& interface) {
size_t lastDot = interface.rfind('.');
if (lastDot == std::string::npos) {
@@ -437,6 +466,22 @@
return Status::ok();
}
+Status ServiceManager::getConnectionInfo(const std::string& name,
+ std::optional<ConnectionInfo>* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ if (!mAccess->canFind(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ *outReturn = std::nullopt;
+
+#ifndef VENDORSERVICEMANAGER
+ *outReturn = getVintfConnectionInfo(name);
+#endif
+ return Status::ok();
+}
+
void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 4f23c21..5e40319 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -24,6 +24,7 @@
namespace android {
+using os::ConnectionInfo;
using os::IClientCallback;
using os::IServiceCallback;
using os::ServiceDebugInfo;
@@ -48,6 +49,8 @@
binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
binder::Status updatableViaApex(const std::string& name,
std::optional<std::string>* outReturn) override;
+ binder::Status getConnectionInfo(const std::string& name,
+ std::optional<ConnectionInfo>* outReturn) override;
binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) override;
binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
diff --git a/include/ftl/initializer_list.h b/include/ftl/initializer_list.h
index 769c09f..2102c25 100644
--- a/include/ftl/initializer_list.h
+++ b/include/ftl/initializer_list.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <tuple>
#include <utility>
@@ -65,18 +66,18 @@
std::tuple<Types...> tuple;
};
-template <typename K, typename V>
+template <typename K, typename V, typename KeyEqual = std::equal_to<K>>
struct KeyValue {};
// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the
// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works
// with the latter.
-template <typename K, typename V, std::size_t... Sizes, typename... Types>
-struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> {
+template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types>
+struct InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...> {
// Accumulate the three arguments to std::pair's piecewise constructor.
template <typename... Args>
[[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList<
- KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
+ KeyValue<K, V, E>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
std::tuple<K&&>, std::tuple<Args&&...>> {
return {std::tuple_cat(
std::move(tuple),
@@ -94,9 +95,9 @@
return InitializerList<T>{}(std::forward<Args>(args)...);
}
-template <typename K, typename V, typename... Args>
+template <typename K, typename V, typename E = std::equal_to<K>, typename... Args>
[[nodiscard]] constexpr auto map(Args&&... args) {
- return list<KeyValue<K, V>>(std::forward<Args>(args)...);
+ return list<KeyValue<K, V, E>>(std::forward<Args>(args)...);
}
template <typename K, typename V>
diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h
index bcaba82..2effaa4 100644
--- a/include/ftl/small_map.h
+++ b/include/ftl/small_map.h
@@ -61,7 +61,7 @@
//
// assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
//
-template <typename K, typename V, std::size_t N>
+template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>>
class SmallMap final {
using Map = SmallVector<std::pair<const K, V>, N>;
@@ -153,7 +153,7 @@
auto get(const key_type& key, F f) const
-> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
for (auto& [k, v] : *this) {
- if (k == key) {
+ if (KeyEqual{}(k, key)) {
if constexpr (std::is_void_v<R>) {
f(v);
return true;
@@ -237,9 +237,16 @@
//
bool erase(const key_type& key) { return erase(key, begin()); }
+ // Removes all mappings.
+ //
+ // All iterators are invalidated.
+ //
+ void clear() { map_.clear(); }
+
private:
iterator find(const key_type& key, iterator first) {
- return std::find_if(first, end(), [&key](const auto& pair) { return pair.first == key; });
+ return std::find_if(first, end(),
+ [&key](const auto& pair) { return KeyEqual{}(pair.first, key); });
}
bool erase(const key_type& key, iterator first) {
@@ -261,13 +268,13 @@
};
// Deduction guide for in-place constructor.
-template <typename K, typename V, std::size_t... Sizes, typename... Types>
-SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&)
- -> SmallMap<K, V, sizeof...(Sizes)>;
+template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types>
+SmallMap(InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...>&&)
+ -> SmallMap<K, V, sizeof...(Sizes), E>;
// Returns whether the key-value pairs of two maps are equal.
-template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
-bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E>
+bool operator==(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) {
if (lhs.size() != rhs.size()) return false;
for (const auto& [k, v] : lhs) {
@@ -281,8 +288,8 @@
}
// TODO: Remove in C++20.
-template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
-inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E>
+inline bool operator!=(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) {
return !(lhs == rhs);
}
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
index 0341435..65a9536 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -151,8 +151,6 @@
DISPATCH(reference, back, noexcept)
DISPATCH(const_reference, back, const)
-#undef DISPATCH
-
reference operator[](size_type i) {
return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i];
}
@@ -214,13 +212,15 @@
//
// The last() and end() iterators are invalidated.
//
- void pop_back() {
- if (dynamic()) {
- std::get<Dynamic>(vector_).pop_back();
- } else {
- std::get<Static>(vector_).pop_back();
- }
- }
+ DISPATCH(void, pop_back, noexcept)
+
+ // Removes all elements.
+ //
+ // All iterators are invalidated.
+ //
+ DISPATCH(void, clear, noexcept)
+
+#undef DISPATCH
// Erases an element, but does not preserve order. Rather than shifting subsequent elements,
// this moves the last element to the slot of the erased element.
@@ -345,6 +345,7 @@
return true;
}
+ using Impl::clear;
using Impl::pop_back;
void unstable_erase(iterator it) {
diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h
index 96a1ae8..cd7b92a 100644
--- a/include/ftl/static_vector.h
+++ b/include/ftl/static_vector.h
@@ -189,8 +189,7 @@
}
StaticVector& operator=(StaticVector&& other) {
- std::destroy(begin(), end());
- size_ = 0;
+ clear();
swap<true>(other);
return *this;
}
@@ -280,6 +279,15 @@
//
void pop_back() { unstable_erase(last()); }
+ // Removes all elements.
+ //
+ // All iterators are invalidated.
+ //
+ void clear() {
+ std::destroy(begin(), end());
+ size_ = 0;
+ }
+
// Erases an element, but does not preserve order. Rather than shifting subsequent elements,
// this moves the last element to the slot of the erased element.
//
diff --git a/include/input/Input.h b/include/input/Input.h
index d2d9fd4..d397313 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -524,13 +524,17 @@
inline int32_t getAction() const { return mAction; }
- inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
+ static int32_t getActionMasked(int32_t action) { return action & AMOTION_EVENT_ACTION_MASK; }
- inline int32_t getActionIndex() const {
- return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
- >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ inline int32_t getActionMasked() const { return getActionMasked(mAction); }
+
+ static int32_t getActionIndex(int32_t action) {
+ return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+ AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
+ inline int32_t getActionIndex() const { return getActionIndex(mAction); }
+
inline void setAction(int32_t action) { mAction = action; }
inline int32_t getFlags() const { return mFlags; }
@@ -575,9 +579,7 @@
void setCursorPosition(float x, float y);
- uint32_t getDisplayOrientation() const { return mDisplayOrientation; }
-
- int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; }
+ ui::Transform getRawTransform() const { return mRawTransform; }
static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
@@ -753,8 +755,8 @@
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float rawXCursorPosition,
- float rawYCursorPosition, uint32_t displayOrientation, int32_t displayWidth,
- int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ float rawYCursorPosition, const ui::Transform& rawTransform, nsecs_t downTime,
+ nsecs_t eventTime, size_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
void copyFrom(const MotionEvent* other, bool keepHistory);
@@ -812,9 +814,7 @@
float mYPrecision;
float mRawXCursorPosition;
float mRawYCursorPosition;
- uint32_t mDisplayOrientation;
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
+ ui::Transform mRawTransform;
nsecs_t mDownTime;
Vector<PointerProperties> mPointerProperties;
std::vector<nsecs_t> mSampleEventTimes;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 7632b30..d655b28 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -114,7 +114,7 @@
struct Motion {
int32_t eventId;
- uint32_t empty1;
+ uint32_t pointerCount;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -129,20 +129,22 @@
uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
- float dsdx;
- float dtdx;
- float dtdy;
- float dsdy;
- float tx;
- float ty;
+ float dsdx; // Begin window transform
+ float dtdx; //
+ float dtdy; //
+ float dsdy; //
+ float tx; //
+ float ty; // End window transform
float xPrecision;
float yPrecision;
float xCursorPosition;
float yCursorPosition;
- uint32_t displayOrientation;
- int32_t displayWidth;
- int32_t displayHeight;
- uint32_t pointerCount;
+ float dsdxRaw; // Begin raw transform
+ float dtdxRaw; //
+ float dtdyRaw; //
+ float dsdyRaw; //
+ float txRaw; //
+ float tyRaw; // End raw transform
/**
* The "pointers" field must be the last field of the struct InputMessage.
* When we send the struct InputMessage across the socket, we are not
@@ -367,9 +369,8 @@
int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, uint32_t displayOrientation,
- int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
- nsecs_t eventTime, uint32_t pointerCount,
+ float yCursorPosition, const ui::Transform& rawTransform,
+ nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
index 68e0883..125cfaf 100644
--- a/libs/battery/LongArrayMultiStateCounter.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -62,7 +62,7 @@
template <>
std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
std::stringstream s;
- s << "{ ";
+ s << "{";
bool first = true;
for (uint64_t n : v) {
if (!first) {
diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp
index 24cb437..e4e6b2a 100644
--- a/libs/battery/LongArrayMultiStateCounterTest.cpp
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -24,7 +24,9 @@
class LongArrayMultiStateCounterTest : public testing::Test {};
TEST_F(LongArrayMultiStateCounterTest, stateChange) {
- LongArrayMultiStateCounter testCounter(2, 0, std::vector<uint64_t>(4), 1000);
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
@@ -34,7 +36,9 @@
}
TEST_F(LongArrayMultiStateCounterTest, accumulation) {
- LongArrayMultiStateCounter testCounter(2, 0, std::vector<uint64_t>(4), 1000);
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
testCounter.setState(1, 2000);
testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
testCounter.setState(0, 4000);
@@ -50,5 +54,16 @@
EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
}
+TEST_F(LongArrayMultiStateCounterTest, toString) {
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
+ testCounter.setState(1, 2000);
+ testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+
+ EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
+ testCounter.toString().c_str());
+}
+
} // namespace battery
} // namespace android
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
index 9f56b29..112fb85 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -42,6 +42,7 @@
T lastValue;
time_t lastUpdateTimestamp;
T deltaValue;
+ bool isEnabled;
struct State {
time_t timeInStateSinceUpdate;
@@ -51,14 +52,27 @@
State* states;
public:
- MultiStateCounter(uint16_t stateCount, state_t initialState, const T& emptyValue,
- time_t timestamp);
+ MultiStateCounter(uint16_t stateCount, const T& emptyValue);
virtual ~MultiStateCounter();
+ void setEnabled(bool enabled, time_t timestamp);
+
void setState(state_t state, time_t timestamp);
- void updateValue(const T& value, time_t timestamp);
+ void setValue(state_t state, const T& value);
+
+ /**
+ * Updates the value for the current state and returns the delta from the previously
+ * set value.
+ */
+ const T& updateValue(const T& value, time_t timestamp);
+
+ void addValue(const T& value);
+
+ void reset();
+
+ uint16_t getStateCount();
const T& getCount(state_t state);
@@ -86,15 +100,15 @@
// Since MultiStateCounter is a template, the implementation must be inlined.
template <class T>
-MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, state_t initialState,
- const T& emptyValue, time_t timestamp)
+MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
: stateCount(stateCount),
- currentState(initialState),
- lastStateChangeTimestamp(timestamp),
+ currentState(0),
+ lastStateChangeTimestamp(-1),
emptyValue(emptyValue),
lastValue(emptyValue),
- lastUpdateTimestamp(timestamp),
- deltaValue(emptyValue) {
+ lastUpdateTimestamp(-1),
+ deltaValue(emptyValue),
+ isEnabled(true) {
states = new State[stateCount];
for (int i = 0; i < stateCount; i++) {
states[i].timeInStateSinceUpdate = 0;
@@ -108,18 +122,39 @@
};
template <class T>
+void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
+ if (enabled == isEnabled) {
+ return;
+ }
+
+ if (!enabled) {
+ // Confirm the current state for the side-effect of updating the time-in-state
+ // counter for the current state.
+ setState(currentState, timestamp);
+ }
+
+ isEnabled = enabled;
+
+ if (lastStateChangeTimestamp >= 0) {
+ lastStateChangeTimestamp = timestamp;
+ }
+}
+
+template <class T>
void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
- if (timestamp >= lastStateChangeTimestamp) {
- states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
- } else {
- ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
- (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
- // The accumulated durations have become unreliable. For example, if the timestamp
- // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
- // we would get 4000, which is greater than (last - first). This could lead to
- // counts exceeding 100%.
- for (int i = 0; i < stateCount; i++) {
- states[i].timeInStateSinceUpdate = 0;
+ if (isEnabled && lastStateChangeTimestamp >= 0) {
+ if (timestamp >= lastStateChangeTimestamp) {
+ states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
+ } else {
+ ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
+ // The accumulated durations have become unreliable. For example, if the timestamp
+ // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
+ // we would get 4000, which is greater than (last - first). This could lead to
+ // counts exceeding 100%.
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
}
}
currentState = state;
@@ -127,33 +162,70 @@
}
template <class T>
-void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
- // Confirm the current state for the side-effect of updating the time-in-state
- // counter for the current state.
- setState(currentState, timestamp);
+void MultiStateCounter<T>::setValue(state_t state, const T& value) {
+ states[state].counter = value;
+}
- if (timestamp > lastUpdateTimestamp) {
- if (delta(lastValue, value, &deltaValue)) {
- time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
- for (int i = 0; i < stateCount; i++) {
- time_t timeInState = states[i].timeInStateSinceUpdate;
- if (timeInState) {
- add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
- states[i].timeInStateSinceUpdate = 0;
+template <class T>
+const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
+ // If the counter is disabled, we ignore the update, except when the counter got disabled after
+ // the previous update, in which case we still need to pick up the residual delta.
+ if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
+ // Confirm the current state for the side-effect of updating the time-in-state
+ // counter for the current state.
+ setState(currentState, timestamp);
+
+ if (lastUpdateTimestamp >= 0) {
+ if (timestamp > lastUpdateTimestamp) {
+ if (delta(lastValue, value, &deltaValue)) {
+ time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
+ for (int i = 0; i < stateCount; i++) {
+ time_t timeInState = states[i].timeInStateSinceUpdate;
+ if (timeInState) {
+ add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ } else {
+ std::stringstream str;
+ str << "updateValue is called with a value " << valueToString(value)
+ << ", which is lower than the previous value " << valueToString(lastValue)
+ << "\n";
+ ALOGE("%s", str.str().c_str());
}
+ } else if (timestamp < lastUpdateTimestamp) {
+ ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
}
- } else {
- std::stringstream str;
- str << "updateValue is called with a value " << valueToString(value)
- << ", which is lower than the previous value " << valueToString(lastValue) << "\n";
- ALOGE("%s", str.str().c_str());
}
- } else if (timestamp < lastUpdateTimestamp) {
- ALOGE("updateValue is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
- (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
}
lastValue = value;
lastUpdateTimestamp = timestamp;
+ return deltaValue;
+}
+
+template <class T>
+void MultiStateCounter<T>::addValue(const T& value) {
+ if (!isEnabled) {
+ return;
+ }
+
+ add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
+}
+
+template <class T>
+void MultiStateCounter<T>::reset() {
+ lastStateChangeTimestamp = -1;
+ lastUpdateTimestamp = -1;
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ states[i].counter = emptyValue;
+ }
+}
+
+template <class T>
+uint16_t MultiStateCounter<T>::getStateCount() {
+ return stateCount;
}
template <class T>
@@ -164,17 +236,31 @@
template <class T>
std::string MultiStateCounter<T>::toString() {
std::stringstream str;
- str << "currentState: " << currentState
- << " lastStateChangeTimestamp: " << lastStateChangeTimestamp
- << " lastUpdateTimestamp: " << lastUpdateTimestamp << " states: [";
+ str << "[";
for (int i = 0; i < stateCount; i++) {
if (i != 0) {
str << ", ";
}
- str << i << ": time: " << states[i].timeInStateSinceUpdate
- << " counter: " << valueToString(states[i].counter);
+ str << i << ": " << valueToString(states[i].counter);
+ if (states[i].timeInStateSinceUpdate > 0) {
+ str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
+ }
}
str << "]";
+ if (lastUpdateTimestamp >= 0) {
+ str << " updated: " << lastUpdateTimestamp;
+ }
+ if (lastStateChangeTimestamp >= 0) {
+ str << " currentState: " << currentState;
+ if (lastStateChangeTimestamp > lastUpdateTimestamp) {
+ str << " stateChanged: " << lastStateChangeTimestamp;
+ }
+ } else {
+ str << " currentState: none";
+ }
+ if (!isEnabled) {
+ str << " disabled";
+ }
return str.str();
}
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
index 942d5ca..848fd10 100644
--- a/libs/battery/MultiStateCounterTest.cpp
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -49,17 +49,21 @@
class MultiStateCounterTest : public testing::Test {};
TEST_F(MultiStateCounterTest, constructor) {
- DoubleMultiStateCounter testCounter(3, 1, 0, 1000);
- testCounter.setState(1, 2000);
- testCounter.updateValue(3.14, 3000);
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ double delta = testCounter.updateValue(3.14, 3000);
EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+ EXPECT_DOUBLE_EQ(3.14, delta);
}
TEST_F(MultiStateCounterTest, stateChange) {
- DoubleMultiStateCounter testCounter(3, 1, 0, 0);
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
testCounter.setState(2, 1000);
testCounter.updateValue(6.0, 3000);
@@ -68,8 +72,87 @@
EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
}
+TEST_F(MultiStateCounterTest, setEnabled) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setEnabled(false, 1000);
+ testCounter.setState(2, 2000);
+ testCounter.updateValue(6.0, 3000);
+
+ // In state 1: accumulated 1000 before disabled, that's 6.0 * 1000/3000 = 2.0
+ // In state 2: 0, since it is still disabled
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ // Should have no effect since the counter is disabled
+ testCounter.setState(0, 3500);
+
+ // Should have no effect since the counter is disabled
+ testCounter.updateValue(10.0, 4000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ testCounter.setState(2, 4500);
+
+ // Enable the counter to partially accumulate deltas for the current state, 2
+ testCounter.setEnabled(true, 5000);
+ testCounter.setEnabled(false, 6000);
+ testCounter.setEnabled(true, 7000);
+ testCounter.updateValue(20.0, 8000);
+
+ // The delta is 10.0 over 5000-3000=2000.
+ // Counter has been enabled in state 2 for (6000-5000)+(8000-7000) = 2000,
+ // so its share is (20.0-10.0) * 2000/(8000-4000) = 5.0
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(5.0, testCounter.getCount(2));
+
+ testCounter.reset();
+ testCounter.setState(0, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 2000);
+ testCounter.setEnabled(false, 3000);
+ testCounter.updateValue(200, 5000);
+
+ // 200 over 5000 = 40 per second
+ // Counter was in state 0 from 0 to 2000, so 2 sec, so the count should be 40 * 2 = 80
+ // It stayed in state 1 from 2000 to 3000, at which point the counter was disabled,
+ // so the count for state 1 should be 40 * 1 = 40.
+ // The remaining 2 seconds from 3000 to 5000 don't count because the counter was disabled.
+ EXPECT_DOUBLE_EQ(80.0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(40.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, reset) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.updateValue(2.72, 3000);
+
+ testCounter.reset();
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ // Assert that we can still continue accumulating after a reset
+ testCounter.updateValue(0, 4000);
+ testCounter.updateValue(3.14, 5000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
TEST_F(MultiStateCounterTest, timeAdjustment_setState) {
- DoubleMultiStateCounter testCounter(3, 1, 0, 0);
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
testCounter.setState(2, 2000);
// Time moves back
@@ -88,17 +171,56 @@
}
TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) {
- DoubleMultiStateCounter testCounter(1, 0, 0, 0);
+ DoubleMultiStateCounter testCounter(1, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
testCounter.updateValue(6.0, 2000);
// Time moves back. The negative delta from 2000 to 1000 is ignored
testCounter.updateValue(8.0, 1000);
- testCounter.updateValue(11.0, 3000);
+ double delta = testCounter.updateValue(11.0, 3000);
// The total accumulated count is:
// 6.0 // For the period 0-2000
// +(11.0-8.0) // For the period 1000-3000
EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+
+ // 11.0-8.0
+ EXPECT_DOUBLE_EQ(3.0, delta);
+}
+
+TEST_F(MultiStateCounterTest, addValue) {
+ DoubleMultiStateCounter testCounter(1, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ testCounter.addValue(8.0);
+
+ EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0));
+
+ testCounter.setEnabled(false, 3000);
+ testCounter.addValue(888.0);
+
+ EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0));
+}
+
+TEST_F(MultiStateCounterTest, toString) {
+ DoubleMultiStateCounter testCounter(2, 0);
+
+ EXPECT_STREQ("[0: 0.000000, 1: 0.000000] currentState: none", testCounter.toString().c_str());
+
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setState(1, 2000);
+ EXPECT_STREQ("[0: 0.000000, 1: 0.000000 timeInStateSinceUpdate: 2000]"
+ " updated: 0 currentState: 1 stateChanged: 2000",
+ testCounter.toString().c_str());
+
+ testCounter.updateValue(3.14, 3000);
+
+ EXPECT_STREQ("[0: 0.000000, 1: 3.140000] updated: 3000 currentState: 1",
+ testCounter.toString().c_str());
}
} // namespace battery
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 06594d7..89a31c5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -250,13 +250,16 @@
export_header_lib_headers: [
"libbinder_headers",
],
+ export_shared_lib_headers: [
+ "libssl",
+ ],
export_include_dirs: ["include_tls"],
static_libs: [
"libbase",
],
srcs: [
"RpcTransportTls.cpp",
- "RpcCertificateUtils.cpp",
+ "RpcTlsUtils.cpp",
],
}
@@ -278,6 +281,7 @@
filegroup {
name: "libbinder_aidl",
srcs: [
+ "aidl/android/os/ConnectionInfo.aidl",
"aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 92df874..06542f0 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -48,10 +48,6 @@
// Another arbitrary value a binder count needs to drop below before another callback will be called
uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000;
-// Once the limit has been exceeded, keep calling the limit callback for every this many new proxies
-// created over the limit.
-constexpr uint32_t REPEAT_LIMIT_CALLBACK_INTERVAL = 1000;
-
enum {
LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached
COUNTING_VALUE_MASK = 0x7FFFFFFF, // A mask of the remaining bits for the count value
@@ -129,7 +125,7 @@
uint32_t lastLimitCallbackAt = sLastLimitCallbackMap[trackedUid];
if (trackedValue > lastLimitCallbackAt &&
- (trackedValue - lastLimitCallbackAt > REPEAT_LIMIT_CALLBACK_INTERVAL)) {
+ (trackedValue - lastLimitCallbackAt > sBinderProxyCountHighWatermark)) {
ALOGE("Still too many binder proxy objects sent to uid %d from uid %d (%d proxies "
"held)",
getuid(), trackedUid, trackedValue);
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index b197a6a..49f83ff 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -53,25 +53,10 @@
continue;
}
if (pfd[1].revents & POLLHUP) {
- return -ECANCELED;
+ return DEAD_OBJECT;
}
return pfd[0].revents & event ? OK : DEAD_OBJECT;
}
}
-android::base::Result<bool> FdTrigger::isTriggeredPolled() {
- pollfd pfd{.fd = mRead.get(), .events = 0, .revents = 0};
- int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, 0));
- if (ret < 0) {
- return android::base::ErrnoError() << "FdTrigger::isTriggeredPolled: Error in poll()";
- }
- if (ret == 0) {
- return false;
- }
- if (pfd.revents & POLLHUP) {
- return true;
- }
- return android::base::Error() << "FdTrigger::isTriggeredPolled: poll() returns " << pfd.revents;
-}
-
} // namespace android
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index a428417..a545d6c 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -35,9 +35,13 @@
void trigger();
/**
- * Check whether this has been triggered by checking the write end.
+ * Check whether this has been triggered by checking the write end. Note:
+ * this has no internal locking, and it is inherently racey, but this is
+ * okay, because if we accidentally return false when a trigger has already
+ * happened, we can imagine that instead, the scheduler actually executed
+ * the code which is polling isTriggered earlier.
*/
- bool isTriggered();
+ [[nodiscard]] bool isTriggered();
/**
* Poll for a read event.
@@ -48,17 +52,7 @@
* true - time to read!
* false - trigger happened
*/
- status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
-
- /**
- * Check whether this has been triggered by poll()ing the read end.
- *
- * Return:
- * true - triggered
- * false - not triggered
- * error - error when polling
- */
- android::base::Result<bool> isTriggeredPolled();
+ [[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event);
private:
base::unique_fd mWrite;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 9e04ffe..55d3d70 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1410,23 +1410,6 @@
}
}
-status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, bool *sync_received, bool *async_received)
-{
- int ret = 0;
- binder_frozen_status_info info;
- info.pid = pid;
-
-#if defined(__ANDROID__)
- if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
- ret = -errno;
-#endif
- *sync_received = info.sync_recv;
- *async_received = info.async_recv;
-
- return ret;
-}
-
-#ifndef __ANDROID_VNDK__
status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
uint32_t *async_received)
{
@@ -1443,7 +1426,6 @@
return ret;
}
-#endif
status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
struct binder_freeze_info info;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 6e318ea..aff9e0d 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -78,6 +78,7 @@
bool isDeclared(const String16& name) override;
Vector<String16> getDeclaredInstances(const String16& interface) override;
std::optional<String16> updatableViaApex(const String16& name) override;
+ std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override;
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
@@ -426,6 +427,21 @@
return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
}
+std::optional<IServiceManager::ConnectionInfo> ServiceManagerShim::getConnectionInfo(
+ const String16& name) {
+ std::optional<os::ConnectionInfo> connectionInfo;
+ if (Status status =
+ mTheRealServiceManager->getConnectionInfo(String8(name).c_str(), &connectionInfo);
+ !status.isOk()) {
+ ALOGW("Failed to get ConnectionInfo for %s: %s", String8(name).c_str(),
+ status.toString8().c_str());
+ }
+ return connectionInfo.has_value()
+ ? std::make_optional<IServiceManager::ConnectionInfo>(
+ {connectionInfo->ipAddress, static_cast<unsigned int>(connectionInfo->port)})
+ : std::nullopt;
+}
+
#ifndef __ANDROID__
// ServiceManagerShim for host. Implements the old libbinder android::IServiceManager API.
// The internal implementation of the AIDL interface android::os::IServiceManager calls into
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 7575252..07555f6 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -233,7 +233,7 @@
ALOGE("null proxy");
} else {
if (proxy->isRpcBinder()) {
- ALOGE("Sending a socket binder over RPC is prohibited");
+ ALOGE("Sending a socket binder over kernel binder is prohibited");
return INVALID_OPERATION;
}
}
@@ -291,6 +291,9 @@
if (status_t status = mSession->state()->onBinderEntering(mSession, addr, &binder);
status != OK)
return status;
+ if (status_t status = mSession->state()->flushExcessBinderRefs(mSession, addr, binder);
+ status != OK)
+ return status;
}
return finishUnflattenBinder(binder, out);
diff --git a/libs/binder/RpcCertificateUtils.cpp b/libs/binder/RpcCertificateUtils.cpp
deleted file mode 100644
index d91736c..0000000
--- a/libs/binder/RpcCertificateUtils.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "RpcCertificateUtils"
-#include <log/log.h>
-
-#include <binder/RpcCertificateUtils.h>
-
-#include "Utils.h"
-
-namespace android {
-
-namespace {
-
-bssl::UniquePtr<X509> fromPem(const std::vector<uint8_t>& cert) {
- if (cert.size() > std::numeric_limits<int>::max()) return nullptr;
- bssl::UniquePtr<BIO> certBio(BIO_new_mem_buf(cert.data(), static_cast<int>(cert.size())));
- return bssl::UniquePtr<X509>(PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr));
-}
-
-bssl::UniquePtr<X509> fromDer(const std::vector<uint8_t>& cert) {
- if (cert.size() > std::numeric_limits<long>::max()) return nullptr;
- const unsigned char* data = cert.data();
- auto expectedEnd = data + cert.size();
- bssl::UniquePtr<X509> ret(d2i_X509(nullptr, &data, static_cast<long>(cert.size())));
- if (data != expectedEnd) {
- ALOGE("%s: %td bytes remaining!", __PRETTY_FUNCTION__, expectedEnd - data);
- return nullptr;
- }
- return ret;
-}
-
-} // namespace
-
-bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& cert,
- RpcCertificateFormat format) {
- switch (format) {
- case RpcCertificateFormat::PEM:
- return fromPem(cert);
- case RpcCertificateFormat::DER:
- return fromDer(cert);
- }
- LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
-}
-
-std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format) {
- bssl::UniquePtr<BIO> certBio(BIO_new(BIO_s_mem()));
- switch (format) {
- case RpcCertificateFormat::PEM: {
- TEST_AND_RETURN({}, PEM_write_bio_X509(certBio.get(), x509));
- } break;
- case RpcCertificateFormat::DER: {
- TEST_AND_RETURN({}, i2d_X509_bio(certBio.get(), x509));
- } break;
- default: {
- LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
- }
- }
- const uint8_t* data;
- size_t len;
- TEST_AND_RETURN({}, BIO_mem_contents(certBio.get(), &data, &len));
- return std::vector<uint8_t>(data, data + len);
-}
-
-} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 5733993..ba2920e 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -205,8 +205,11 @@
}
mShutdownTrigger->trigger();
+
for (auto& [id, session] : mSessions) {
(void)id;
+ // server lock is a more general lock
+ std::lock_guard<std::mutex> _lSession(session->mMutex);
session->mShutdownTrigger->trigger();
}
@@ -275,7 +278,7 @@
RpcConnectionHeader header;
if (status == OK) {
status = client->interruptableReadFully(server->mShutdownTrigger.get(), &header,
- sizeof(header));
+ sizeof(header), {});
if (status != OK) {
ALOGE("Failed to read ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -288,7 +291,7 @@
if (header.sessionIdSize > 0) {
sessionId.resize(header.sessionIdSize);
status = client->interruptableReadFully(server->mShutdownTrigger.get(),
- sessionId.data(), sessionId.size());
+ sessionId.data(), sessionId.size(), {});
if (status != OK) {
ALOGE("Failed to read session ID for client connecting to RPC server: %s",
statusToString(status).c_str());
@@ -313,7 +316,7 @@
};
status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &response,
- sizeof(response));
+ sizeof(response), {});
if (status != OK) {
ALOGE("Failed to send new session response: %s", statusToString(status).c_str());
// still need to cleanup before we can return
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 38958c9..65f6bc6 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -217,15 +217,17 @@
}
status_t RpcSession::sendDecStrong(const BpBinder* binder) {
- return sendDecStrong(binder->getPrivateAccessor().rpcAddress());
+ // target is 0 because this is used to free BpBinder objects
+ return sendDecStrongToTarget(binder->getPrivateAccessor().rpcAddress(), 0 /*target*/);
}
-status_t RpcSession::sendDecStrong(uint64_t address) {
+status_t RpcSession::sendDecStrongToTarget(uint64_t address, size_t target) {
ExclusiveConnection connection;
status_t status = ExclusiveConnection::find(sp<RpcSession>::fromExisting(this),
ConnectionUse::CLIENT_REFCOUNT, &connection);
if (status != OK) return status;
- return state()->sendDecStrong(connection.get(), sp<RpcSession>::fromExisting(this), address);
+ return state()->sendDecStrongToTarget(connection.get(), sp<RpcSession>::fromExisting(this),
+ address, target);
}
status_t RpcSession::readId() {
@@ -558,7 +560,7 @@
}
auto sendHeaderStatus =
- server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header));
+ server->interruptableWriteFully(mShutdownTrigger.get(), &header, sizeof(header), {});
if (sendHeaderStatus != OK) {
ALOGE("Could not write connection header to socket: %s",
statusToString(sendHeaderStatus).c_str());
@@ -568,7 +570,7 @@
if (sessionId.size() > 0) {
auto sendSessionIdStatus =
server->interruptableWriteFully(mShutdownTrigger.get(), sessionId.data(),
- sessionId.size());
+ sessionId.size(), {});
if (sendSessionIdStatus != OK) {
ALOGE("Could not write session ID ('%s') to socket: %s",
base::HexString(sessionId.data(), sessionId.size()).c_str(),
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 11a083a..86cc91c 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -152,7 +152,7 @@
return BAD_VALUE;
}
- std::unique_lock<std::mutex> _l(mNodeMutex);
+ std::lock_guard<std::mutex> _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT;
if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
@@ -160,14 +160,6 @@
// implicitly have strong RPC refcount, since we received this binder
it->second.timesRecd++;
-
- _l.unlock();
-
- // We have timesRecd RPC refcounts, but we only need to hold on to one
- // when we keep the object. All additional dec strongs are sent
- // immediately, we wait to send the last one in BpBinder::onLastDecStrong.
- (void)session->sendDecStrong(address);
-
return OK;
}
@@ -189,6 +181,36 @@
return OK;
}
+status_t RpcState::flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t address,
+ const sp<IBinder>& binder) {
+ std::unique_lock<std::mutex> _l(mNodeMutex);
+ if (mTerminated) return DEAD_OBJECT;
+
+ auto it = mNodeForAddress.find(address);
+
+ LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Can't be deleted while we hold sp<>");
+ LOG_ALWAYS_FATAL_IF(it->second.binder != binder,
+ "Caller of flushExcessBinderRefs using inconsistent arguments");
+
+ // if this is a local binder, then we want to get rid of all refcounts
+ // (tell the other process it can drop the binder when it wants to - we
+ // have a local sp<>, so we will drop it when we want to as well). if
+ // this is a remote binder, then we need to hold onto one refcount until
+ // it is dropped in BpBinder::onLastStrongRef
+ size_t targetRecd = binder->localBinder() ? 0 : 1;
+
+ // We have timesRecd RPC refcounts, but we only need to hold on to one
+ // when we keep the object. All additional dec strongs are sent
+ // immediately, we wait to send the last one in BpBinder::onLastDecStrong.
+ if (it->second.timesRecd != targetRecd) {
+ _l.unlock();
+
+ return session->sendDecStrongToTarget(address, targetRecd);
+ }
+
+ return OK;
+}
+
size_t RpcState::countBinders() {
std::lock_guard<std::mutex> _l(mNodeMutex);
return mNodeForAddress.size();
@@ -285,7 +307,7 @@
status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const char* what, const void* data,
- size_t size) {
+ size_t size, const std::function<status_t()>& altPoll) {
LOG_RPC_DETAIL("Sending %s on RpcTransport %p: %s", what, connection->rpcTransport.get(),
android::base::HexString(data, size).c_str());
@@ -297,7 +319,7 @@
if (status_t status =
connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(),
- data, size);
+ data, size, altPoll);
status != OK) {
LOG_RPC_DETAIL("Failed to write %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
connection->rpcTransport.get(), statusToString(status).c_str());
@@ -319,10 +341,11 @@
if (status_t status =
connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
- data, size);
+ data, size, {});
status != OK) {
LOG_RPC_DETAIL("Failed to read %s (%zu bytes) on RpcTransport %p, error: %s", what, size,
connection->rpcTransport.get(), statusToString(status).c_str());
+ (void)session->shutdownAndWait(false);
return status;
}
@@ -428,12 +451,16 @@
const sp<IBinder>& binder, uint32_t code, const Parcel& data,
const sp<RpcSession>& session, Parcel* reply, uint32_t flags) {
if (!data.isForRpc()) {
- ALOGE("Refusing to send RPC with parcel not crafted for RPC");
+ ALOGE("Refusing to send RPC with parcel not crafted for RPC call on binder %p code "
+ "%" PRIu32,
+ binder.get(), code);
return BAD_TYPE;
}
if (data.objectsCount() != 0) {
- ALOGE("Parcel at %p has attached objects but is being used in an RPC call", &data);
+ ALOGE("Parcel at %p has attached objects but is being used in an RPC call on binder %p "
+ "code %" PRIu32,
+ &data, binder.get(), code);
return BAD_TYPE;
}
@@ -496,21 +523,44 @@
memcpy(transactionData.data() + sizeof(RpcWireHeader) + sizeof(RpcWireTransaction), data.data(),
data.dataSize());
+ constexpr size_t kWaitMaxUs = 1000000;
+ constexpr size_t kWaitLogUs = 10000;
+ size_t waitUs = 0;
+
+ // Oneway calls have no sync point, so if many are sent before, whether this
+ // is a twoway or oneway transaction, they may have filled up the socket.
+ // So, make sure we drain them before polling.
+ std::function<status_t()> drainRefs = [&] {
+ if (waitUs > kWaitLogUs) {
+ ALOGE("Cannot send command, trying to process pending refcounts. Waiting %zuus. Too "
+ "many oneway calls?",
+ waitUs);
+ }
+
+ if (waitUs > 0) {
+ usleep(waitUs);
+ waitUs = std::min(kWaitMaxUs, waitUs * 2);
+ } else {
+ waitUs = 1;
+ }
+
+ return drainCommands(connection, session, CommandType::CONTROL_ONLY);
+ };
+
if (status_t status = rpcSend(connection, session, "transaction", transactionData.data(),
- transactionData.size());
- status != OK)
+ transactionData.size(), drainRefs);
+ status != OK) {
// TODO(b/167966510): need to undo onBinderLeaving - we know the
// refcount isn't successfully transferred.
return status;
+ }
if (flags & IBinder::FLAG_ONEWAY) {
LOG_RPC_DETAIL("Oneway command, so no longer waiting on RpcTransport %p",
connection->rpcTransport.get());
// Do not wait on result.
- // However, too many oneway calls may cause refcounts to build up and fill up the socket,
- // so process those.
- return drainCommands(connection, session, CommandType::CONTROL_ONLY);
+ return OK;
}
LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction.");
@@ -531,8 +581,8 @@
const sp<RpcSession>& session, Parcel* reply) {
RpcWireHeader command;
while (true) {
- if (status_t status =
- rpcRec(connection, session, "command header", &command, sizeof(command));
+ if (status_t status = rpcRec(connection, session, "command header (for reply)", &command,
+ sizeof(command));
status != OK)
return status;
@@ -568,32 +618,43 @@
return OK;
}
-status_t RpcState::sendDecStrong(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, uint64_t addr) {
+status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, uint64_t addr,
+ size_t target) {
+ RpcDecStrong body = {
+ .address = RpcWireAddress::fromRaw(addr),
+ };
+
{
std::lock_guard<std::mutex> _l(mNodeMutex);
if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
auto it = mNodeForAddress.find(addr);
LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(),
"Sending dec strong on unknown address %" PRIu64, addr);
- LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %" PRIu64, addr);
- it->second.timesRecd--;
+ LOG_ALWAYS_FATAL_IF(it->second.timesRecd < target, "Can't dec count of %zu to %zu.",
+ it->second.timesRecd, target);
+
+ // typically this happens when multiple threads send dec refs at the
+ // same time - the transactions will get combined automatically
+ if (it->second.timesRecd == target) return OK;
+
+ body.amount = it->second.timesRecd - target;
+ it->second.timesRecd = target;
+
LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(it),
"Bad state. RpcState shouldn't own received binder");
}
RpcWireHeader cmd = {
.command = RPC_COMMAND_DEC_STRONG,
- .bodySize = sizeof(RpcWireAddress),
+ .bodySize = sizeof(RpcDecStrong),
};
if (status_t status = rpcSend(connection, session, "dec ref header", &cmd, sizeof(cmd));
status != OK)
return status;
- if (status_t status = rpcSend(connection, session, "dec ref body", &addr, sizeof(addr));
- status != OK)
- return status;
- return OK;
+
+ return rpcSend(connection, session, "dec ref body", &body, sizeof(body));
}
status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
@@ -601,7 +662,8 @@
LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
RpcWireHeader command;
- if (status_t status = rpcRec(connection, session, "command header", &command, sizeof(command));
+ if (status_t status = rpcRec(connection, session, "command header (for server)", &command,
+ sizeof(command));
status != OK)
return status;
@@ -684,7 +746,7 @@
// for 'recursive' calls to this, we have already read and processed the
// binder from the transaction data and taken reference counts into account,
// so it is cached here.
- sp<IBinder> targetRef;
+ sp<IBinder> target;
processTransactInternalTailCall:
if (transactionData.size() < sizeof(RpcWireTransaction)) {
@@ -699,12 +761,9 @@
bool oneway = transaction->flags & IBinder::FLAG_ONEWAY;
status_t replyStatus = OK;
- sp<IBinder> target;
if (addr != 0) {
- if (!targetRef) {
+ if (!target) {
replyStatus = onBinderEntering(session, addr, &target);
- } else {
- target = targetRef;
}
if (replyStatus != OK) {
@@ -823,6 +882,12 @@
}
}
+ // Binder refs are flushed for oneway calls only after all calls which are
+ // built up are executed. Otherwise, they fill up the binder buffer.
+ if (addr != 0 && replyStatus == OK && !oneway) {
+ replyStatus = flushExcessBinderRefs(session, addr, target);
+ }
+
if (oneway) {
if (replyStatus != OK) {
ALOGW("Oneway call failed with error: %d", replyStatus);
@@ -865,12 +930,20 @@
// reset up arguments
transactionData = std::move(todo.data);
- targetRef = std::move(todo.ref);
+ LOG_ALWAYS_FATAL_IF(target != todo.ref,
+ "async list should be associated with a binder");
it->second.asyncTodo.pop();
goto processTransactInternalTailCall;
}
}
+
+ // done processing all the async commands on this binder that we can, so
+ // write decstrongs on the binder
+ if (addr != 0 && replyStatus == OK) {
+ return flushExcessBinderRefs(session, addr, target);
+ }
+
return OK;
}
@@ -912,16 +985,15 @@
status != OK)
return status;
- if (command.bodySize != sizeof(RpcWireAddress)) {
- ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireAddress. Terminating!",
- sizeof(RpcWireAddress), command.bodySize);
+ if (command.bodySize != sizeof(RpcDecStrong)) {
+ ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcDecStrong. Terminating!",
+ sizeof(RpcDecStrong), command.bodySize);
(void)session->shutdownAndWait(false);
return BAD_VALUE;
}
- RpcWireAddress* address = reinterpret_cast<RpcWireAddress*>(commandData.data());
+ RpcDecStrong* body = reinterpret_cast<RpcDecStrong*>(commandData.data());
- uint64_t addr = RpcWireAddress::toRaw(*address);
-
+ uint64_t addr = RpcWireAddress::toRaw(body->address);
std::unique_lock<std::mutex> _l(mNodeMutex);
auto it = mNodeForAddress.find(addr);
if (it == mNodeForAddress.end()) {
@@ -939,15 +1011,19 @@
return BAD_VALUE;
}
- if (it->second.timesSent == 0) {
- ALOGE("No record of sending binder, but requested decStrong: %" PRIu64, addr);
+ if (it->second.timesSent < body->amount) {
+ ALOGE("Record of sending binder %zu times, but requested decStrong for %" PRIu64 " of %u",
+ it->second.timesSent, addr, body->amount);
return OK;
}
LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %" PRIu64,
addr);
- it->second.timesSent--;
+ LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body->amount,
+ it->second.timesSent);
+
+ it->second.timesSent -= body->amount;
sp<IBinder> tempHold = tryEraseNode(it);
_l.unlock();
tempHold = nullptr; // destructor may make binder calls on this session
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index dcfb569..50de22b 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -82,8 +82,29 @@
uint64_t address, uint32_t code, const Parcel& data,
const sp<RpcSession>& session, Parcel* reply,
uint32_t flags);
- [[nodiscard]] status_t sendDecStrong(const sp<RpcSession::RpcConnection>& connection,
- const sp<RpcSession>& session, uint64_t address);
+
+ /**
+ * The ownership model here carries an implicit strong refcount whenever a
+ * binder is sent across processes. Since we have a local strong count in
+ * sp<> over these objects, we only ever need to keep one of these. So,
+ * typically we tell the remote process that we drop all the implicit dec
+ * strongs, and we hold onto the last one. 'target' here is the target
+ * timesRecd (the number of remaining reference counts) we wish to keep.
+ * Typically this should be '0' or '1'. The target is used instead of an
+ * explicit decrement count in order to allow multiple threads to lower the
+ * number of counts simultaneously. Since we only lower the count to 0 when
+ * a binder is deleted, targets of '1' should only be sent when the caller
+ * owns a local strong reference to the binder. Larger targets may be used
+ * for testing, and to make the function generic, but generally this should
+ * be avoided because it would be hard to guarantee another thread doesn't
+ * lower the number of held refcounts to '1'. Note also, these refcounts
+ * must be sent actively. If they are sent when binders are deleted, this
+ * can cause leaks, since even remote binders carry an implicit strong ref
+ * when they are sent to another process.
+ */
+ [[nodiscard]] status_t sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, uint64_t address,
+ size_t target);
enum class CommandType {
ANY,
@@ -108,6 +129,13 @@
*/
[[nodiscard]] status_t onBinderEntering(const sp<RpcSession>& session, uint64_t address,
sp<IBinder>* out);
+ /**
+ * Called on incoming binders to update refcounting information. This should
+ * only be called when it is done as part of making progress on a
+ * transaction.
+ */
+ [[nodiscard]] status_t flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t address,
+ const sp<IBinder>& binder);
size_t countBinders();
void dump();
@@ -149,7 +177,8 @@
[[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const char* what,
- const void* data, size_t size);
+ const void* data, size_t size,
+ const std::function<status_t()>& altPoll = nullptr);
[[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, const char* what, void* data,
size_t size);
diff --git a/libs/binder/RpcTlsUtils.cpp b/libs/binder/RpcTlsUtils.cpp
new file mode 100644
index 0000000..f3ca02a
--- /dev/null
+++ b/libs/binder/RpcTlsUtils.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcTlsUtils"
+#include <log/log.h>
+
+#include <binder/RpcTlsUtils.h>
+
+#include "Utils.h"
+
+namespace android {
+
+namespace {
+
+static_assert(sizeof(unsigned char) == sizeof(uint8_t));
+
+template <typename PemReadBioFn,
+ typename T = std::remove_pointer_t<std::invoke_result_t<
+ PemReadBioFn, BIO*, std::nullptr_t, std::nullptr_t, std::nullptr_t>>>
+bssl::UniquePtr<T> fromPem(const std::vector<uint8_t>& data, PemReadBioFn fn) {
+ if (data.size() > std::numeric_limits<int>::max()) return nullptr;
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(data.data(), static_cast<int>(data.size())));
+ return bssl::UniquePtr<T>(fn(bio.get(), nullptr, nullptr, nullptr));
+}
+
+template <typename D2iFn,
+ typename T = std::remove_pointer_t<
+ std::invoke_result_t<D2iFn, std::nullptr_t, const unsigned char**, long>>>
+bssl::UniquePtr<T> fromDer(const std::vector<uint8_t>& data, D2iFn fn) {
+ if (data.size() > std::numeric_limits<long>::max()) return nullptr;
+ const unsigned char* dataPtr = data.data();
+ auto expectedEnd = dataPtr + data.size();
+ bssl::UniquePtr<T> ret(fn(nullptr, &dataPtr, static_cast<long>(data.size())));
+ if (dataPtr != expectedEnd) {
+ ALOGE("%s: %td bytes remaining!", __PRETTY_FUNCTION__, expectedEnd - dataPtr);
+ return nullptr;
+ }
+ return ret;
+}
+
+template <typename T, typename WriteBioFn = int (*)(BIO*, T*)>
+std::vector<uint8_t> serialize(T* object, WriteBioFn writeBio) {
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+ TEST_AND_RETURN({}, writeBio(bio.get(), object));
+ const uint8_t* data;
+ size_t len;
+ TEST_AND_RETURN({}, BIO_mem_contents(bio.get(), &data, &len));
+ return std::vector<uint8_t>(data, data + len);
+}
+
+} // namespace
+
+bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& data,
+ RpcCertificateFormat format) {
+ switch (format) {
+ case RpcCertificateFormat::PEM:
+ return fromPem(data, PEM_read_bio_X509);
+ case RpcCertificateFormat::DER:
+ return fromDer(data, d2i_X509);
+ }
+ LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
+}
+
+std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format) {
+ switch (format) {
+ case RpcCertificateFormat::PEM:
+ return serialize(x509, PEM_write_bio_X509);
+ case RpcCertificateFormat::DER:
+ return serialize(x509, i2d_X509_bio);
+ }
+ LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
+}
+
+bssl::UniquePtr<EVP_PKEY> deserializeUnencryptedPrivatekey(const std::vector<uint8_t>& data,
+ RpcKeyFormat format) {
+ switch (format) {
+ case RpcKeyFormat::PEM:
+ return fromPem(data, PEM_read_bio_PrivateKey);
+ case RpcKeyFormat::DER:
+ return fromDer(data, d2i_AutoPrivateKey);
+ }
+ LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
+}
+
+std::vector<uint8_t> serializeUnencryptedPrivatekey(EVP_PKEY* pkey, RpcKeyFormat format) {
+ switch (format) {
+ case RpcKeyFormat::PEM:
+ return serialize(pkey, [](BIO* bio, EVP_PKEY* pkey) {
+ return PEM_write_bio_PrivateKey(bio, pkey, nullptr /* enc */, nullptr /* kstr */,
+ 0 /* klen */, nullptr, nullptr);
+ });
+ case RpcKeyFormat::DER:
+ return serialize(pkey, i2d_PrivateKey_bio);
+ }
+ LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format));
+}
+
+} // namespace android
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index c012df8..7669518 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -35,20 +35,6 @@
class RpcTransportRaw : public RpcTransport {
public:
explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
- Result<size_t> send(const void* buf, size_t size) {
- ssize_t ret = TEMP_FAILURE_RETRY(::send(mSocket.get(), buf, size, MSG_NOSIGNAL));
- if (ret < 0) {
- return ErrnoError() << "send()";
- }
- return ret;
- }
- Result<size_t> recv(void* buf, size_t size) {
- ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_NOSIGNAL));
- if (ret < 0) {
- return ErrnoError() << "recv()";
- }
- return ret;
- }
Result<size_t> peek(void *buf, size_t size) override {
ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK));
if (ret < 0) {
@@ -57,52 +43,72 @@
return ret;
}
- status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size) override {
- const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data);
- const uint8_t* end = buffer + size;
+ template <typename Buffer, typename SendOrReceive>
+ status_t interruptableReadOrWrite(FdTrigger* fdTrigger, Buffer buffer, size_t size,
+ SendOrReceive sendOrReceiveFun, const char* funName,
+ int16_t event, const std::function<status_t()>& altPoll) {
+ const Buffer end = buffer + size;
MAYBE_WAIT_IN_FLAKE_MODE;
- status_t status;
- while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLOUT)) == OK) {
- auto writeSize = this->send(buffer, end - buffer);
- if (!writeSize.ok()) {
- LOG_RPC_DETAIL("RpcTransport::send(): %s", writeSize.error().message().c_str());
- return writeSize.error().code() == 0 ? UNKNOWN_ERROR : -writeSize.error().code();
+ // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we
+ // may never know we should be shutting down.
+ if (fdTrigger->isTriggered()) {
+ return DEAD_OBJECT;
+ }
+
+ bool havePolled = false;
+ while (true) {
+ ssize_t processSize = TEMP_FAILURE_RETRY(
+ sendOrReceiveFun(mSocket.get(), buffer, end - buffer, MSG_NOSIGNAL));
+
+ if (processSize < 0) {
+ int savedErrno = errno;
+
+ // Still return the error on later passes, since it would expose
+ // a problem with polling
+ if (havePolled ||
+ (!havePolled && savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) {
+ LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno));
+ return -savedErrno;
+ }
+ } else if (processSize == 0) {
+ return DEAD_OBJECT;
+ } else {
+ buffer += processSize;
+ if (buffer == end) {
+ return OK;
+ }
}
- if (*writeSize == 0) return DEAD_OBJECT;
-
- buffer += *writeSize;
- if (buffer == end) return OK;
+ if (altPoll) {
+ if (status_t status = altPoll(); status != OK) return status;
+ if (fdTrigger->isTriggered()) {
+ return DEAD_OBJECT;
+ }
+ } else {
+ if (status_t status = fdTrigger->triggerablePoll(mSocket.get(), event);
+ status != OK)
+ return status;
+ if (!havePolled) havePolled = true;
+ }
}
- return status;
}
- status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) override {
- uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
- uint8_t* end = buffer + size;
+ status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size,
+ const std::function<status_t()>& altPoll) override {
+ return interruptableReadOrWrite(fdTrigger, reinterpret_cast<const uint8_t*>(data), size,
+ send, "send", POLLOUT, altPoll);
+ }
- MAYBE_WAIT_IN_FLAKE_MODE;
-
- status_t status;
- while ((status = fdTrigger->triggerablePoll(mSocket.get(), POLLIN)) == OK) {
- auto readSize = this->recv(buffer, end - buffer);
- if (!readSize.ok()) {
- LOG_RPC_DETAIL("RpcTransport::recv(): %s", readSize.error().message().c_str());
- return readSize.error().code() == 0 ? UNKNOWN_ERROR : -readSize.error().code();
- }
-
- if (*readSize == 0) return DEAD_OBJECT; // EOF
-
- buffer += *readSize;
- if (buffer == end) return OK;
- }
- return status;
+ status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+ const std::function<status_t()>& altPoll) override {
+ return interruptableReadOrWrite(fdTrigger, reinterpret_cast<uint8_t*>(data), size, recv,
+ "recv", POLLIN, altPoll);
}
private:
- android::base::unique_fd mSocket;
+ base::unique_fd mSocket;
};
// RpcTransportCtx with TLS disabled.
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 63f9339..7f810b1 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -22,7 +22,7 @@
#include <openssl/bn.h>
#include <openssl/ssl.h>
-#include <binder/RpcCertificateUtils.h>
+#include <binder/RpcTlsUtils.h>
#include <binder/RpcTransportTls.h>
#include "FdTrigger.h"
@@ -44,8 +44,6 @@
namespace android {
namespace {
-constexpr const int kCertValidDays = 30;
-
// Implement BIO for socket that ignores SIGPIPE.
int socketNew(BIO* bio) {
BIO_set_data(bio, reinterpret_cast<void*>(-1));
@@ -100,49 +98,6 @@
return ret;
}
-bssl::UniquePtr<EVP_PKEY> makeKeyPairForSelfSignedCert() {
- bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
- if (ec_key == nullptr || !EC_KEY_generate_key(ec_key.get())) {
- ALOGE("Failed to generate key pair.");
- return nullptr;
- }
- bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
- // Use set1 instead of assign to avoid leaking ec_key when assign fails. set1 increments
- // the refcount of the ec_key, so it is okay to release it at the end of this function.
- if (evp_pkey == nullptr || !EVP_PKEY_set1_EC_KEY(evp_pkey.get(), ec_key.get())) {
- ALOGE("Failed to assign key pair.");
- return nullptr;
- }
- return evp_pkey;
-}
-
-bssl::UniquePtr<X509> makeSelfSignedCert(EVP_PKEY* evp_pkey, const int valid_days) {
- bssl::UniquePtr<X509> x509(X509_new());
- bssl::UniquePtr<BIGNUM> serial(BN_new());
- bssl::UniquePtr<BIGNUM> serialLimit(BN_new());
- TEST_AND_RETURN(nullptr, BN_lshift(serialLimit.get(), BN_value_one(), 128));
- TEST_AND_RETURN(nullptr, BN_rand_range(serial.get(), serialLimit.get()));
- TEST_AND_RETURN(nullptr, BN_to_ASN1_INTEGER(serial.get(), X509_get_serialNumber(x509.get())));
- TEST_AND_RETURN(nullptr, X509_gmtime_adj(X509_getm_notBefore(x509.get()), 0));
- TEST_AND_RETURN(nullptr,
- X509_gmtime_adj(X509_getm_notAfter(x509.get()), 60 * 60 * 24 * valid_days));
-
- X509_NAME* subject = X509_get_subject_name(x509.get());
- TEST_AND_RETURN(nullptr,
- X509_NAME_add_entry_by_txt(subject, "O", MBSTRING_ASC,
- reinterpret_cast<const uint8_t*>("Android"), -1, -1,
- 0));
- TEST_AND_RETURN(nullptr,
- X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC,
- reinterpret_cast<const uint8_t*>("BinderRPC"), -1,
- -1, 0));
- TEST_AND_RETURN(nullptr, X509_set_issuer_name(x509.get(), subject));
-
- TEST_AND_RETURN(nullptr, X509_set_pubkey(x509.get(), evp_pkey));
- TEST_AND_RETURN(nullptr, X509_sign(x509.get(), evp_pkey, EVP_sha256()));
- return x509;
-}
-
[[maybe_unused]] void sslDebugLog(const SSL* ssl, int type, int value) {
switch (type) {
case SSL_CB_HANDSHAKE_START:
@@ -214,12 +169,13 @@
// If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
// return error. Also return error if |fdTrigger| is triggered before or during poll().
status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger,
- const char* fnString, int additionalEvent = 0) {
+ const char* fnString, int additionalEvent,
+ const std::function<status_t()>& altPoll) {
switch (sslError) {
case SSL_ERROR_WANT_READ:
- return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString);
+ return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
case SSL_ERROR_WANT_WRITE:
- return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString);
+ return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll);
case SSL_ERROR_SYSCALL: {
auto queue = toString();
LOG_TLS_DETAIL("%s(): %s. Treating as DEAD_OBJECT. Error queue: %s", fnString,
@@ -239,11 +195,17 @@
bool mHandled = false;
status_t handlePoll(int event, android::base::borrowed_fd fd, FdTrigger* fdTrigger,
- const char* fnString) {
- status_t ret = fdTrigger->triggerablePoll(fd, event);
- if (ret != OK && ret != DEAD_OBJECT && ret != -ECANCELED) {
- ALOGE("triggerablePoll error while poll()-ing after %s(): %s", fnString,
- statusToString(ret).c_str());
+ const char* fnString, const std::function<status_t()>& altPoll) {
+ status_t ret;
+ if (altPoll) {
+ ret = altPoll();
+ if (fdTrigger->isTriggered()) ret = DEAD_OBJECT;
+ } else {
+ ret = fdTrigger->triggerablePoll(fd, event);
+ }
+
+ if (ret != OK && ret != DEAD_OBJECT) {
+ ALOGE("poll error while after %s(): %s", fnString, statusToString(ret).c_str());
}
clear();
return ret;
@@ -313,14 +275,14 @@
RpcTransportTls(android::base::unique_fd socket, Ssl ssl)
: mSocket(std::move(socket)), mSsl(std::move(ssl)) {}
Result<size_t> peek(void* buf, size_t size) override;
- status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size) override;
- status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) override;
+ status_t interruptableWriteFully(FdTrigger* fdTrigger, const void* data, size_t size,
+ const std::function<status_t()>& altPoll) override;
+ status_t interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+ const std::function<status_t()>& altPoll) override;
private:
android::base::unique_fd mSocket;
Ssl mSsl;
-
- static status_t isTriggered(FdTrigger* fdTrigger);
};
// Error code is errno.
@@ -341,17 +303,9 @@
return ret;
}
-status_t RpcTransportTls::isTriggered(FdTrigger* fdTrigger) {
- auto ret = fdTrigger->isTriggeredPolled();
- if (!ret.ok()) {
- ALOGE("%s: %s", __PRETTY_FUNCTION__, ret.error().message().c_str());
- return ret.error().code() == 0 ? UNKNOWN_ERROR : -ret.error().code();
- }
- return *ret ? -ECANCELED : OK;
-}
-
status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, const void* data,
- size_t size) {
+ size_t size,
+ const std::function<status_t()>& altPoll) {
auto buffer = reinterpret_cast<const uint8_t*>(data);
const uint8_t* end = buffer + size;
@@ -359,7 +313,7 @@
// Before doing any I/O, check trigger once. This ensures the trigger is checked at least
// once. The trigger is also checked via triggerablePoll() after every SSL_write().
- if (status_t status = isTriggered(fdTrigger); status != OK) return status;
+ if (fdTrigger->isTriggered()) return DEAD_OBJECT;
while (buffer < end) {
size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
@@ -373,8 +327,8 @@
int sslError = mSsl.getError(writeSize);
// TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be
// triggerablePoll()-ed. Then additionalEvent is no longer necessary.
- status_t pollStatus =
- errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, "SSL_write", POLLIN);
+ status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+ "SSL_write", POLLIN, altPoll);
if (pollStatus != OK) return pollStatus;
// Do not advance buffer. Try SSL_write() again.
}
@@ -382,7 +336,8 @@
return OK;
}
-status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size) {
+status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, void* data, size_t size,
+ const std::function<status_t()>& altPoll) {
auto buffer = reinterpret_cast<uint8_t*>(data);
uint8_t* end = buffer + size;
@@ -390,7 +345,7 @@
// Before doing any I/O, check trigger once. This ensures the trigger is checked at least
// once. The trigger is also checked via triggerablePoll() after every SSL_write().
- if (status_t status = isTriggered(fdTrigger); status != OK) return status;
+ if (fdTrigger->isTriggered()) return DEAD_OBJECT;
while (buffer < end) {
size_t todo = std::min<size_t>(end - buffer, std::numeric_limits<int>::max());
@@ -406,8 +361,8 @@
return DEAD_OBJECT;
}
int sslError = mSsl.getError(readSize);
- status_t pollStatus =
- errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, "SSL_read");
+ status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger,
+ "SSL_read", 0, altPoll);
if (pollStatus != OK) return pollStatus;
// Do not advance buffer. Try SSL_read() again.
}
@@ -438,7 +393,7 @@
}
int sslError = ssl->getError(ret);
status_t pollStatus =
- errorQueue.pollForSslError(fd, sslError, fdTrigger, "SSL_do_handshake");
+ errorQueue.pollForSslError(fd, sslError, fdTrigger, "SSL_do_handshake", 0, {});
if (pollStatus != OK) return false;
}
}
@@ -448,7 +403,7 @@
template <typename Impl,
typename = std::enable_if_t<std::is_base_of_v<RpcTransportCtxTls, Impl>>>
static std::unique_ptr<RpcTransportCtxTls> create(
- std::shared_ptr<RpcCertificateVerifier> verifier);
+ std::shared_ptr<RpcCertificateVerifier> verifier, RpcAuth* auth);
std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd,
FdTrigger* fdTrigger) const override;
std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override;
@@ -471,17 +426,13 @@
LOG_ALWAYS_FATAL_IF(outAlert == nullptr);
const char* logPrefix = SSL_is_server(ssl) ? "Server" : "Client";
- bssl::UniquePtr<X509> peerCert(SSL_get_peer_certificate(ssl)); // Does not set error queue
- LOG_ALWAYS_FATAL_IF(peerCert == nullptr,
- "%s: libssl should not ask to verify non-existing cert", logPrefix);
-
auto ctx = SSL_get_SSL_CTX(ssl); // Does not set error queue
LOG_ALWAYS_FATAL_IF(ctx == nullptr);
// void* -> RpcTransportCtxTls*
auto rpcTransportCtxTls = reinterpret_cast<RpcTransportCtxTls*>(SSL_CTX_get_app_data(ctx));
LOG_ALWAYS_FATAL_IF(rpcTransportCtxTls == nullptr);
- status_t verifyStatus = rpcTransportCtxTls->mCertVerifier->verify(peerCert.get(), outAlert);
+ status_t verifyStatus = rpcTransportCtxTls->mCertVerifier->verify(ssl, outAlert);
if (verifyStatus == OK) {
return ssl_verify_ok;
}
@@ -494,16 +445,15 @@
// provided as a template argument so that this function can initialize an |Impl| object.
template <typename Impl, typename>
std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create(
- std::shared_ptr<RpcCertificateVerifier> verifier) {
+ std::shared_ptr<RpcCertificateVerifier> verifier, RpcAuth* auth) {
bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
TEST_AND_RETURN(nullptr, ctx != nullptr);
- auto evp_pkey = makeKeyPairForSelfSignedCert();
- TEST_AND_RETURN(nullptr, evp_pkey != nullptr);
- auto cert = makeSelfSignedCert(evp_pkey.get(), kCertValidDays);
- TEST_AND_RETURN(nullptr, cert != nullptr);
- TEST_AND_RETURN(nullptr, SSL_CTX_use_PrivateKey(ctx.get(), evp_pkey.get()));
- TEST_AND_RETURN(nullptr, SSL_CTX_use_certificate(ctx.get(), cert.get()));
+ if (status_t authStatus = auth->configure(ctx.get()); authStatus != OK) {
+ ALOGE("%s: Failed to configure auth info: %s", __PRETTY_FUNCTION__,
+ statusToString(authStatus).c_str());
+ return nullptr;
+ };
// Enable two-way authentication by setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT on server.
// Client ignores SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag.
@@ -553,11 +503,13 @@
} // namespace
std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newServerCtx() const {
- return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(mCertVerifier);
+ return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(mCertVerifier,
+ mAuth.get());
}
std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newClientCtx() const {
- return android::RpcTransportCtxTls::create<RpcTransportCtxTlsClient>(mCertVerifier);
+ return android::RpcTransportCtxTls::create<RpcTransportCtxTlsClient>(mCertVerifier,
+ mAuth.get());
}
const char* RpcTransportCtxFactoryTls::toCString() const {
@@ -565,13 +517,17 @@
}
std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTls::make(
- std::shared_ptr<RpcCertificateVerifier> verifier) {
+ std::shared_ptr<RpcCertificateVerifier> verifier, std::unique_ptr<RpcAuth> auth) {
if (verifier == nullptr) {
ALOGE("%s: Must provide a certificate verifier", __PRETTY_FUNCTION__);
return nullptr;
}
+ if (auth == nullptr) {
+ ALOGE("%s: Must provide an auth provider", __PRETTY_FUNCTION__);
+ return nullptr;
+ }
return std::unique_ptr<RpcTransportCtxFactoryTls>(
- new RpcTransportCtxFactoryTls(std::move(verifier)));
+ new RpcTransportCtxFactoryTls(std::move(verifier), std::move(auth)));
}
} // namespace android
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index a87aa07..171550e 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -85,7 +85,7 @@
*/
RPC_COMMAND_REPLY,
/**
- * follows is RpcWireAddress
+ * follows is RpcDecStrong
*
* note - this in the protocol directly instead of as a 'special
* transaction' in order to keep it as lightweight as possible (we don't
@@ -117,6 +117,13 @@
};
static_assert(sizeof(RpcWireHeader) == 16);
+struct RpcDecStrong {
+ RpcWireAddress address;
+ uint32_t amount;
+ uint32_t reserved;
+};
+static_assert(sizeof(RpcDecStrong) == 16);
+
struct RpcWireTransaction {
RpcWireAddress address;
uint32_t code;
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 59334b7..27cc563 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -74,12 +74,12 @@
result->toString().c_str());
return std::nullopt;
}
- if (!result->stderr.empty()) {
+ if (!result->stderrStr.empty()) {
LOG_HOST("`adb forward tcp:0 tcp:%d` writes to stderr: %s", devicePort,
- result->stderr.c_str());
+ result->stderrStr.c_str());
}
- unsigned int hostPort = parsePortNumber(result->stdout, "host port");
+ unsigned int hostPort = parsePortNumber(result->stdoutStr, "host port");
if (hostPort == 0) return std::nullopt;
return AdbForwarder(hostPort);
@@ -105,9 +105,9 @@
result->toString().c_str());
return;
}
- if (!result->stderr.empty()) {
+ if (!result->stderrStr.empty()) {
LOG_HOST("`adb forward --remove tcp:%d` writes to stderr: %s", *mPort,
- result->stderr.c_str());
+ result->stderrStr.c_str());
}
LOG_HOST("Successfully run `adb forward --remove tcp:%d`", *mPort);
@@ -139,8 +139,8 @@
ALOGE("Command exits with: %s", result->toString().c_str());
return nullptr;
}
- if (!result->stderr.empty()) {
- LOG_HOST("servicedispatcher writes to stderr: %s", result->stderr.c_str());
+ if (!result->stderrStr.empty()) {
+ LOG_HOST("servicedispatcher writes to stderr: %s", result->stderrStr.c_str());
}
if (!result->stdoutEndsWithNewLine()) {
@@ -148,7 +148,7 @@
return nullptr;
}
- unsigned int devicePort = parsePortNumber(result->stdout, "device port");
+ unsigned int devicePort = parsePortNumber(result->stdoutStr, "device port");
if (devicePort == 0) return nullptr;
auto forwardResult = AdbForwarder::forward(devicePort);
diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp
index d121ce2..52b8f69 100644
--- a/libs/binder/UtilsHost.cpp
+++ b/libs/binder/UtilsHost.cpp
@@ -63,7 +63,7 @@
if (res.exitCode) os << "code=" << *res.exitCode;
if (res.signal) os << "signal=" << *res.signal;
if (res.pid) os << ", pid=" << *res.pid;
- return os << ", stdout=" << res.stdout << ", stderr=" << res.stderr;
+ return os << ", stdout=" << res.stdoutStr << ", stderr=" << res.stderrStr;
}
std::string CommandResult::toString() const {
@@ -142,9 +142,9 @@
int pollRet = poll(fds, nfds, 1000 /* ms timeout */);
if (pollRet == -1) return android::base::ErrnoError() << "poll()";
- if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdout))
+ if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr))
return android::base::ErrnoError() << "read(stdout)";
- if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderr))
+ if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr))
return android::base::ErrnoError() << "read(stderr)";
if (end && end(ret)) return ret;
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index 0f29f60..98ac4e0 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -43,8 +43,8 @@
std::optional<int32_t> exitCode;
std::optional<int32_t> signal;
std::optional<pid_t> pid;
- std::string stdout;
- std::string stderr;
+ std::string stdoutStr;
+ std::string stderrStr;
android::base::unique_fd outPipe;
android::base::unique_fd errPipe;
@@ -55,15 +55,15 @@
std::swap(exitCode, other.exitCode);
std::swap(signal, other.signal);
std::swap(pid, other.pid);
- std::swap(stdout, other.stdout);
- std::swap(stderr, other.stderr);
+ std::swap(stdoutStr, other.stdoutStr);
+ std::swap(stderrStr, other.stderrStr);
return *this;
}
~CommandResult();
[[nodiscard]] std::string toString() const;
[[nodiscard]] bool stdoutEndsWithNewLine() const {
- return !stdout.empty() && stdout.back() == '\n';
+ return !stdoutStr.empty() && stdoutStr.back() == '\n';
}
private:
diff --git a/libs/binder/aidl/android/os/ConnectionInfo.aidl b/libs/binder/aidl/android/os/ConnectionInfo.aidl
new file mode 100644
index 0000000..160c9ea
--- /dev/null
+++ b/libs/binder/aidl/android/os/ConnectionInfo.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Remote connection info associated with a declared service
+ * @hide
+ */
+parcelable ConnectionInfo {
+ /**
+ * IP address that the service is listening on.
+ */
+ @utf8InCpp String ipAddress;
+ /**
+ * Port number that the service is listening on. Actual value is an unsigned integer.
+ */
+ int port;
+}
+
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 75c4092..5880c0a 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -19,6 +19,7 @@
import android.os.IClientCallback;
import android.os.IServiceCallback;
import android.os.ServiceDebugInfo;
+import android.os.ConnectionInfo;
/**
* Basic interface for finding and publishing system services.
@@ -113,6 +114,11 @@
@nullable @utf8InCpp String updatableViaApex(@utf8InCpp String name);
/**
+ * If connection info is available for the given instance, returns the ConnectionInfo
+ */
+ @nullable ConnectionInfo getConnectionInfo(@utf8InCpp String name);
+
+ /**
* Request a callback when the number of clients of the service changes.
* Used by LazyServiceRegistrar to dynamically stop services that have no clients.
*/
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 065e6e3..82bebc9 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -51,17 +51,11 @@
static status_t freeze(pid_t pid, bool enabled, uint32_t timeout_ms);
// Provide information about the state of a frozen process
- static status_t getProcessFreezeInfo(pid_t pid, bool *sync_received,
- bool *async_received);
-
- // TODO: Remove the above legacy duplicated function in next version
-#ifndef __ANDROID_VNDK__
static status_t getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
uint32_t *async_received);
-#endif
sp<ProcessState> process();
-
+
status_t clearLastError();
/**
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index d152005..a48075d 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -15,11 +15,9 @@
*/
#pragma once
-
#include <binder/IInterface.h>
#include <utils/Vector.h>
#include <utils/String16.h>
-
#include <optional>
namespace android {
@@ -107,6 +105,16 @@
* this can be updated.
*/
virtual std::optional<String16> updatableViaApex(const String16& name) = 0;
+
+ /**
+ * If this instance has declared remote connection information, returns
+ * the ConnectionInfo.
+ */
+ struct ConnectionInfo {
+ std::string ipAddress;
+ unsigned int port;
+ };
+ virtual std::optional<ConnectionInfo> getConnectionInfo(const String16& name) = 0;
};
sp<IServiceManager> defaultServiceManager();
diff --git a/libs/binder/include/binder/RpcCertificateVerifier.h b/libs/binder/include/binder/RpcKeyFormat.h
similarity index 64%
rename from libs/binder/include/binder/RpcCertificateVerifier.h
rename to libs/binder/include/binder/RpcKeyFormat.h
index 97af31c..5099c2e 100644
--- a/libs/binder/include/binder/RpcCertificateVerifier.h
+++ b/libs/binder/include/binder/RpcKeyFormat.h
@@ -14,19 +14,28 @@
* limitations under the License.
*/
+// Formats for serializing TLS private keys.
+
#pragma once
-#include <openssl/ssl.h>
-#include <utils/Errors.h>
+#include <string>
namespace android {
-// An interface with a function that verifies a peer certificate. It is a wrapper over the custom
-// verify function (see SSL_CTX_set_custom_verify).
-class RpcCertificateVerifier {
-public:
- virtual ~RpcCertificateVerifier() = default;
- virtual status_t verify(const X509* peerCert, uint8_t* outAlert) = 0;
+enum class RpcKeyFormat {
+ PEM,
+ DER,
};
+static inline std::string PrintToString(RpcKeyFormat format) {
+ switch (format) {
+ case RpcKeyFormat::PEM:
+ return "PEM";
+ case RpcKeyFormat::DER:
+ return "DER";
+ default:
+ return "<unknown>";
+ }
+}
+
} // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 6a29c05..12d448d 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -176,7 +176,8 @@
friend RpcState;
explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
- [[nodiscard]] status_t sendDecStrong(uint64_t address);
+ // for 'target', see RpcState::sendDecStrongToTarget
+ [[nodiscard]] status_t sendDecStrongToTarget(uint64_t address, size_t target);
class EventListener : public virtual RefBase {
public:
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 1c0bb18..db8b5e9 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -18,6 +18,7 @@
#pragma once
+#include <functional>
#include <memory>
#include <string>
@@ -38,18 +39,25 @@
virtual ~RpcTransport() = default;
// replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled.
- virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
+ [[nodiscard]] virtual android::base::Result<size_t> peek(void *buf, size_t size) = 0;
/**
* Read (or write), but allow to be interrupted by a trigger.
*
+ * altPoll - function to be called instead of polling, when needing to wait
+ * to read/write data. If this returns an error, that error is returned from
+ * this function.
+ *
* Return:
* OK - succeeded in completely processing 'size'
* error - interrupted (failure or trigger)
*/
- virtual status_t interruptableWriteFully(FdTrigger *fdTrigger, const void *buf,
- size_t size) = 0;
- virtual status_t interruptableReadFully(FdTrigger *fdTrigger, void *buf, size_t size) = 0;
+ [[nodiscard]] virtual status_t interruptableWriteFully(
+ FdTrigger *fdTrigger, const void *buf, size_t size,
+ const std::function<status_t()> &altPoll) = 0;
+ [[nodiscard]] virtual status_t interruptableReadFully(
+ FdTrigger *fdTrigger, void *buf, size_t size,
+ const std::function<status_t()> &altPoll) = 0;
protected:
RpcTransport() = default;
diff --git a/libs/binder/include_tls/binder/RpcAuth.h b/libs/binder/include_tls/binder/RpcAuth.h
new file mode 100644
index 0000000..4c2f296
--- /dev/null
+++ b/libs/binder/include_tls/binder/RpcAuth.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <openssl/ssl.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+// An interface with a function that configures the SSL_CTX object with authentication information,
+// including certificates and private keys.
+class RpcAuth {
+public:
+ virtual ~RpcAuth() = default;
+
+ // The keys and certificates to provide is up to the implementation. Multiple calls to
+ // |configure()| may configure |ctx| with the same keys / certificates, or generate a
+ // different key / certificate every time |configure()| is called.
+ //
+ // It is guaranteed that, when a context object (RpcTransportCtx) is created,
+ // libbinder_tls calls |configure()| on server RpcAuth exactly once.
+ //
+ // The implementation may use the following function to set the private
+ // keys and certificates:
+ // - SSL_CTX_use_PrivateKey
+ // - SSL_CTX_use_certificate
+ // - SSL_CTX_set*_chain
+ // - SSL_CTX_add0_chain_cert
+ virtual status_t configure(SSL_CTX* ctx) = 0;
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/RpcCertificateVerifier.h b/libs/binder/include_tls/binder/RpcCertificateVerifier.h
similarity index 64%
copy from libs/binder/include/binder/RpcCertificateVerifier.h
copy to libs/binder/include_tls/binder/RpcCertificateVerifier.h
index 97af31c..800e375 100644
--- a/libs/binder/include/binder/RpcCertificateVerifier.h
+++ b/libs/binder/include_tls/binder/RpcCertificateVerifier.h
@@ -26,7 +26,18 @@
class RpcCertificateVerifier {
public:
virtual ~RpcCertificateVerifier() = default;
- virtual status_t verify(const X509* peerCert, uint8_t* outAlert) = 0;
+
+ // The implementation may use the following function to get
+ // the peer certificate and chain:
+ // - SSL_get_peer_certificate
+ // - SSL_get_peer_cert_chain
+ // - SSL_get_peer_full_cert_chain
+ //
+ // The implementation should return OK on success or error codes on error. For example:
+ // - PERMISSION_DENIED for rejected certificates
+ // - NO_INIT for not presenting a certificate when requested
+ // - UNKNOWN_ERROR for other errors
+ virtual status_t verify(const SSL* ssl, uint8_t* outAlert) = 0;
};
} // namespace android
diff --git a/libs/binder/include_tls/binder/RpcCertificateUtils.h b/libs/binder/include_tls/binder/RpcTlsUtils.h
similarity index 72%
rename from libs/binder/include_tls/binder/RpcCertificateUtils.h
rename to libs/binder/include_tls/binder/RpcTlsUtils.h
index 8d07835..591926b 100644
--- a/libs/binder/include_tls/binder/RpcCertificateUtils.h
+++ b/libs/binder/include_tls/binder/RpcTlsUtils.h
@@ -23,12 +23,20 @@
#include <openssl/ssl.h>
#include <binder/RpcCertificateFormat.h>
+#include <binder/RpcKeyFormat.h>
namespace android {
-bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& cert,
+bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& data,
RpcCertificateFormat format);
std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format);
+// Deserialize an un-encrypted private key.
+bssl::UniquePtr<EVP_PKEY> deserializeUnencryptedPrivatekey(const std::vector<uint8_t>& data,
+ RpcKeyFormat format);
+
+// Serialize a private key in un-encrypted form.
+std::vector<uint8_t> serializeUnencryptedPrivatekey(EVP_PKEY* pkey, RpcKeyFormat format);
+
} // namespace android
diff --git a/libs/binder/include_tls/binder/RpcTransportTls.h b/libs/binder/include_tls/binder/RpcTransportTls.h
index f26a3e9..8a11125 100644
--- a/libs/binder/include_tls/binder/RpcTransportTls.h
+++ b/libs/binder/include_tls/binder/RpcTransportTls.h
@@ -18,6 +18,7 @@
#pragma once
+#include <binder/RpcAuth.h>
#include <binder/RpcCertificateVerifier.h>
#include <binder/RpcTransport.h>
@@ -26,17 +27,20 @@
// RpcTransportCtxFactory with TLS enabled with self-signed certificate.
class RpcTransportCtxFactoryTls : public RpcTransportCtxFactory {
public:
- static std::unique_ptr<RpcTransportCtxFactory> make(std::shared_ptr<RpcCertificateVerifier>);
+ static std::unique_ptr<RpcTransportCtxFactory> make(std::shared_ptr<RpcCertificateVerifier>,
+ std::unique_ptr<RpcAuth>);
std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
const char* toCString() const override;
private:
- RpcTransportCtxFactoryTls(std::shared_ptr<RpcCertificateVerifier> verifier)
- : mCertVerifier(std::move(verifier)){};
+ RpcTransportCtxFactoryTls(std::shared_ptr<RpcCertificateVerifier> verifier,
+ std::unique_ptr<RpcAuth> auth)
+ : mCertVerifier(std::move(verifier)), mAuth(std::move(auth)){};
std::shared_ptr<RpcCertificateVerifier> mCertVerifier;
+ std::unique_ptr<RpcAuth> mAuth;
};
} // namespace android
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index f88896f..49c7b7c 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -373,6 +373,12 @@
return clazz->getInterfaceDescriptorUtf8();
}
+AIBinder_DeathRecipient::TransferDeathRecipient::~TransferDeathRecipient() {
+ if (mOnUnlinked != nullptr) {
+ mOnUnlinked(mCookie);
+ }
+}
+
void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get()
<< " (" << mWho.get_refs() << ")";
@@ -394,7 +400,7 @@
}
AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied)
- : mOnDied(onDied) {
+ : mOnDied(onDied), mOnUnlinked(nullptr) {
CHECK(onDied != nullptr);
}
@@ -412,10 +418,12 @@
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
sp<TransferDeathRecipient> recipient =
- new TransferDeathRecipient(binder, cookie, this, mOnDied);
+ new TransferDeathRecipient(binder, cookie, this, mOnDied, mOnUnlinked);
status_t status = binder->linkToDeath(recipient, cookie, 0 /*flags*/);
if (status != STATUS_OK) {
+ // When we failed to link, the destructor of TransferDeathRecipient runs here, which
+ // ensures that mOnUnlinked is called before we return with an error from this method.
return PruneStatusT(status);
}
@@ -448,6 +456,10 @@
return STATUS_NAME_NOT_FOUND;
}
+void AIBinder_DeathRecipient::setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked) {
+ mOnUnlinked = onUnlinked;
+}
+
// start of C-API methods
AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) {
@@ -689,6 +701,15 @@
return ret;
}
+void AIBinder_DeathRecipient_setOnUnlinked(AIBinder_DeathRecipient* recipient,
+ AIBinder_DeathRecipient_onBinderUnlinked onUnlinked) {
+ if (recipient == nullptr) {
+ return;
+ }
+
+ recipient->setOnUnlinked(onUnlinked);
+}
+
void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) {
if (recipient == nullptr) {
return;
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 3cb95ea..730e51b 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -148,8 +148,14 @@
struct TransferDeathRecipient : ::android::IBinder::DeathRecipient {
TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie,
const ::android::wp<AIBinder_DeathRecipient>& parentRecipient,
- const AIBinder_DeathRecipient_onBinderDied onDied)
- : mWho(who), mCookie(cookie), mParentRecipient(parentRecipient), mOnDied(onDied) {}
+ const AIBinder_DeathRecipient_onBinderDied onDied,
+ const AIBinder_DeathRecipient_onBinderUnlinked onUnlinked)
+ : mWho(who),
+ mCookie(cookie),
+ mParentRecipient(parentRecipient),
+ mOnDied(onDied),
+ mOnUnlinked(onUnlinked) {}
+ ~TransferDeathRecipient();
void binderDied(const ::android::wp<::android::IBinder>& who) override;
@@ -165,11 +171,13 @@
// This is kept separately from AIBinder_DeathRecipient in case the death recipient is
// deleted while the death notification is fired
const AIBinder_DeathRecipient_onBinderDied mOnDied;
+ const AIBinder_DeathRecipient_onBinderUnlinked mOnUnlinked;
};
explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied);
binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie);
binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie);
+ void setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked);
private:
// When the user of this API deletes a Bp object but not the death recipient, the
@@ -180,4 +188,5 @@
std::mutex mDeathRecipientsMutex;
std::vector<::android::sp<TransferDeathRecipient>> mDeathRecipients;
AIBinder_DeathRecipient_onBinderDied mOnDied;
+ AIBinder_DeathRecipient_onBinderUnlinked mOnUnlinked;
};
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index b881c2c..43533c5 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -319,9 +319,9 @@
/**
* Registers for notifications that the associated binder is dead. The same death recipient may be
* associated with multiple different binders. If the binder is local, then no death recipient will
- * be given (since if the local process dies, then no recipient will exist to recieve a
+ * be given (since if the local process dies, then no recipient will exist to receive a
* transaction). The cookie is passed to recipient in the case that this binder dies and can be
- * null. The exact cookie must also be used to unlink this transaction (see AIBinder_linkToDeath).
+ * null. The exact cookie must also be used to unlink this transaction (see AIBinder_unlinkToDeath).
* This function may return a binder transaction failure. The cookie can be used both for
* identification and holding user data.
*
@@ -348,6 +348,10 @@
* If the binder dies, it will automatically unlink. If the binder is deleted, it will be
* automatically unlinked.
*
+ * Be aware that it is not safe to immediately deallocate the cookie when this call returns. If you
+ * need to clean up the cookie, you should do so in the onUnlinked callback, which can be set using
+ * AIBinder_DeathRecipient_setOnUnlinked.
+ *
* Available since API level 29.
*
* \param binder the binder object to remove a previously linked death recipient from.
@@ -568,6 +572,22 @@
typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_IN(29);
/**
+ * This function is intended for cleaning up the data in the provided cookie, and it is executed
+ * when the DeathRecipient is unlinked. When the DeathRecipient is unlinked due to a death receipt,
+ * this method is called after the call to onBinderDied.
+ *
+ * This method is called once for each binder that is unlinked. Hence, if the same cookie is passed
+ * to multiple binders, then the caller is responsible for reference counting the cookie.
+ *
+ * See also AIBinder_linkToDeath/AIBinder_unlinkToDeath.
+ *
+ * Available since API level 33.
+ *
+ * \param cookie the cookie passed to AIBinder_linkToDeath.
+ */
+typedef void (*AIBinder_DeathRecipient_onBinderUnlinked)(void* cookie) __INTRODUCED_IN(33);
+
+/**
* Creates a new binder death recipient. This can be attached to multiple different binder objects.
*
* Available since API level 29.
@@ -580,9 +600,47 @@
AIBinder_DeathRecipient_onBinderDied onBinderDied) __INTRODUCED_IN(29);
/**
+ * Set the callback to be called when this DeathRecipient is unlinked from a binder. The callback is
+ * called in the following situations:
+ *
+ * 1. If the binder died, shortly after the call to onBinderDied.
+ * 2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
+ * AIBinder_DeathRecipient_delete.
+ * 3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
+ *
+ * It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
+ * process is aborted before the binder is unlinked.
+ *
+ * Be aware that when the binder is explicitly unlinked, it is not guaranteed that onUnlinked has
+ * been called before the call to AIBinder_unlinkToDeath or AIBinder_DeathRecipient_delete returns.
+ * For example, if the binder dies concurrently with a call to AIBinder_unlinkToDeath, the binder is
+ * not unlinked until after the death notification is delivered, even if AIBinder_unlinkToDeath
+ * returns before that happens.
+ *
+ * This method should be called before linking the DeathRecipient to a binder because the function
+ * pointer is cached. If you change it after linking to a binder, it is unspecified whether the old
+ * binder will call the old or new onUnlinked callback.
+ *
+ * The onUnlinked argument may be null. In this case, no notification is given when the binder is
+ * unlinked.
+ *
+ * Available since API level 33.
+ *
+ * \param recipient the DeathRecipient to set the onUnlinked callback for.
+ * \param onUnlinked the callback to call when a binder is unlinked from recipient.
+ */
+void AIBinder_DeathRecipient_setOnUnlinked(AIBinder_DeathRecipient* recipient,
+ AIBinder_DeathRecipient_onBinderUnlinked onUnlinked)
+ __INTRODUCED_IN(33);
+
+/**
* Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before
* calling this as these will all be automatically unlinked.
*
+ * Be aware that it is not safe to immediately deallocate the cookie when this call returns. If you
+ * need to clean up the cookie, you should do so in the onUnlinked callback, which can be set using
+ * AIBinder_DeathRecipient_setOnUnlinked.
+ *
* Available since API level 29.
*
* \param recipient the binder to delete (previously created with AIBinder_DeathRecipient_new).
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index a2f5c93..8457581 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -1167,6 +1167,8 @@
/**
* Marshals the raw bytes of the Parcel to a buffer.
*
+ * Available since API level 33.
+ *
* The parcel must not contain any binders or file descriptors.
*
* The data you retrieve here must not be placed in any kind of persistent storage. (on local disk,
@@ -1189,6 +1191,8 @@
/**
* Set the data in the parcel to the raw bytes from the buffer.
*
+ * Available since API level 33.
+ *
* \param parcel The parcel to set data.
* \param buffer The data buffer to set.
* \param len The size of the data to set.
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index ac892db..8605686 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -144,6 +144,7 @@
LIBBINDER_NDK33 { # introduced=33
global:
AIBinder_Class_disableInterfaceTokenHeader;
+ AIBinder_DeathRecipient_setOnUnlinked;
AParcel_marshal;
AParcel_unmarshal;
};
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index a8ae441..8ed91a5 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -20,6 +20,7 @@
#include <android-base/logging.h>
using ::android::status_t;
+using ::android::statusToString;
using ::android::binder::Status;
AStatus* AStatus_newOk() {
@@ -126,7 +127,7 @@
return STATUS_UNKNOWN_ERROR;
default:
- LOG(WARNING) << __func__ << ": Unknown status_t (" << status
+ LOG(WARNING) << __func__ << ": Unknown status_t (" << statusToString(status)
<< ") pruned into STATUS_UNKNOWN_ERROR";
return STATUS_UNKNOWN_ERROR;
}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 4b36530..499f88e 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -395,9 +395,16 @@
<< "Service failed to shut down.";
}
+struct DeathRecipientCookie {
+ std::function<void(void)>*onDeath, *onUnlink;
+};
void LambdaOnDeath(void* cookie) {
- auto onDeath = static_cast<std::function<void(void)>*>(cookie);
- (*onDeath)();
+ auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+ (*funcs->onDeath)();
+};
+void LambdaOnUnlink(void* cookie) {
+ auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+ (*funcs->onUnlink)();
};
TEST(NdkBinder, DeathRecipient) {
using namespace std::chrono_literals;
@@ -409,26 +416,46 @@
std::mutex deathMutex;
std::condition_variable deathCv;
- bool deathRecieved = false;
+ bool deathReceived = false;
std::function<void(void)> onDeath = [&] {
std::cerr << "Binder died (as requested)." << std::endl;
- deathRecieved = true;
+ deathReceived = true;
deathCv.notify_one();
};
- AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
+ std::mutex unlinkMutex;
+ std::condition_variable unlinkCv;
+ bool unlinkReceived = false;
+ bool wasDeathReceivedFirst = false;
- EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath)));
+ std::function<void(void)> onUnlink = [&] {
+ std::cerr << "Binder unlinked (as requested)." << std::endl;
+ wasDeathReceivedFirst = deathReceived;
+ unlinkReceived = true;
+ unlinkCv.notify_one();
+ };
+
+ DeathRecipientCookie cookie = {&onDeath, &onUnlink};
+
+ AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
+ AIBinder_DeathRecipient_setOnUnlinked(recipient, LambdaOnUnlink);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&cookie)));
// the binder driver should return this if the service dies during the transaction
EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
foo = nullptr;
- std::unique_lock<std::mutex> lock(deathMutex);
- EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
- EXPECT_TRUE(deathRecieved);
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockDeath, 1s, [&] { return deathReceived; }));
+ EXPECT_TRUE(deathReceived);
+
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockUnlink, 1s, [&] { return unlinkReceived; }));
+ EXPECT_TRUE(unlinkReceived);
+ EXPECT_TRUE(wasDeathReceivedFirst);
AIBinder_DeathRecipient_delete(recipient);
AIBinder_decStrong(binder);
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index dd0c7b8..41ceee5 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -152,20 +152,46 @@
/// available.
fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
+ /// Create a Parcel that can be used with `submit_transact`.
+ fn prepare_transact(&self) -> Result<Parcel>;
+
/// Perform a generic operation with the object.
///
+ /// The provided [`Parcel`] must have been created by a call to
+ /// `prepare_transact` on the same binder.
+ ///
+ /// # Arguments
+ ///
+ /// * `code` - Transaction code for the operation.
+ /// * `data` - [`Parcel`] with input data.
+ /// * `flags` - Transaction flags, e.g. marking the transaction as
+ /// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)).
+ fn submit_transact(
+ &self,
+ code: TransactionCode,
+ data: Parcel,
+ flags: TransactionFlags,
+ ) -> Result<Parcel>;
+
+ /// Perform a generic operation with the object. This is a convenience
+ /// method that internally calls `prepare_transact` followed by
+ /// `submit_transact.
+ ///
/// # Arguments
/// * `code` - Transaction code for the operation
- /// * `data` - [`Parcel`] with input data
- /// * `reply` - Optional [`Parcel`] for reply data
/// * `flags` - Transaction flags, e.g. marking the transaction as
/// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY))
+ /// * `input_callback` A callback for building the `Parcel`.
fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
&self,
code: TransactionCode,
flags: TransactionFlags,
input_callback: F,
- ) -> Result<Parcel>;
+ ) -> Result<Parcel> {
+ let mut parcel = self.prepare_transact()?;
+ input_callback(&mut parcel)?;
+ self.submit_transact(code, parcel, flags)
+ }
}
/// Interface of binder local or remote objects.
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index e7c3396..a91092e 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -441,6 +441,8 @@
///
/// Registers the given binder object with the given identifier. If successful,
/// this service can then be retrieved using that identifier.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
let instance = CString::new(identifier).unwrap();
let status = unsafe {
@@ -462,6 +464,8 @@
///
/// If any service in the process is registered as lazy, all should be, otherwise
/// the process may be shut down while a service is in use.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
let instance = CString::new(identifier).unwrap();
let status = unsafe {
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index a0e991c..dad89ec 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -25,6 +25,7 @@
use std::convert::TryInto;
use std::mem::ManuallyDrop;
use std::ptr;
+use std::fmt;
mod file_descriptor;
mod parcelable;
@@ -96,6 +97,13 @@
let _ = ManuallyDrop::new(self);
ptr
}
+
+ pub(crate) fn is_owned(&self) -> bool {
+ match *self {
+ Self::Owned(_) => true,
+ Self::Borrowed(_) => false,
+ }
+ }
}
// Data serialization methods
@@ -412,6 +420,13 @@
}
}
+impl fmt::Debug for Parcel {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Parcel")
+ .finish()
+ }
+}
+
#[cfg(test)]
impl Parcel {
/// Create a new parcel tied to a bogus binder. TESTING ONLY!
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 179b7c8..f71a686 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -23,7 +23,7 @@
use crate::sys;
use std::fs::File;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
/// Rust version of the Java class android.os.ParcelFileDescriptor
#[derive(Debug)]
@@ -54,6 +54,12 @@
}
}
+impl IntoRawFd for ParcelFileDescriptor {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
impl Serialize for ParcelFileDescriptor {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
let fd = self.0.as_raw_fd();
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index b03ed49..68fa34b 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -31,8 +31,10 @@
use std::convert::TryInto;
use std::ffi::{c_void, CString};
use std::fmt;
+use std::mem;
use std::os::unix::io::AsRawFd;
use std::ptr;
+use std::sync::Arc;
/// A strong reference to a Binder remote object.
///
@@ -233,13 +235,7 @@
}
impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
- /// Perform a binder transaction
- fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
- &self,
- code: TransactionCode,
- flags: TransactionFlags,
- input_callback: F,
- ) -> Result<Parcel> {
+ fn prepare_transact(&self) -> Result<Parcel> {
let mut input = ptr::null_mut();
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -252,15 +248,25 @@
// pointer, or null.
sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input)
};
+
status_result(status)?;
- let mut input = unsafe {
+
+ unsafe {
// Safety: At this point, `input` is either a valid, owned `AParcel`
// pointer, or null. `Parcel::owned` safely handles both cases,
// taking ownership of the parcel.
- Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)?
- };
- input_callback(&mut input)?;
+ Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
+ }
+ }
+
+ fn submit_transact(
+ &self,
+ code: TransactionCode,
+ data: Parcel,
+ flags: TransactionFlags,
+ ) -> Result<Parcel> {
let mut reply = ptr::null_mut();
+ assert!(data.is_owned());
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. Although `IBinder::transact` is
@@ -275,13 +281,13 @@
// only providing `on_transact` with an immutable reference to
// `self`.
//
- // This call takes ownership of the `input` parcel pointer, and
+ // This call takes ownership of the `data` parcel pointer, and
// passes ownership of the `reply` out parameter to its caller. It
// does not affect ownership of the `binder` parameter.
sys::AIBinder_transact(
self.as_native() as *mut sys::AIBinder,
code,
- &mut input.into_raw(),
+ &mut data.into_raw(),
&mut reply,
flags,
)
@@ -378,13 +384,17 @@
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. `recipient` can always be
// converted into a valid pointer to an
- // `AIBinder_DeathRecipient`. Any value is safe to pass as the
- // cookie, although we depend on this value being set by
- // `get_cookie` when the death recipient callback is called.
+ // `AIBinder_DeathRecipient`.
+ //
+ // The cookie is also the correct pointer, and by calling new_cookie,
+ // we have created a new ref-count to the cookie, which linkToDeath
+ // takes ownership of. Once the DeathRecipient is unlinked for any
+ // reason (including if this call fails), the onUnlinked callback
+ // will consume that ref-count.
sys::AIBinder_linkToDeath(
self.as_native_mut(),
recipient.as_native_mut(),
- recipient.get_cookie(),
+ recipient.new_cookie(),
)
})
}
@@ -552,10 +562,20 @@
}
/// Rust wrapper around DeathRecipient objects.
+///
+/// The cookie in this struct represents an Arc<F> for the owned callback.
+/// This struct owns a ref-count of it, and so does every binder that we
+/// have been linked with.
#[repr(C)]
pub struct DeathRecipient {
recipient: *mut sys::AIBinder_DeathRecipient,
- callback: Box<dyn Fn() + Send + 'static>,
+ cookie: *mut c_void,
+ vtable: &'static DeathRecipientVtable,
+}
+
+struct DeathRecipientVtable {
+ cookie_incr_refcount: unsafe extern "C" fn(*mut c_void),
+ cookie_decr_refcount: unsafe extern "C" fn(*mut c_void),
}
impl DeathRecipient {
@@ -563,9 +583,9 @@
/// associated object dies.
pub fn new<F>(callback: F) -> DeathRecipient
where
- F: Fn() + Send + 'static,
+ F: Fn() + Send + Sync + 'static,
{
- let callback = Box::new(callback);
+ let callback: *const F = Arc::into_raw(Arc::new(callback));
let recipient = unsafe {
// Safety: The function pointer is a valid death recipient callback.
//
@@ -574,34 +594,85 @@
// no longer needed.
sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
};
+ unsafe {
+ // Safety: The function pointer is a valid onUnlinked callback.
+ //
+ // All uses of linkToDeath in this file correctly increment the
+ // ref-count that this onUnlinked callback will decrement.
+ sys::AIBinder_DeathRecipient_setOnUnlinked(recipient, Some(Self::cookie_decr_refcount::<F>));
+ }
DeathRecipient {
recipient,
- callback,
+ cookie: callback as *mut c_void,
+ vtable: &DeathRecipientVtable {
+ cookie_incr_refcount: Self::cookie_incr_refcount::<F>,
+ cookie_decr_refcount: Self::cookie_decr_refcount::<F>,
+ },
}
}
+ /// Increment the ref-count for the cookie and return it.
+ ///
+ /// # Safety
+ ///
+ /// The caller must handle the returned ref-count correctly.
+ unsafe fn new_cookie(&self) -> *mut c_void {
+ (self.vtable.cookie_incr_refcount)(self.cookie);
+
+ // Return a raw pointer with ownership of a ref-count
+ self.cookie
+ }
+
/// Get the opaque cookie that identifies this death recipient.
///
/// This cookie will be used to link and unlink this death recipient to a
/// binder object and will be passed to the `binder_died` callback as an
/// opaque userdata pointer.
fn get_cookie(&self) -> *mut c_void {
- &*self.callback as *const _ as *mut c_void
+ self.cookie
}
/// Callback invoked from C++ when the binder object dies.
///
/// # Safety
///
- /// The `cookie` parameter must have been created with the `get_cookie`
- /// method of this object.
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the caller must hold a ref-count to it.
unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
where
- F: Fn() + Send + 'static,
+ F: Fn() + Send + Sync + 'static,
{
- let callback = (cookie as *mut F).as_ref().unwrap();
+ let callback = (cookie as *const F).as_ref().unwrap();
callback();
}
+
+ /// Callback that decrements the ref-count.
+ /// This is invoked from C++ when a binder is unlinked.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the owner must give up a ref-count to it.
+ unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn() + Send + Sync + 'static,
+ {
+ drop(Arc::from_raw(cookie as *const F));
+ }
+
+ /// Callback that increments the ref-count.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the owner must handle the created ref-count properly.
+ unsafe extern "C" fn cookie_incr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn() + Send + Sync + 'static,
+ {
+ let arc = mem::ManuallyDrop::new(Arc::from_raw(cookie as *const F));
+ mem::forget(Arc::clone(&arc));
+ }
}
/// # Safety
@@ -627,6 +698,12 @@
// `AIBinder_DeathRecipient_new` when `self` was created. This
// delete method can only be called once when `self` is dropped.
sys::AIBinder_DeathRecipient_delete(self.recipient);
+
+ // Safety: We own a ref-count to the cookie, and so does every
+ // linked binder. This call gives up our ref-count. The linked
+ // binders should already have given up their ref-count, or should
+ // do so shortly.
+ (self.vtable.cookie_decr_refcount)(self.cookie)
}
}
}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index da8907d..335e8d8 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -363,13 +363,58 @@
);
}
- fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
+ struct Bools {
+ binder_died: Arc<AtomicBool>,
+ binder_dealloc: Arc<AtomicBool>,
+ }
+
+ impl Bools {
+ fn is_dead(&self) -> bool {
+ self.binder_died.load(Ordering::Relaxed)
+ }
+ fn assert_died(&self) {
+ assert!(
+ self.is_dead(),
+ "Did not receive death notification"
+ );
+ }
+ fn assert_dropped(&self) {
+ assert!(
+ self.binder_dealloc.load(Ordering::Relaxed),
+ "Did not dealloc death notification"
+ );
+ }
+ fn assert_not_dropped(&self) {
+ assert!(
+ !self.binder_dealloc.load(Ordering::Relaxed),
+ "Dealloc death notification too early"
+ );
+ }
+ }
+
+ fn register_death_notification(binder: &mut SpIBinder) -> (Bools, DeathRecipient) {
let binder_died = Arc::new(AtomicBool::new(false));
+ let binder_dealloc = Arc::new(AtomicBool::new(false));
+
+ struct SetOnDrop {
+ binder_dealloc: Arc<AtomicBool>,
+ }
+ impl Drop for SetOnDrop {
+ fn drop(&mut self) {
+ self.binder_dealloc.store(true, Ordering::Relaxed);
+ }
+ }
let mut death_recipient = {
let flag = binder_died.clone();
+ let set_on_drop = SetOnDrop {
+ binder_dealloc: binder_dealloc.clone(),
+ };
DeathRecipient::new(move || {
flag.store(true, Ordering::Relaxed);
+ // Force the closure to take ownership of set_on_drop. When the closure is
+ // dropped, the destructor of `set_on_drop` will run.
+ let _ = &set_on_drop;
})
};
@@ -377,7 +422,12 @@
.link_to_death(&mut death_recipient)
.expect("link_to_death failed");
- (binder_died, death_recipient)
+ let bools = Bools {
+ binder_died,
+ binder_dealloc,
+ };
+
+ (bools, death_recipient)
}
/// Killing a remote service should unregister the service and trigger
@@ -390,7 +440,7 @@
let service_process = ScopedServiceProcess::new(service_name);
let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
- let (binder_died, _recipient) = register_death_notification(&mut remote);
+ let (bools, recipient) = register_death_notification(&mut remote);
drop(service_process);
remote
@@ -400,10 +450,12 @@
// Pause to ensure any death notifications get delivered
thread::sleep(Duration::from_secs(1));
- assert!(
- binder_died.load(Ordering::Relaxed),
- "Did not receive death notification"
- );
+ bools.assert_died();
+ bools.assert_not_dropped();
+
+ drop(recipient);
+
+ bools.assert_dropped();
}
/// Test unregistering death notifications.
@@ -415,7 +467,7 @@
let service_process = ScopedServiceProcess::new(service_name);
let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
- let (binder_died, mut recipient) = register_death_notification(&mut remote);
+ let (bools, mut recipient) = register_death_notification(&mut remote);
remote
.unlink_to_death(&mut recipient)
@@ -430,9 +482,13 @@
thread::sleep(Duration::from_secs(1));
assert!(
- !binder_died.load(Ordering::Relaxed),
+ !bools.is_dead(),
"Received unexpected death notification after unlinking",
);
+
+ bools.assert_not_dropped();
+ drop(recipient);
+ bools.assert_dropped();
}
/// Dropping a remote handle should unregister any death notifications.
@@ -444,7 +500,7 @@
let service_process = ScopedServiceProcess::new(service_name);
let mut remote = binder::get_service(service_name).expect("Could not retrieve service");
- let (binder_died, _recipient) = register_death_notification(&mut remote);
+ let (bools, recipient) = register_death_notification(&mut remote);
// This should automatically unregister our death notification.
drop(remote);
@@ -457,9 +513,13 @@
// We dropped the remote handle, so we should not receive the death
// notification when the remote process dies here.
assert!(
- !binder_died.load(Ordering::Relaxed),
+ !bools.is_dead(),
"Received unexpected death notification after dropping remote handle"
);
+
+ bools.assert_not_dropped();
+ drop(recipient);
+ bools.assert_dropped();
}
/// Test IBinder interface methods not exercised elsewhere.
@@ -637,4 +697,27 @@
assert!(!(service1 > service1));
assert_eq!(service1 < service2, !(service2 < service1));
}
+
+ #[test]
+ fn binder_parcel_mixup() {
+ let service1 = BnTest::new_binder(
+ TestService::new("testing_service1"),
+ BinderFeatures::default(),
+ );
+ let service2 = BnTest::new_binder(
+ TestService::new("testing_service2"),
+ BinderFeatures::default(),
+ );
+
+ let service1 = service1.as_binder();
+ let service2 = service2.as_binder();
+
+ let parcel = service1.prepare_transact().unwrap();
+ let res = service2.submit_transact(super::TestTransactionCode::Test as binder::TransactionCode, parcel, 0);
+
+ match res {
+ Ok(_) => panic!("submit_transact should fail"),
+ Err(err) => assert_eq!(err, binder::StatusCode::BAD_VALUE),
+ }
+ }
}
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 9811cdf..23e34aa 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -157,6 +157,11 @@
std::optional<std::string>* _aidl_return) override {
return mImpl->updatableViaApex(name, _aidl_return);
}
+ android::binder::Status getConnectionInfo(
+ const std::string& name,
+ std::optional<android::os::ConnectionInfo>* _aidl_return) override {
+ return mImpl->getConnectionInfo(name, _aidl_return);
+ }
android::binder::Status registerClientCallback(
const std::string&, const android::sp<android::IBinder>&,
const android::sp<android::os::IClientCallback>&) override {
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 1968058..680f0ed 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -134,6 +134,37 @@
},
}
+cc_library_static {
+ name: "libbinder_tls_test_utils",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ defaults: [
+ "binder_test_defaults",
+ "libbinder_tls_shared_deps",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libbinder_tls_static",
+ ],
+ srcs: [
+ "RpcTlsTestUtils.cpp",
+ ],
+ export_include_dirs: [
+ "include_tls_test_utils",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
cc_test {
name: "binderRpcTest",
host_supported: true,
@@ -153,7 +184,6 @@
srcs: [
"binderRpcTest.cpp",
- "RpcCertificateVerifierSimple.cpp",
],
shared_libs: [
"libbinder",
@@ -165,6 +195,7 @@
],
static_libs: [
"libbinder_tls_static",
+ "libbinder_tls_test_utils",
"binderRpcTestIface-cpp",
"binderRpcTestIface-ndk",
],
@@ -172,9 +203,43 @@
require_root: true,
}
+cc_test {
+ name: "RpcTlsUtilsTest",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ android: {
+ test_suites: ["vts"],
+ },
+ },
+ defaults: [
+ "binder_test_defaults",
+ "libbinder_tls_shared_deps",
+ ],
+ srcs: [
+ "RpcTlsUtilsTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbase",
+ "libutils",
+ "liblog",
+ ],
+ static_libs: [
+ "libbinder_tls_test_utils",
+ "libbinder_tls_static",
+ ],
+ test_suites: ["general-tests", "device-tests"],
+}
+
cc_benchmark {
name: "binderRpcBenchmark",
- defaults: ["binder_test_defaults"],
+ defaults: [
+ "binder_test_defaults",
+ "libbinder_tls_shared_deps",
+ ],
host_supported: true,
target: {
darwin: {
@@ -191,6 +256,10 @@
"liblog",
"libutils",
],
+ static_libs: [
+ "libbinder_tls_test_utils",
+ "libbinder_tls_static",
+ ],
}
cc_test {
diff --git a/libs/binder/tests/RpcCertificateVerifierSimple.cpp b/libs/binder/tests/RpcCertificateVerifierSimple.cpp
deleted file mode 100644
index 4694d1b..0000000
--- a/libs/binder/tests/RpcCertificateVerifierSimple.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define LOG_TAG "RpcCertificateVerifierSimple"
-#include <log/log.h>
-
-#include <binder/RpcCertificateUtils.h>
-
-#include "RpcCertificateVerifierSimple.h"
-
-namespace android {
-
-status_t RpcCertificateVerifierSimple::verify(const X509* peerCert, uint8_t* outAlert) {
- std::lock_guard<std::mutex> lock(mMutex);
- for (const auto& trustedCert : mTrustedPeerCertificates) {
- if (0 == X509_cmp(trustedCert.get(), peerCert)) {
- return OK;
- }
- }
- *outAlert = SSL_AD_CERTIFICATE_UNKNOWN;
- return PERMISSION_DENIED;
-}
-
-status_t RpcCertificateVerifierSimple::addTrustedPeerCertificate(RpcCertificateFormat format,
- const std::vector<uint8_t>& cert) {
- bssl::UniquePtr<X509> x509 = deserializeCertificate(cert, format);
- if (x509 == nullptr) {
- ALOGE("Certificate is not in the proper format %s", PrintToString(format).c_str());
- return BAD_VALUE;
- }
- std::lock_guard<std::mutex> lock(mMutex);
- mTrustedPeerCertificates.push_back(std::move(x509));
- return OK;
-}
-
-} // namespace android
diff --git a/libs/binder/tests/RpcCertificateVerifierSimple.h b/libs/binder/tests/RpcCertificateVerifierSimple.h
deleted file mode 100644
index 1f2e531..0000000
--- a/libs/binder/tests/RpcCertificateVerifierSimple.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <mutex>
-#include <string_view>
-#include <vector>
-
-#include <openssl/ssl.h>
-
-#include <binder/RpcCertificateFormat.h>
-#include <binder/RpcCertificateVerifier.h>
-
-namespace android {
-
-// A simple certificate verifier for testing.
-// Keep a list of leaf certificates as trusted. No certificate chain support.
-//
-// All APIs are thread-safe. However, if verify() and addTrustedPeerCertificate() are called
-// simultaneously in different threads, it is not deterministic whether verify() will use the
-// certificate being added.
-class RpcCertificateVerifierSimple : public RpcCertificateVerifier {
-public:
- status_t verify(const X509*, uint8_t*) override;
-
- // Add a trusted peer certificate. Peers presenting this certificate are accepted.
- //
- // Caller must ensure that RpcTransportCtx::newTransport() are called after all trusted peer
- // certificates are added. Otherwise, RpcTransport-s created before may not trust peer
- // certificates added later.
- [[nodiscard]] status_t addTrustedPeerCertificate(RpcCertificateFormat format,
- const std::vector<uint8_t>& cert);
-
-private:
- std::mutex mMutex; // for below
- std::vector<bssl::UniquePtr<X509>> mTrustedPeerCertificates;
-};
-
-} // namespace android
diff --git a/libs/binder/tests/RpcTlsTestUtils.cpp b/libs/binder/tests/RpcTlsTestUtils.cpp
new file mode 100644
index 0000000..6119313
--- /dev/null
+++ b/libs/binder/tests/RpcTlsTestUtils.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RpcTlsTestUtils"
+#include <log/log.h>
+
+#include <binder/RpcTlsTestUtils.h>
+
+#include <binder/RpcTlsUtils.h>
+
+#include "../Utils.h" // for TEST_AND_RETURN
+
+namespace android {
+
+bssl::UniquePtr<EVP_PKEY> makeKeyPairForSelfSignedCert() {
+ bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+ if (ec_key == nullptr || !EC_KEY_generate_key(ec_key.get())) {
+ ALOGE("Failed to generate key pair.");
+ return nullptr;
+ }
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+ // Use set1 instead of assign to avoid leaking ec_key when assign fails. set1 increments
+ // the refcount of the ec_key, so it is okay to release it at the end of this function.
+ if (pkey == nullptr || !EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get())) {
+ ALOGE("Failed to assign key pair.");
+ return nullptr;
+ }
+ return pkey;
+}
+
+bssl::UniquePtr<X509> makeSelfSignedCert(EVP_PKEY* pkey, const uint32_t validSeconds) {
+ bssl::UniquePtr<X509> x509(X509_new());
+ bssl::UniquePtr<BIGNUM> serial(BN_new());
+ bssl::UniquePtr<BIGNUM> serialLimit(BN_new());
+ TEST_AND_RETURN(nullptr, BN_lshift(serialLimit.get(), BN_value_one(), 128));
+ TEST_AND_RETURN(nullptr, BN_rand_range(serial.get(), serialLimit.get()));
+ TEST_AND_RETURN(nullptr, BN_to_ASN1_INTEGER(serial.get(), X509_get_serialNumber(x509.get())));
+ TEST_AND_RETURN(nullptr, X509_gmtime_adj(X509_getm_notBefore(x509.get()), 0));
+ TEST_AND_RETURN(nullptr, X509_gmtime_adj(X509_getm_notAfter(x509.get()), validSeconds));
+
+ X509_NAME* subject = X509_get_subject_name(x509.get());
+ TEST_AND_RETURN(nullptr,
+ X509_NAME_add_entry_by_txt(subject, "O", MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>("Android"), -1, -1,
+ 0));
+ TEST_AND_RETURN(nullptr,
+ X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>("BinderRPC"), -1,
+ -1, 0));
+ TEST_AND_RETURN(nullptr, X509_set_issuer_name(x509.get(), subject));
+
+ TEST_AND_RETURN(nullptr, X509_set_pubkey(x509.get(), pkey));
+ TEST_AND_RETURN(nullptr, X509_sign(x509.get(), pkey, EVP_sha256()));
+ return x509;
+}
+
+status_t RpcAuthSelfSigned::configure(SSL_CTX* ctx) {
+ auto pkey = makeKeyPairForSelfSignedCert();
+ TEST_AND_RETURN(UNKNOWN_ERROR, pkey != nullptr);
+ auto cert = makeSelfSignedCert(pkey.get(), mValidSeconds);
+ TEST_AND_RETURN(UNKNOWN_ERROR, cert != nullptr);
+ TEST_AND_RETURN(INVALID_OPERATION, SSL_CTX_use_PrivateKey(ctx, pkey.get()));
+ TEST_AND_RETURN(INVALID_OPERATION, SSL_CTX_use_certificate(ctx, cert.get()));
+ return OK;
+}
+
+status_t RpcAuthPreSigned::configure(SSL_CTX* ctx) {
+ if (!SSL_CTX_use_PrivateKey(ctx, mPkey.get())) {
+ return INVALID_OPERATION;
+ }
+ if (!SSL_CTX_use_certificate(ctx, mCert.get())) {
+ return INVALID_OPERATION;
+ }
+ return OK;
+}
+
+status_t RpcCertificateVerifierSimple::verify(const SSL* ssl, uint8_t* outAlert) {
+ const char* logPrefix = SSL_is_server(ssl) ? "Server" : "Client";
+ bssl::UniquePtr<X509> peerCert(SSL_get_peer_certificate(ssl)); // Does not set error queue
+ LOG_ALWAYS_FATAL_IF(peerCert == nullptr,
+ "%s: libssl should not ask to verify non-existing cert", logPrefix);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ for (const auto& trustedCert : mTrustedPeerCertificates) {
+ if (0 == X509_cmp(trustedCert.get(), peerCert.get())) {
+ return OK;
+ }
+ }
+ *outAlert = SSL_AD_CERTIFICATE_UNKNOWN;
+ return PERMISSION_DENIED;
+}
+
+status_t RpcCertificateVerifierSimple::addTrustedPeerCertificate(RpcCertificateFormat format,
+ const std::vector<uint8_t>& cert) {
+ bssl::UniquePtr<X509> x509 = deserializeCertificate(cert, format);
+ if (x509 == nullptr) {
+ ALOGE("Certificate is not in the proper format %s", PrintToString(format).c_str());
+ return BAD_VALUE;
+ }
+ std::lock_guard<std::mutex> lock(mMutex);
+ mTrustedPeerCertificates.push_back(std::move(x509));
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/RpcTlsUtilsTest.cpp b/libs/binder/tests/RpcTlsUtilsTest.cpp
new file mode 100644
index 0000000..530606c
--- /dev/null
+++ b/libs/binder/tests/RpcTlsUtilsTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 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 <binder/RpcTlsTestUtils.h>
+#include <binder/RpcTlsUtils.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+std::string toDebugString(EVP_PKEY* pkey) {
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+ int res = EVP_PKEY_print_public(bio.get(), pkey, 2, nullptr);
+ std::string buf = "\nEVP_PKEY_print_public -> " + std::to_string(res) + "\n";
+ if (BIO_write(bio.get(), buf.data(), buf.length()) <= 0) return {};
+ res = EVP_PKEY_print_private(bio.get(), pkey, 2, nullptr);
+ buf = "\nEVP_PKEY_print_private -> " + std::to_string(res);
+ if (BIO_write(bio.get(), buf.data(), buf.length()) <= 0) return {};
+ const uint8_t* data;
+ size_t len;
+ if (!BIO_mem_contents(bio.get(), &data, &len)) return {};
+ return std::string(reinterpret_cast<const char*>(data), len);
+}
+
+class RpcTlsUtilsKeyTest : public testing::TestWithParam<RpcKeyFormat> {
+public:
+ static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ return PrintToString(info.param);
+ }
+};
+
+TEST_P(RpcTlsUtilsKeyTest, Test) {
+ auto pkey = makeKeyPairForSelfSignedCert();
+ ASSERT_NE(nullptr, pkey);
+ auto pkeyData = serializeUnencryptedPrivatekey(pkey.get(), GetParam());
+ auto deserializedPkey = deserializeUnencryptedPrivatekey(pkeyData, GetParam());
+ ASSERT_NE(nullptr, deserializedPkey);
+ EXPECT_EQ(1, EVP_PKEY_cmp(pkey.get(), deserializedPkey.get()))
+ << "expected: " << toDebugString(pkey.get())
+ << "\nactual: " << toDebugString(deserializedPkey.get());
+}
+
+INSTANTIATE_TEST_CASE_P(RpcTlsUtilsTest, RpcTlsUtilsKeyTest,
+ testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER),
+ RpcTlsUtilsKeyTest::PrintParamInfo);
+
+class RpcTlsUtilsCertTest : public testing::TestWithParam<RpcCertificateFormat> {
+public:
+ static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ return PrintToString(info.param);
+ }
+};
+
+TEST_P(RpcTlsUtilsCertTest, Test) {
+ auto pkey = makeKeyPairForSelfSignedCert();
+ ASSERT_NE(nullptr, pkey);
+ // Make certificate from the original key in memory
+ auto cert = makeSelfSignedCert(pkey.get(), kCertValidSeconds);
+ ASSERT_NE(nullptr, cert);
+ auto certData = serializeCertificate(cert.get(), GetParam());
+ auto deserializedCert = deserializeCertificate(certData, GetParam());
+ ASSERT_NE(nullptr, deserializedCert);
+ EXPECT_EQ(0, X509_cmp(cert.get(), deserializedCert.get()));
+}
+
+INSTANTIATE_TEST_CASE_P(RpcTlsUtilsTest, RpcTlsUtilsCertTest,
+ testing::Values(RpcCertificateFormat::PEM, RpcCertificateFormat::DER),
+ RpcTlsUtilsCertTest::PrintParamInfo);
+
+class RpcTlsUtilsKeyAndCertTest
+ : public testing::TestWithParam<std::tuple<RpcKeyFormat, RpcCertificateFormat>> {
+public:
+ static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ auto [keyFormat, certificateFormat] = info.param;
+ return "key_" + PrintToString(keyFormat) + "_cert_" + PrintToString(certificateFormat);
+ }
+};
+
+TEST_P(RpcTlsUtilsKeyAndCertTest, TestCertFromDeserializedKey) {
+ auto [keyFormat, certificateFormat] = GetParam();
+ auto pkey = makeKeyPairForSelfSignedCert();
+ ASSERT_NE(nullptr, pkey);
+ auto pkeyData = serializeUnencryptedPrivatekey(pkey.get(), keyFormat);
+ auto deserializedPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat);
+ ASSERT_NE(nullptr, deserializedPkey);
+
+ // Make certificate from deserialized key loaded from bytes
+ auto cert = makeSelfSignedCert(deserializedPkey.get(), kCertValidSeconds);
+ ASSERT_NE(nullptr, cert);
+ auto certData = serializeCertificate(cert.get(), certificateFormat);
+ auto deserializedCert = deserializeCertificate(certData, certificateFormat);
+ ASSERT_NE(nullptr, deserializedCert);
+ EXPECT_EQ(0, X509_cmp(cert.get(), deserializedCert.get()));
+}
+
+INSTANTIATE_TEST_CASE_P(RpcTlsUtilsTest, RpcTlsUtilsKeyAndCertTest,
+ testing::Combine(testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER),
+ testing::Values(RpcCertificateFormat::PEM,
+ RpcCertificateFormat::DER)),
+ RpcTlsUtilsKeyAndCertTest::PrintParamInfo);
+
+} // namespace android
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 3f72b8f..eec3b44 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -75,10 +75,10 @@
auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr);
ASSERT_THAT(debuggableResult, Ok());
ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult;
- auto debuggableBool = ParseBool(Trim(debuggableResult->stdout));
- ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdout);
+ auto debuggableBool = ParseBool(Trim(debuggableResult->stdoutStr));
+ ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdoutStr);
if (debuggableBool == ParseBoolResult::kFalse) {
- GTEST_SKIP() << "ro.debuggable=" << Trim(debuggableResult->stdout);
+ GTEST_SKIP() << "ro.debuggable=" << Trim(debuggableResult->stdoutStr);
}
auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 639876f..c69203b 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -491,7 +491,7 @@
EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
- bool sync_received, async_received;
+ uint32_t sync_received, async_received;
EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received,
&async_received));
@@ -499,15 +499,7 @@
EXPECT_EQ(sync_received, 1);
EXPECT_EQ(async_received, 0);
- uint32_t sync_received2, async_received2;
-
- EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received2,
- &async_received2));
-
- EXPECT_EQ(sync_received2, 1);
- EXPECT_EQ(async_received2, 0);
-
- EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
}
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index e430c28..6bf6e92 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -21,8 +21,15 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <binder/RpcCertificateFormat.h>
+#include <binder/RpcCertificateVerifier.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
+#include <binder/RpcTlsTestUtils.h>
+#include <binder/RpcTlsUtils.h>
+#include <binder/RpcTransportRaw.h>
+#include <binder/RpcTransportTls.h>
+#include <openssl/ssl.h>
#include <thread>
@@ -39,8 +46,15 @@
using android::IServiceManager;
using android::OK;
using android::ProcessState;
+using android::RpcAuthPreSigned;
+using android::RpcCertificateFormat;
+using android::RpcCertificateVerifier;
+using android::RpcCertificateVerifierNoOp;
using android::RpcServer;
using android::RpcSession;
+using android::RpcTransportCtxFactory;
+using android::RpcTransportCtxFactoryRaw;
+using android::RpcTransportCtxFactoryTls;
using android::sp;
using android::status_t;
using android::statusToString;
@@ -65,15 +79,32 @@
enum Transport {
KERNEL,
RPC,
+ RPC_TLS,
};
static const std::initializer_list<int64_t> kTransportList = {
#ifdef __BIONIC__
Transport::KERNEL,
#endif
- Transport::RPC};
+ Transport::RPC,
+ Transport::RPC_TLS,
+};
+
+std::unique_ptr<RpcTransportCtxFactory> makeFactoryTls() {
+ auto pkey = android::makeKeyPairForSelfSignedCert();
+ CHECK_NE(pkey.get(), nullptr);
+ auto cert = android::makeSelfSignedCert(pkey.get(), android::kCertValidSeconds);
+ CHECK_NE(cert.get(), nullptr);
+
+ auto verifier = std::make_shared<RpcCertificateVerifierNoOp>();
+ auto auth = std::make_unique<RpcAuthPreSigned>(std::move(pkey), std::move(cert));
+ return RpcTransportCtxFactoryTls::make(verifier, std::move(auth));
+}
static sp<RpcSession> gSession = RpcSession::make();
+// Certificate validation happens during handshake and does not affect the result of benchmarks.
+// Skip certificate validation to simplify the setup process.
+static sp<RpcSession> gSessionTls = RpcSession::make(makeFactoryTls());
#ifdef __BIONIC__
static const String16 kKernelBinderInstance = String16(u"binderRpcBenchmark-control");
static sp<IBinder> gKernelBinder;
@@ -88,6 +119,8 @@
#endif
case RPC:
return gSession->getRootObject();
+ case RPC_TLS:
+ return gSessionTls->getRootObject();
default:
LOG(FATAL) << "Unknown transport value: " << transport;
return nullptr;
@@ -169,26 +202,35 @@
}
BENCHMARK(BM_repeatBinder)->ArgsProduct({kTransportList});
+void forkRpcServer(const char* addr, const sp<RpcServer>& server) {
+ if (0 == fork()) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
+ server->setRootObject(sp<MyBinderRpcBenchmark>::make());
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ CHECK_EQ(OK, server->setupUnixDomainServer(addr));
+ server->join();
+ exit(1);
+ }
+}
+
+void setupClient(const sp<RpcSession>& session, const char* addr) {
+ status_t status;
+ for (size_t tries = 0; tries < 5; tries++) {
+ usleep(10000);
+ status = session->setupUnixDomainClient(addr);
+ if (status == OK) break;
+ }
+ CHECK_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str();
+}
+
int main(int argc, char** argv) {
::benchmark::Initialize(&argc, argv);
if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
- std::string addr = std::string(getenv("TMPDIR") ?: "/tmp") + "/binderRpcBenchmark";
- (void)unlink(addr.c_str());
-
std::cerr << "Tests suffixes:" << std::endl;
std::cerr << "\t.../" << Transport::KERNEL << " is KERNEL" << std::endl;
std::cerr << "\t.../" << Transport::RPC << " is RPC" << std::endl;
-
- if (0 == fork()) {
- prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay
- sp<RpcServer> server = RpcServer::make();
- server->setRootObject(sp<MyBinderRpcBenchmark>::make());
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
- CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
- server->join();
- exit(1);
- }
+ std::cerr << "\t.../" << Transport::RPC_TLS << " is RPC with TLS" << std::endl;
#ifdef __BIONIC__
if (0 == fork()) {
@@ -207,14 +249,17 @@
CHECK_NE(nullptr, gKernelBinder.get());
#endif
- status_t status;
- for (size_t tries = 0; tries < 5; tries++) {
- usleep(10000);
- status = gSession->setupUnixDomainClient(addr.c_str());
- if (status == OK) goto success;
- }
- LOG(FATAL) << "Could not connect: " << statusToString(status).c_str();
-success:
+ std::string tmp = getenv("TMPDIR") ?: "/tmp";
+
+ std::string addr = tmp + "/binderRpcBenchmark";
+ (void)unlink(addr.c_str());
+ forkRpcServer(addr.c_str(), RpcServer::make(RpcTransportCtxFactoryRaw::make()));
+ setupClient(gSession, addr.c_str());
+
+ std::string tlsAddr = tmp + "/binderRpcTlsBenchmark";
+ (void)unlink(tlsAddr.c_str());
+ forkRpcServer(tlsAddr.c_str(), RpcServer::make(makeFactoryTls()));
+ setupClient(gSessionTls, tlsAddr.c_str());
::benchmark::RunSpecifiedBenchmarks();
return 0;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 2fd63a3..a1058bc 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -31,6 +31,8 @@
#include <binder/ProcessState.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
+#include <binder/RpcTlsTestUtils.h>
+#include <binder/RpcTlsUtils.h>
#include <binder/RpcTransport.h>
#include <binder/RpcTransportRaw.h>
#include <binder/RpcTransportTls.h>
@@ -48,9 +50,8 @@
#include "../FdTrigger.h"
#include "../RpcSocketAddress.h" // for testing preconnected clients
-#include "../RpcState.h" // for debugging
-#include "../vm_sockets.h" // for VMADDR_*
-#include "RpcCertificateVerifierSimple.h"
+#include "../RpcState.h" // for debugging
+#include "../vm_sockets.h" // for VMADDR_*
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -71,7 +72,8 @@
}
static inline std::unique_ptr<RpcTransportCtxFactory> newFactory(
- RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr) {
+ RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr,
+ std::unique_ptr<RpcAuth> auth = nullptr) {
switch (rpcSecurity) {
case RpcSecurity::RAW:
return RpcTransportCtxFactoryRaw::make();
@@ -79,7 +81,10 @@
if (verifier == nullptr) {
verifier = std::make_shared<RpcCertificateVerifierSimple>();
}
- return RpcTransportCtxFactoryTls::make(std::move(verifier));
+ if (auth == nullptr) {
+ auth = std::make_unique<RpcAuthSelfSigned>();
+ }
+ return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth));
}
default:
LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity);
@@ -615,25 +620,20 @@
status = session->setupPreconnectedClient({}, [=]() {
return connectTo(UnixSocketAddress(addr.c_str()));
});
- if (status == OK) goto success;
break;
case SocketType::UNIX:
status = session->setupUnixDomainClient(addr.c_str());
- if (status == OK) goto success;
break;
case SocketType::VSOCK:
status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort);
- if (status == OK) goto success;
break;
case SocketType::INET:
status = session->setupInetClient("127.0.0.1", serverInfo.port);
- if (status == OK) goto success;
break;
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
- LOG_ALWAYS_FATAL("Could not connect %s", statusToString(status).c_str());
- success:
+ CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
ret.sessions.push_back({session, session->getRootObject()});
}
return ret;
@@ -1041,10 +1041,18 @@
for (auto& t : threads) t.join();
}
+static void saturateThreadPool(size_t threadCount, const sp<IBinderRpcTest>& iface) {
+ std::vector<std::thread> threads;
+ for (size_t i = 0; i < threadCount; i++) {
+ threads.push_back(std::thread([&] { EXPECT_OK(iface->sleepMs(500)); }));
+ }
+ for (auto& t : threads) t.join();
+}
+
TEST_P(BinderRpc, OnewayStressTest) {
constexpr size_t kNumClientThreads = 10;
constexpr size_t kNumServerThreads = 10;
- constexpr size_t kNumCalls = 500;
+ constexpr size_t kNumCalls = 1000;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
@@ -1054,13 +1062,12 @@
for (size_t j = 0; j < kNumCalls; j++) {
EXPECT_OK(proc.rootIface->sendString("a"));
}
-
- // check threads are not stuck
- EXPECT_OK(proc.rootIface->sleepMs(250));
}));
}
for (auto& t : threads) t.join();
+
+ saturateThreadPool(kNumServerThreads, proc.rootIface);
}
TEST_P(BinderRpc, OnewayCallDoesNotWait) {
@@ -1087,26 +1094,23 @@
EXPECT_OK(proc.rootIface->lock());
- for (size_t i = 0; i < kNumSleeps; i++) {
- // these should be processed serially
+ size_t epochMsBefore = epochMillis();
+
+ // all these *Async commands should be queued on the server sequentially,
+ // even though there are multiple threads.
+ for (size_t i = 0; i + 1 < kNumSleeps; i++) {
proc.rootIface->sleepMsAsync(kSleepMs);
}
- // should also be processesed serially
EXPECT_OK(proc.rootIface->unlockInMsAsync(kSleepMs));
- size_t epochMsBefore = epochMillis();
+ // this can only return once the final async call has unlocked
EXPECT_OK(proc.rootIface->lockUnlock());
+
size_t epochMsAfter = epochMillis();
EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
- // pending oneway transactions hold ref, make sure we read data on all
- // sockets
- std::vector<std::thread> threads;
- for (size_t i = 0; i < 1 + kNumExtraServerThreads; i++) {
- threads.push_back(std::thread([&] { EXPECT_OK(proc.rootIface->sleepMs(250)); }));
- }
- for (auto& t : threads) t.join();
+ saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
}
TEST_P(BinderRpc, OnewayCallExhaustion) {
@@ -1435,18 +1439,10 @@
INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()),
BinderRpcSimple::PrintTestParam);
-class RpcTransportTest
- : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity, RpcCertificateFormat>> {
+class RpcTransportTestUtils {
public:
+ using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>;
using ConnectToServer = std::function<base::unique_fd()>;
- static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [socketType, rpcSecurity, certificateFormat] = info.param;
- return PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString() + "_" +
- PrintToString(certificateFormat);
- }
- void TearDown() override {
- for (auto& server : mServers) server->shutdownAndWait();
- }
// A server that handles client socket connections.
class Server {
@@ -1454,8 +1450,10 @@
explicit Server() {}
Server(Server&&) = default;
~Server() { shutdownAndWait(); }
- [[nodiscard]] AssertionResult setUp() {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ [[nodiscard]] AssertionResult setUp(
+ const Param& param,
+ std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
+ auto [socketType, rpcSecurity, certificateFormat] = param;
auto rpcServer = RpcServer::make(newFactory(rpcSecurity));
rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
switch (socketType) {
@@ -1506,7 +1504,7 @@
}
mFd = rpcServer->releaseServer();
if (!mFd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
- mCtx = newFactory(rpcSecurity, mCertVerifier)->newServerCtx();
+ mCtx = newFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx();
if (mCtx == nullptr) return AssertionFailure() << "newServerCtx";
mSetup = true;
return AssertionSuccess();
@@ -1575,7 +1573,7 @@
FdTrigger* fdTrigger) {
std::string message(kMessage);
auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
- message.size());
+ message.size(), {});
if (status != OK) return AssertionFailure() << statusToString(status);
return AssertionSuccess();
}
@@ -1585,8 +1583,8 @@
public:
explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {}
Client(Client&&) = default;
- [[nodiscard]] AssertionResult setUp() {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ [[nodiscard]] AssertionResult setUp(const Param& param) {
+ auto [socketType, rpcSecurity, certificateFormat] = param;
mFdTrigger = FdTrigger::make();
mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx();
if (mCtx == nullptr) return AssertionFailure() << "newClientCtx";
@@ -1608,7 +1606,7 @@
std::string readMessage(expectedMessage.size(), '\0');
status_t readStatus =
mClientTransport->interruptableReadFully(mFdTrigger.get(), readMessage.data(),
- readMessage.size());
+ readMessage.size(), {});
if (readStatus != OK) {
return AssertionFailure() << statusToString(readStatus);
}
@@ -1639,23 +1637,58 @@
// Make A trust B.
template <typename A, typename B>
- status_t trust(A* a, B* b) {
- auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ static status_t trust(RpcSecurity rpcSecurity,
+ std::optional<RpcCertificateFormat> certificateFormat, const A& a,
+ const B& b) {
if (rpcSecurity != RpcSecurity::TLS) return OK;
- auto bCert = b->getCtx()->getCertificate(certificateFormat);
- return a->getCertVerifier()->addTrustedPeerCertificate(certificateFormat, bCert);
+ LOG_ALWAYS_FATAL_IF(!certificateFormat.has_value());
+ auto bCert = b->getCtx()->getCertificate(*certificateFormat);
+ return a->getCertVerifier()->addTrustedPeerCertificate(*certificateFormat, bCert);
}
static constexpr const char* kMessage = "hello";
- std::vector<std::unique_ptr<Server>> mServers;
+};
+
+class RpcTransportTest : public testing::TestWithParam<RpcTransportTestUtils::Param> {
+public:
+ using Server = RpcTransportTestUtils::Server;
+ using Client = RpcTransportTestUtils::Client;
+ static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ auto [socketType, rpcSecurity, certificateFormat] = info.param;
+ auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString();
+ if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat);
+ return ret;
+ }
+ static std::vector<ParamType> getRpcTranportTestParams() {
+ std::vector<ParamType> ret;
+ for (auto socketType : testSocketTypes(false /* hasPreconnected */)) {
+ for (auto rpcSecurity : RpcSecurityValues()) {
+ switch (rpcSecurity) {
+ case RpcSecurity::RAW: {
+ ret.emplace_back(socketType, rpcSecurity, std::nullopt);
+ } break;
+ case RpcSecurity::TLS: {
+ ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM);
+ ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER);
+ } break;
+ }
+ }
+ }
+ return ret;
+ }
+ template <typename A, typename B>
+ status_t trust(const A& a, const B& b) {
+ auto [socketType, rpcSecurity, certificateFormat] = GetParam();
+ return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b);
+ }
};
TEST_P(RpcTransportTest, GoodCertificate) {
- auto server = mServers.emplace_back(std::make_unique<Server>()).get();
- ASSERT_TRUE(server->setUp());
+ auto server = std::make_unique<Server>();
+ ASSERT_TRUE(server->setUp(GetParam()));
Client client(server->getConnectToServerFn());
- ASSERT_TRUE(client.setUp());
+ ASSERT_TRUE(client.setUp(GetParam()));
ASSERT_EQ(OK, trust(&client, server));
ASSERT_EQ(OK, trust(server, &client));
@@ -1665,13 +1698,13 @@
}
TEST_P(RpcTransportTest, MultipleClients) {
- auto server = mServers.emplace_back(std::make_unique<Server>()).get();
- ASSERT_TRUE(server->setUp());
+ auto server = std::make_unique<Server>();
+ ASSERT_TRUE(server->setUp(GetParam()));
std::vector<Client> clients;
for (int i = 0; i < 2; i++) {
auto& client = clients.emplace_back(server->getConnectToServerFn());
- ASSERT_TRUE(client.setUp());
+ ASSERT_TRUE(client.setUp(GetParam()));
ASSERT_EQ(OK, trust(&client, server));
ASSERT_EQ(OK, trust(server, &client));
}
@@ -1683,11 +1716,11 @@
TEST_P(RpcTransportTest, UntrustedServer) {
auto [socketType, rpcSecurity, certificateFormat] = GetParam();
- auto untrustedServer = mServers.emplace_back(std::make_unique<Server>()).get();
- ASSERT_TRUE(untrustedServer->setUp());
+ auto untrustedServer = std::make_unique<Server>();
+ ASSERT_TRUE(untrustedServer->setUp(GetParam()));
Client client(untrustedServer->getConnectToServerFn());
- ASSERT_TRUE(client.setUp());
+ ASSERT_TRUE(client.setUp(GetParam()));
ASSERT_EQ(OK, trust(untrustedServer, &client));
@@ -1700,14 +1733,14 @@
}
TEST_P(RpcTransportTest, MaliciousServer) {
auto [socketType, rpcSecurity, certificateFormat] = GetParam();
- auto validServer = mServers.emplace_back(std::make_unique<Server>()).get();
- ASSERT_TRUE(validServer->setUp());
+ auto validServer = std::make_unique<Server>();
+ ASSERT_TRUE(validServer->setUp(GetParam()));
- auto maliciousServer = mServers.emplace_back(std::make_unique<Server>()).get();
- ASSERT_TRUE(maliciousServer->setUp());
+ auto maliciousServer = std::make_unique<Server>();
+ ASSERT_TRUE(maliciousServer->setUp(GetParam()));
Client client(maliciousServer->getConnectToServerFn());
- ASSERT_TRUE(client.setUp());
+ ASSERT_TRUE(client.setUp(GetParam()));
ASSERT_EQ(OK, trust(&client, validServer));
ASSERT_EQ(OK, trust(validServer, &client));
@@ -1723,11 +1756,11 @@
TEST_P(RpcTransportTest, UntrustedClient) {
auto [socketType, rpcSecurity, certificateFormat] = GetParam();
- auto server = mServers.emplace_back(std::make_unique<Server>()).get();
- ASSERT_TRUE(server->setUp());
+ auto server = std::make_unique<Server>();
+ ASSERT_TRUE(server->setUp(GetParam()));
Client client(server->getConnectToServerFn());
- ASSERT_TRUE(client.setUp());
+ ASSERT_TRUE(client.setUp(GetParam()));
ASSERT_EQ(OK, trust(&client, server));
@@ -1742,13 +1775,13 @@
TEST_P(RpcTransportTest, MaliciousClient) {
auto [socketType, rpcSecurity, certificateFormat] = GetParam();
- auto server = mServers.emplace_back(std::make_unique<Server>()).get();
- ASSERT_TRUE(server->setUp());
+ auto server = std::make_unique<Server>();
+ ASSERT_TRUE(server->setUp(GetParam()));
Client validClient(server->getConnectToServerFn());
- ASSERT_TRUE(validClient.setUp());
+ ASSERT_TRUE(validClient.setUp(GetParam()));
Client maliciousClient(server->getConnectToServerFn());
- ASSERT_TRUE(maliciousClient.setUp());
+ ASSERT_TRUE(maliciousClient.setUp(GetParam()));
ASSERT_EQ(OK, trust(&validClient, server));
ASSERT_EQ(OK, trust(&maliciousClient, server));
@@ -1766,9 +1799,9 @@
std::condition_variable writeCv;
bool shouldContinueWriting = false;
auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) {
- std::string message(kMessage);
- auto status =
- serverTransport->interruptableWriteFully(fdTrigger, message.data(), message.size());
+ std::string message(RpcTransportTestUtils::kMessage);
+ auto status = serverTransport->interruptableWriteFully(fdTrigger, message.data(),
+ message.size(), {});
if (status != OK) return AssertionFailure() << statusToString(status);
{
@@ -1778,20 +1811,20 @@
}
}
- status = serverTransport->interruptableWriteFully(fdTrigger, msg2.data(), msg2.size());
- if (status != -ECANCELED)
+ status = serverTransport->interruptableWriteFully(fdTrigger, msg2.data(), msg2.size(), {});
+ if (status != DEAD_OBJECT)
return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully "
- "should return -ECANCELLED, but it is "
+ "should return DEAD_OBJECT, but it is "
<< statusToString(status);
return AssertionSuccess();
};
- auto server = mServers.emplace_back(std::make_unique<Server>()).get();
- ASSERT_TRUE(server->setUp());
+ auto server = std::make_unique<Server>();
+ ASSERT_TRUE(server->setUp(GetParam()));
// Set up client
Client client(server->getConnectToServerFn());
- ASSERT_TRUE(client.setUp());
+ ASSERT_TRUE(client.setUp(GetParam()));
// Exchange keys
ASSERT_EQ(OK, trust(&client, server));
@@ -1799,41 +1832,85 @@
server->setPostConnect(serverPostConnect);
- // Start server
server->start();
// connect() to server and do handshake
ASSERT_TRUE(client.setUpTransport());
- // read the first message. This confirms that server has finished handshake and start handling
- // client fd. Server thread should pause at waitForWriteBarrier.
- ASSERT_TRUE(client.readMessage(kMessage));
+ // read the first message. This ensures that server has finished handshake and start handling
+ // client fd. Server thread should pause at writeCv.wait_for().
+ ASSERT_TRUE(client.readMessage(RpcTransportTestUtils::kMessage));
// Trigger server shutdown after server starts handling client FD. This ensures that the second
// write is on an FdTrigger that has been shut down.
server->shutdown();
// Continues server thread to write the second message.
{
- std::unique_lock<std::mutex> lock(writeMutex);
+ std::lock_guard<std::mutex> lock(writeMutex);
shouldContinueWriting = true;
- lock.unlock();
- writeCv.notify_all();
}
+ writeCv.notify_all();
// After this line, server thread unblocks and attempts to write the second message, but
- // shutdown is triggered, so write should failed with -ECANCELLED. See |serverPostConnect|.
+ // shutdown is triggered, so write should failed with DEAD_OBJECT. See |serverPostConnect|.
// On the client side, second read fails with DEAD_OBJECT
ASSERT_FALSE(client.readMessage(msg2));
}
-std::vector<RpcCertificateFormat> testRpcCertificateFormats() {
- return {
- RpcCertificateFormat::PEM,
- RpcCertificateFormat::DER,
+INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest,
+ ::testing::ValuesIn(RpcTransportTest::getRpcTranportTestParams()),
+ RpcTransportTest::PrintParamInfo);
+
+class RpcTransportTlsKeyTest
+ : public testing::TestWithParam<std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat>> {
+public:
+ template <typename A, typename B>
+ status_t trust(const A& a, const B& b) {
+ auto [socketType, certificateFormat, keyFormat] = GetParam();
+ return RpcTransportTestUtils::trust(RpcSecurity::TLS, certificateFormat, a, b);
+ }
+ static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ auto [socketType, certificateFormat, keyFormat] = info.param;
+ auto ret = PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) +
+ "_key_" + PrintToString(keyFormat);
+ return ret;
};
+};
+
+TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) {
+ auto [socketType, certificateFormat, keyFormat] = GetParam();
+
+ std::vector<uint8_t> pkeyData, certData;
+ {
+ auto pkey = makeKeyPairForSelfSignedCert();
+ ASSERT_NE(nullptr, pkey);
+ auto cert = makeSelfSignedCert(pkey.get(), kCertValidSeconds);
+ ASSERT_NE(nullptr, cert);
+ pkeyData = serializeUnencryptedPrivatekey(pkey.get(), keyFormat);
+ certData = serializeCertificate(cert.get(), certificateFormat);
+ }
+
+ auto desPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat);
+ auto desCert = deserializeCertificate(certData, certificateFormat);
+ auto auth = std::make_unique<RpcAuthPreSigned>(std::move(desPkey), std::move(desCert));
+ auto utilsParam =
+ std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat));
+
+ auto server = std::make_unique<RpcTransportTestUtils::Server>();
+ ASSERT_TRUE(server->setUp(utilsParam, std::move(auth)));
+
+ RpcTransportTestUtils::Client client(server->getConnectToServerFn());
+ ASSERT_TRUE(client.setUp(utilsParam));
+
+ ASSERT_EQ(OK, trust(&client, server));
+ ASSERT_EQ(OK, trust(server, &client));
+
+ server->start();
+ client.run();
}
-INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest,
- ::testing::Combine(::testing::ValuesIn(testSocketTypes(false)),
- ::testing::ValuesIn(RpcSecurityValues()),
- ::testing::ValuesIn(testRpcCertificateFormats())),
- RpcTransportTest::PrintParamInfo);
+INSTANTIATE_TEST_CASE_P(
+ BinderRpc, RpcTransportTlsKeyTest,
+ testing::Combine(testing::ValuesIn(testSocketTypes(false /* hasPreconnected*/)),
+ testing::Values(RpcCertificateFormat::PEM, RpcCertificateFormat::DER),
+ testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER)),
+ RpcTransportTlsKeyTest::PrintParamInfo);
} // namespace android
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 6c3b3d9..2398e1e 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -197,6 +197,14 @@
}
}
+TEST(BinderStability, ConnectionInfoRequiresManifestEntries) {
+ sp<IServiceManager> sm = android::defaultServiceManager();
+ sp<IBinder> systemBinder = BadStableBinder::system();
+ EXPECT_EQ(OK, sm->addService(String16("no.connection.foo"), systemBinder));
+ std::optional<android::IServiceManager::ConnectionInfo> connectionInfo;
+ connectionInfo = sm->getConnectionInfo(String16("no.connection.foo"));
+ EXPECT_EQ(connectionInfo, std::nullopt);
+}
TEST(BinderStability, CantCallVendorBinderInSystemContext) {
sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer);
auto server = interface_cast<IBinderStabilityTest>(serverBinder);
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index fb24836..4330e3e 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -34,7 +34,7 @@
auto result = execute({"echo", "foo"}, nullptr);
ASSERT_THAT(result, Ok());
EXPECT_THAT(result->exitCode, Optional(EX_OK));
- EXPECT_EQ(result->stdout, "foo\n");
+ EXPECT_EQ(result->stdoutStr, "foo\n");
}
TEST(UtilsHost, ExecuteLongRunning) {
@@ -44,7 +44,7 @@
std::vector<std::string> args{"sh", "-c",
"sleep 0.5 && echo -n f && sleep 0.5 && echo oo && sleep 1"};
auto result = execute(std::move(args), [](const CommandResult& commandResult) {
- return android::base::EndsWith(commandResult.stdout, "\n");
+ return android::base::EndsWith(commandResult.stdoutStr, "\n");
});
auto elapsed = std::chrono::system_clock::now() - now;
auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
@@ -53,7 +53,7 @@
ASSERT_THAT(result, Ok());
EXPECT_EQ(std::nullopt, result->exitCode);
- EXPECT_EQ(result->stdout, "foo\n");
+ EXPECT_EQ(result->stdoutStr, "foo\n");
}
// ~CommandResult() called, child process is killed.
@@ -70,7 +70,7 @@
std::vector<std::string> args{"sh", "-c",
"sleep 2 && echo -n f && sleep 2 && echo oo && sleep 2"};
auto result = execute(std::move(args), [](const CommandResult& commandResult) {
- return android::base::EndsWith(commandResult.stdout, "\n");
+ return android::base::EndsWith(commandResult.stdoutStr, "\n");
});
auto elapsed = std::chrono::system_clock::now() - now;
auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
@@ -79,7 +79,7 @@
ASSERT_THAT(result, Ok());
EXPECT_EQ(std::nullopt, result->exitCode);
- EXPECT_EQ(result->stdout, "foo\n");
+ EXPECT_EQ(result->stdoutStr, "foo\n");
}
// ~CommandResult() called, child process is killed.
diff --git a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h
new file mode 100644
index 0000000..cbf11bf
--- /dev/null
+++ b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <mutex>
+
+#include <binder/RpcAuth.h>
+#include <binder/RpcCertificateFormat.h>
+#include <binder/RpcCertificateVerifier.h>
+#include <binder/RpcTransport.h>
+#include <openssl/ssl.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+constexpr const uint32_t kCertValidSeconds = 30 * (60 * 60 * 24); // 30 days
+bssl::UniquePtr<EVP_PKEY> makeKeyPairForSelfSignedCert();
+bssl::UniquePtr<X509> makeSelfSignedCert(EVP_PKEY* pKey, uint32_t validSeconds);
+
+// An implementation of RpcAuth that generates a key pair and a self-signed
+// certificate every time configure() is called.
+class RpcAuthSelfSigned : public RpcAuth {
+public:
+ RpcAuthSelfSigned(uint32_t validSeconds = kCertValidSeconds) : mValidSeconds(validSeconds) {}
+ status_t configure(SSL_CTX* ctx) override;
+
+private:
+ const uint32_t mValidSeconds;
+};
+
+class RpcAuthPreSigned : public RpcAuth {
+public:
+ RpcAuthPreSigned(bssl::UniquePtr<EVP_PKEY> pkey, bssl::UniquePtr<X509> cert)
+ : mPkey(std::move(pkey)), mCert(std::move(cert)) {}
+ status_t configure(SSL_CTX* ctx) override;
+
+private:
+ bssl::UniquePtr<EVP_PKEY> mPkey;
+ bssl::UniquePtr<X509> mCert;
+};
+
+// A simple certificate verifier for testing.
+// Keep a list of leaf certificates as trusted. No certificate chain support.
+//
+// All APIs are thread-safe. However, if verify() and addTrustedPeerCertificate() are called
+// simultaneously in different threads, it is not deterministic whether verify() will use the
+// certificate being added.
+class RpcCertificateVerifierSimple : public RpcCertificateVerifier {
+public:
+ status_t verify(const SSL*, uint8_t*) override;
+
+ // Add a trusted peer certificate. Peers presenting this certificate are accepted.
+ //
+ // Caller must ensure that RpcTransportCtx::newTransport() are called after all trusted peer
+ // certificates are added. Otherwise, RpcTransport-s created before may not trust peer
+ // certificates added later.
+ [[nodiscard]] status_t addTrustedPeerCertificate(RpcCertificateFormat format,
+ const std::vector<uint8_t>& cert);
+
+private:
+ std::mutex mMutex; // for below
+ std::vector<bssl::UniquePtr<X509>> mTrustedPeerCertificates;
+};
+
+// A RpcCertificateVerifier that does not verify anything.
+class RpcCertificateVerifierNoOp : public RpcCertificateVerifier {
+public:
+ status_t verify(const SSL*, uint8_t*) override { return OK; }
+};
+
+} // namespace android
diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp
index 9323bd5..be55eba 100644
--- a/libs/binder/tests/rpc_fuzzer/Android.bp
+++ b/libs/binder/tests/rpc_fuzzer/Android.bp
@@ -14,6 +14,7 @@
fuzz_config: {
cc: ["smoreland@google.com"],
},
+ dictionary: "binder_rpc_fuzzer.dict",
srcs: [
"main.cpp",
diff --git a/libs/binder/tests/rpc_fuzzer/binder_rpc_fuzzer.dict b/libs/binder/tests/rpc_fuzzer/binder_rpc_fuzzer.dict
new file mode 100644
index 0000000..b110a02
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/binder_rpc_fuzzer.dict
@@ -0,0 +1,2 @@
+# connection okay header
+"cci"
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index 230f5c7..c848798 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -87,8 +87,7 @@
size_t idx = provider.ConsumeIntegralInRange<size_t>(0, connections.size() - 1);
if (provider.ConsumeBool()) {
- std::vector<uint8_t> writeData = provider.ConsumeBytes<uint8_t>(
- provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+ std::string writeData = provider.ConsumeRandomLengthString();
ssize_t size = TEMP_FAILURE_RETRY(send(connections.at(idx).get(), writeData.data(),
writeData.size(), MSG_NOSIGNAL));
CHECK(errno == EPIPE || size == writeData.size())
@@ -101,7 +100,7 @@
if (hangupBeforeShutdown) {
connections.clear();
- while (!server->listSessions().empty() && server->numUninitializedSessions()) {
+ while (!server->listSessions().empty() || server->numUninitializedSessions()) {
// wait for all threads to finish processing existing information
usleep(1);
}
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index 95582bf..20c5569 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -54,10 +54,9 @@
for (size_t tries = 0; tries < 5; tries++) {
usleep(10000);
status = session->setupUnixDomainClient(addr.c_str());
- if (status == OK) goto success;
+ if (status == OK) break;
}
- LOG(FATAL) << "Unable to connect";
-success:
+ CHECK_EQ(status, OK) << "Unable to connect";
sp<BpBinder> bpBinder = session->getRootObject()->remoteBinder();
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 761e45c..9f0754b 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -78,4 +78,10 @@
return std::nullopt;
}
+std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo(
+ const String16& name) {
+ (void)name;
+ return std::nullopt;
+}
+
} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
index e26c21b..b1496ba 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -51,6 +51,8 @@
std::optional<String16> updatableViaApex(const String16& name) override;
+ std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override;
+
private:
std::map<String16, sp<IBinder>> mNameToService;
};
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
index 2e81022..ee650e5 100644
--- a/libs/ftl/small_map_test.cpp
+++ b/libs/ftl/small_map_test.cpp
@@ -345,4 +345,41 @@
}
}
+TEST(SmallMap, Clear) {
+ SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
+
+ map.clear();
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+
+ map = ftl::init::map(1, '1')(2, '2')(3, '3');
+ map.try_emplace(4, '4');
+
+ map.clear();
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_TRUE(map.dynamic());
+}
+
+TEST(SmallMap, KeyEqual) {
+ struct KeyEqual {
+ bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; }
+ };
+
+ SmallMap<int, char, 1, KeyEqual> map;
+
+ EXPECT_TRUE(map.try_emplace(3, '3').second);
+ EXPECT_FALSE(map.try_emplace(13, '3').second);
+
+ EXPECT_TRUE(map.try_emplace(22, '2').second);
+ EXPECT_TRUE(map.contains(42));
+
+ EXPECT_TRUE(map.try_emplace(111, '1').second);
+ EXPECT_EQ(map.get(321), '1');
+
+ map.erase(123);
+ EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2')));
+}
+
} // namespace android::test
diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp
index 3a03e69..4237496 100644
--- a/libs/ftl/small_vector_test.cpp
+++ b/libs/ftl/small_vector_test.cpp
@@ -460,4 +460,34 @@
EXPECT_EQ(0, dead);
}
+TEST(SmallVector, Clear) {
+ int live = 0;
+ int dead = 0;
+
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_FALSE(counts.dynamic());
+
+ EXPECT_EQ(2, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_TRUE(counts.dynamic());
+
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(2, dead);
+}
+
} // namespace android::test
diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp
index cbe8dff..2de3ad2 100644
--- a/libs/ftl/static_vector_test.cpp
+++ b/libs/ftl/static_vector_test.cpp
@@ -396,4 +396,19 @@
EXPECT_EQ(0, dead);
}
+TEST(StaticVector, Clear) {
+ int live = 0;
+ int dead = 0;
+
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_EQ(2, live);
+ EXPECT_EQ(0, dead);
+}
+
} // namespace android::test
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index cda9e19..6afd172 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -33,7 +33,7 @@
target: {
darwin: {
enabled: false,
- }
+ },
},
vendor_available: true,
@@ -48,18 +48,18 @@
min_sdk_version: "29",
srcs: [
- "Gralloc4.cpp"
+ "Gralloc4.cpp",
],
shared_libs: [
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
"liblog",
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 326da3a..8c359c7 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -55,6 +55,7 @@
filegroup {
name: "guiconstants_aidl",
srcs: [
+ "android/gui/DropInputMode.aidl",
"android/**/TouchOcclusionMode.aidl",
],
}
@@ -65,11 +66,13 @@
host_supported: true,
srcs: [
":guiconstants_aidl",
+ "android/gui/DisplayInfo.aidl",
"android/gui/FocusRequest.aidl",
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
"android/gui/WindowInfo.aidl",
+ "DisplayInfo.cpp",
"WindowInfo.cpp",
],
@@ -90,7 +93,7 @@
],
aidl: {
- export_aidl_headers: true
+ export_aidl_headers: true,
},
include_dirs: [
@@ -135,8 +138,8 @@
],
aidl: {
- export_aidl_headers: true
- }
+ export_aidl_headers: true,
+ },
}
cc_library_shared {
diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp
new file mode 100644
index 0000000..52d9540
--- /dev/null
+++ b/libs/gui/DisplayInfo.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DisplayInfo"
+
+#include <binder/Parcel.h>
+#include <gui/DisplayInfo.h>
+#include <private/gui/ParcelUtils.h>
+
+#include <log/log.h>
+
+namespace android::gui {
+
+// --- DisplayInfo ---
+
+status_t DisplayInfo::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ SAFE_PARCEL(parcel->readInt32, &displayId);
+ SAFE_PARCEL(parcel->readInt32, &logicalWidth);
+ SAFE_PARCEL(parcel->readInt32, &logicalHeight);
+ SAFE_PARCEL(parcel->readFloat, &dsdx);
+ SAFE_PARCEL(parcel->readFloat, &dtdx);
+ SAFE_PARCEL(parcel->readFloat, &tx);
+ SAFE_PARCEL(parcel->readFloat, &dtdy);
+ SAFE_PARCEL(parcel->readFloat, &dsdy);
+ SAFE_PARCEL(parcel->readFloat, &ty);
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+
+ return OK;
+}
+
+status_t DisplayInfo::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeInt32, displayId);
+ SAFE_PARCEL(parcel->writeInt32, logicalWidth);
+ SAFE_PARCEL(parcel->writeInt32, logicalHeight);
+ SAFE_PARCEL(parcel->writeFloat, transform.dsdx());
+ SAFE_PARCEL(parcel->writeFloat, transform.dtdx());
+ SAFE_PARCEL(parcel->writeFloat, transform.tx());
+ SAFE_PARCEL(parcel->writeFloat, transform.dtdy());
+ SAFE_PARCEL(parcel->writeFloat, transform.dsdy());
+ SAFE_PARCEL(parcel->writeFloat, transform.ty());
+
+ return OK;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 3bf6306..3c8289f 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -297,6 +297,20 @@
return {};
}
+ status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId* displayId) const override {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(remote()->transact, BnSurfaceComposer::GET_PRIMARY_PHYSICAL_DISPLAY_ID, data,
+ &reply);
+ uint64_t rawId;
+ SAFE_PARCEL(reply.readUint64, &rawId);
+ if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+ *displayId = *id;
+ return NO_ERROR;
+ }
+ return NAME_NOT_FOUND;
+ }
+
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1738,6 +1752,16 @@
[](PhysicalDisplayId id) { return id.value; });
return reply->writeUint64Vector(rawIds);
}
+ case GET_PRIMARY_PHYSICAL_DISPLAY_ID: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ PhysicalDisplayId id;
+ status_t result = getPrimaryPhysicalDisplayId(&id);
+ if (result != NO_ERROR) {
+ ALOGE("getPrimaryPhysicalDisplayId: Failed to get id");
+ return result;
+ }
+ return reply->writeUint64(id.value);
+ }
case ADD_REGION_SAMPLING_LISTENER: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
Rect samplingArea;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 1fd9d13..a419a63 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -69,7 +69,8 @@
isTrustedOverlay(false),
bufferCrop(Rect::INVALID_RECT),
destinationFrame(Rect::INVALID_RECT),
- releaseBufferListener(nullptr) {
+ releaseBufferListener(nullptr),
+ dropInputMode(gui::DropInputMode::NONE) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
hdrMetadata.validTypes = 0;
@@ -174,6 +175,7 @@
SAFE_PARCEL(output.writeBool, isTrustedOverlay);
SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint);
+ SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode));
return NO_ERROR;
}
@@ -304,6 +306,10 @@
SAFE_PARCEL(input.readBool, &isTrustedOverlay);
SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint);
+
+ uint32_t mode;
+ SAFE_PARCEL(input.readUint32, &mode);
+ dropInputMode = static_cast<gui::DropInputMode>(mode);
return NO_ERROR;
}
@@ -558,6 +564,10 @@
if (other.what & eProducerDisconnect) {
what |= eProducerDisconnect;
}
+ if (other.what & eDropInputModeChanged) {
+ what |= eDropInputModeChanged;
+ dropInputMode = other.dropInputMode;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1bca6f9..bbd3cca 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -937,6 +937,10 @@
return ComposerService::getComposerService()->getPhysicalDisplayIds();
}
+status_t SurfaceComposerClient::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) {
+ return ComposerService::getComposerService()->getPrimaryPhysicalDisplayId(id);
+}
+
std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() {
return ComposerService::getComposerService()->getInternalDisplayId();
}
@@ -1739,6 +1743,21 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropInputMode(
+ const sp<SurfaceControl>& sc, gui::DropInputMode mode) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eDropInputModeChanged;
+ s->dropInputMode = mode;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index b2ef7aa..5f3a726 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -54,12 +54,11 @@
info.frameLeft == frameLeft && info.frameTop == frameTop &&
info.frameRight == frameRight && info.frameBottom == frameBottom &&
info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
- info.transform == transform && info.displayOrientation == displayOrientation &&
- info.displayWidth == displayWidth && info.displayHeight == displayHeight &&
- info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
- info.trustedOverlay == trustedOverlay && info.focusable == focusable &&
- info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper &&
- info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+ info.visible == visible && info.trustedOverlay == trustedOverlay &&
+ info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+ info.hasWallpaper == hasWallpaper && info.paused == paused &&
+ info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
info.packageName == packageName && info.inputFeatures == inputFeatures &&
info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
@@ -97,9 +96,6 @@
parcel->writeFloat(transform.dtdy()) ?:
parcel->writeFloat(transform.dsdy()) ?:
parcel->writeFloat(transform.ty()) ?:
- parcel->writeUint32(displayOrientation) ?:
- parcel->writeInt32(displayWidth) ?:
- parcel->writeInt32(displayHeight) ?:
parcel->writeBool(visible) ?:
parcel->writeBool(focusable) ?:
parcel->writeBool(hasWallpaper) ?:
@@ -155,9 +151,6 @@
parcel->readFloat(&dtdy) ?:
parcel->readFloat(&dsdy) ?:
parcel->readFloat(&ty) ?:
- parcel->readUint32(&displayOrientation) ?:
- parcel->readInt32(&displayWidth) ?:
- parcel->readInt32(&displayHeight) ?:
parcel->readBool(&visible) ?:
parcel->readBool(&focusable) ?:
parcel->readBool(&hasWallpaper) ?:
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index c00a438..c32b9ab 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -19,6 +19,7 @@
namespace android {
+using gui::DisplayInfo;
using gui::IWindowInfosReportedListener;
using gui::WindowInfo;
using gui::WindowInfosListener;
@@ -65,7 +66,7 @@
}
binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
- const std::vector<WindowInfo>& windowInfos,
+ const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
std::unordered_set<sp<WindowInfosListener>, ISurfaceComposer::SpHash<WindowInfosListener>>
windowInfosListeners;
@@ -78,7 +79,7 @@
}
for (auto listener : windowInfosListeners) {
- listener->onWindowInfosChanged(windowInfos);
+ listener->onWindowInfosChanged(windowInfos, displayInfos);
}
if (windowInfosReportedListener) {
diff --git a/libs/gui/android/gui/DisplayInfo.aidl b/libs/gui/android/gui/DisplayInfo.aidl
new file mode 100644
index 0000000..30c0885
--- /dev/null
+++ b/libs/gui/android/gui/DisplayInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+parcelable DisplayInfo cpp_header "gui/DisplayInfo.h";
diff --git a/libs/gui/android/gui/DropInputMode.aidl b/libs/gui/android/gui/DropInputMode.aidl
new file mode 100644
index 0000000..2b31744
--- /dev/null
+++ b/libs/gui/android/gui/DropInputMode.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+
+/**
+ * Input event drop modes: Input event drop options for windows and its children.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum DropInputMode {
+ /**
+ * Default mode, input events are sent to the target as usual.
+ */
+ NONE,
+
+ /**
+ * Window and its children will not receive any input even if it has a valid input channel.
+ * Touches and keys will be dropped. If a window is focused, it will remain focused but will
+ * not receive any keys. If the window has a touchable region and is the target of an input
+ * event, the event will be dropped and will not go to the window behind. ref: b/197296414
+ */
+ ALL,
+
+ /**
+ * Similar to DROP but input events are only dropped if the window is considered to be
+ * obscured. ref: b/197364677
+ */
+ OBSCURED
+}
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
index d4553ca..a5b2762 100644
--- a/libs/gui/android/gui/IWindowInfosListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -16,11 +16,12 @@
package android.gui;
+import android.gui.DisplayInfo;
import android.gui.IWindowInfosReportedListener;
import android.gui.WindowInfo;
/** @hide */
oneway interface IWindowInfosListener
{
- void onWindowInfosChanged(in WindowInfo[] windowInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+ void onWindowInfosChanged(in WindowInfo[] windowInfos, in DisplayInfo[] displayInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
}
diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h
new file mode 100644
index 0000000..74f33a2
--- /dev/null
+++ b/libs/gui/include/gui/DisplayInfo.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <gui/constants.h>
+#include <ui/Transform.h>
+
+namespace android::gui {
+
+/*
+ * Describes information about a display that can have windows in it.
+ *
+ * This should only be used by InputFlinger to support raw coordinates in logical display space.
+ */
+struct DisplayInfo : public Parcelable {
+ int32_t displayId = ADISPLAY_ID_NONE;
+
+ // Logical display dimensions.
+ int32_t logicalWidth = 0;
+ int32_t logicalHeight = 0;
+
+ // The display transform. This takes display coordinates to logical display coordinates.
+ ui::Transform transform;
+
+ status_t writeToParcel(android::Parcel*) const override;
+
+ status_t readFromParcel(const android::Parcel*) override;
+};
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index b7cd082..408497d 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -146,6 +146,8 @@
*/
virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const = 0;
+ virtual status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const = 0;
+
// TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
std::optional<PhysicalDisplayId> getInternalDisplayId() const {
const auto displayIds = getPhysicalDisplayIds();
@@ -630,6 +632,7 @@
REMOVE_TUNNEL_MODE_ENABLED_LISTENER,
ADD_WINDOW_INFOS_LISTENER,
REMOVE_WINDOW_INFOS_LISTENER,
+ GET_PRIMARY_PHYSICAL_DISPLAY_ID,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index f14127c..b27102b 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -26,6 +26,7 @@
#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
+#include <android/gui/DropInputMode.h>
#include <android/gui/FocusRequest.h>
#include <gui/ISurfaceComposer.h>
@@ -118,6 +119,7 @@
eAutoRefreshChanged = 0x1000'00000000,
eStretchChanged = 0x2000'00000000,
eTrustedOverlayChanged = 0x4000'00000000,
+ eDropInputModeChanged = 0x8000'00000000,
};
layer_state_t();
@@ -248,6 +250,9 @@
// releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
// was called with.
sp<IBinder> releaseBufferEndpoint;
+
+ // Force inputflinger to drop all input events for the layer and its children.
+ gui::DropInputMode dropInputMode;
};
struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index baa6878..403ca0a 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -308,6 +308,7 @@
//! Get stable IDs for connected physical displays
static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
+ static status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*);
static std::optional<PhysicalDisplayId> getInternalDisplayId();
//! Get token for a physical display given its stable ID
@@ -562,6 +563,7 @@
Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
Transaction& setDestinationFrame(const sp<SurfaceControl>& sc,
const Rect& destinationFrame);
+ Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 47f6c05..54a372c 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -132,9 +132,11 @@
};
enum class Feature : uint32_t {
- DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
- NO_INPUT_CHANNEL = 0x00000002,
- DISABLE_USER_ACTIVITY = 0x00000004,
+ DISABLE_TOUCH_PAD_GESTURES = 1u << 0,
+ NO_INPUT_CHANNEL = 1u << 1,
+ DISABLE_USER_ACTIVITY = 1u << 2,
+ DROP_INPUT = 1u << 3,
+ DROP_INPUT_IF_OBSCURED = 1u << 4,
};
/* These values are filled in by the WM and passed through SurfaceFlinger
@@ -179,13 +181,6 @@
// Transform applied to individual windows.
ui::Transform transform;
- // Display orientation as ui::Transform::RotationFlags. Used for compatibility raw coordinates.
- uint32_t displayOrientation = ui::Transform::ROT_0;
-
- // Display size in its natural rotation. Used to rotate raw coordinates for compatibility.
- int32_t displayWidth = 0;
- int32_t displayHeight = 0;
-
/*
* This is filled in by the WM relative to the frame and then translated
* to absolute coordinates by SurfaceFlinger once the frame is computed.
diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h
index 8a70b9b..a18a498 100644
--- a/libs/gui/include/gui/WindowInfosListener.h
+++ b/libs/gui/include/gui/WindowInfosListener.h
@@ -16,6 +16,7 @@
#pragma once
+#include <gui/DisplayInfo.h>
#include <gui/WindowInfo.h>
#include <utils/RefBase.h>
@@ -23,6 +24,7 @@
class WindowInfosListener : public virtual RefBase {
public:
- virtual void onWindowInfosChanged(const std::vector<WindowInfo>& /*windowInfos*/) = 0;
+ virtual void onWindowInfosChanged(const std::vector<WindowInfo>&,
+ const std::vector<DisplayInfo>&) = 0;
};
} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 7cb96e0..157a804 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -21,7 +21,6 @@
#include <binder/IBinder.h>
#include <gui/ISurfaceComposer.h>
#include <gui/WindowInfosListener.h>
-#include <utils/Mutex.h>
#include <unordered_set>
namespace android {
@@ -30,7 +29,8 @@
class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
public:
static sp<WindowInfosListenerReporter> getInstance();
- binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos,
+ binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>&,
+ const std::vector<gui::DisplayInfo>&,
const sp<gui::IWindowInfosReportedListener>&) override;
status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 3d26c3d..6dd1073 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -27,6 +27,7 @@
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
+ "DisplayInfo_test.cpp",
"DisplayedContentSampling_test.cpp",
"FillBuffer.cpp",
"GLTest.cpp",
@@ -62,7 +63,7 @@
"libinput",
"libui",
"libutils",
- "libnativewindow"
+ "libnativewindow",
],
header_libs: ["libsurfaceflinger_headers"],
@@ -117,7 +118,7 @@
"libgui",
"libui",
"libutils",
- "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
+ "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
"libpdx_default_transport",
],
@@ -146,5 +147,5 @@
"liblog",
"libui",
"libutils",
- ]
+ ],
}
diff --git a/libs/gui/tests/DisplayInfo_test.cpp b/libs/gui/tests/DisplayInfo_test.cpp
new file mode 100644
index 0000000..df3329c
--- /dev/null
+++ b/libs/gui/tests/DisplayInfo_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 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 <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+
+#include <gui/DisplayInfo.h>
+
+namespace android {
+
+using gui::DisplayInfo;
+
+namespace test {
+
+TEST(DisplayInfo, Parcelling) {
+ DisplayInfo info;
+ info.displayId = 42;
+ info.logicalWidth = 99;
+ info.logicalHeight = 78;
+ info.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
+
+ Parcel p;
+ info.writeToParcel(&p);
+ p.setDataPosition(0);
+
+ DisplayInfo info2;
+ info2.readFromParcel(&p);
+ ASSERT_EQ(info.displayId, info2.displayId);
+ ASSERT_EQ(info.logicalWidth, info2.logicalWidth);
+ ASSERT_EQ(info.logicalHeight, info2.logicalHeight);
+ ASSERT_EQ(info.transform, info2.transform);
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index fc84c1b..16208a7 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -24,6 +24,7 @@
#include <memory>
+#include <android/keycodes.h>
#include <android/native_window.h>
#include <binder/Binder.h>
@@ -120,8 +121,8 @@
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
- InputEvent* consumeEvent() {
- waitForEventAvailable();
+ InputEvent *consumeEvent(int timeoutMs = 3000) {
+ waitForEventAvailable(timeoutMs);
InputEvent *ev;
uint32_t seqId;
@@ -178,6 +179,24 @@
EXPECT_EQ(flags, mev->getFlags() & flags);
}
+ void expectKey(uint32_t keycode) {
+ InputEvent *ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, ev->getType());
+ KeyEvent *keyEvent = static_cast<KeyEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, keyEvent->getAction());
+ EXPECT_EQ(keycode, keyEvent->getKeyCode());
+ EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS);
+
+ ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, ev->getType());
+ keyEvent = static_cast<KeyEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_UP, keyEvent->getAction());
+ EXPECT_EQ(keycode, keyEvent->getKeyCode());
+ EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS);
+ }
+
virtual ~InputSurface() {
mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
}
@@ -201,7 +220,7 @@
t.apply(true);
}
- void requestFocus() {
+ void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
SurfaceComposerClient::Transaction t;
FocusRequest request;
request.token = mInputInfo.token;
@@ -209,18 +228,18 @@
request.focusedToken = nullptr;
request.focusedWindowName = "";
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- request.displayId = 0;
+ request.displayId = displayId;
t.setFocusedWindow(request);
t.apply(true);
}
private:
- void waitForEventAvailable() {
+ void waitForEventAvailable(int timeoutMs) {
struct pollfd fd;
fd.fd = mClientChannel->getFd();
fd.events = POLLIN;
- poll(&fd, 1, 3000);
+ poll(&fd, 1, timeoutMs);
}
void populateInputInfo(int width, int height) {
@@ -236,11 +255,6 @@
mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
- // TODO: Fill in from SF?
- mInputInfo.ownerPid = 11111;
- mInputInfo.ownerUid = 11111;
- mInputInfo.displayId = 0;
-
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
@@ -354,15 +368,33 @@
int32_t mBufferPostDelay;
};
-void injectTap(int x, int y) {
- char *buf1, *buf2;
+void injectTapOnDisplay(int x, int y, int displayId) {
+ char *buf1, *buf2, *bufDisplayId;
asprintf(&buf1, "%d", x);
asprintf(&buf2, "%d", y);
+ asprintf(&bufDisplayId, "%d", displayId);
if (fork() == 0) {
- execlp("input", "input", "tap", buf1, buf2, NULL);
+ execlp("input", "input", "-d", bufDisplayId, "tap", buf1, buf2, NULL);
}
}
+void injectTap(int x, int y) {
+ injectTapOnDisplay(x, y, ADISPLAY_ID_DEFAULT);
+}
+
+void injectKeyOnDisplay(uint32_t keycode, int displayId) {
+ char *buf1, *bufDisplayId;
+ asprintf(&buf1, "%d", keycode);
+ asprintf(&bufDisplayId, "%d", displayId);
+ if (fork() == 0) {
+ execlp("input", "input", "-d", bufDisplayId, "keyevent", buf1, NULL);
+ }
+}
+
+void injectKey(uint32_t keycode) {
+ injectKeyOnDisplay(keycode, ADISPLAY_ID_NONE);
+}
+
TEST_F(InputSurfacesTest, can_receive_input) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
@@ -614,6 +646,9 @@
surface->requestFocus();
surface->assertFocusChange(true);
+
+ injectKey(AKEYCODE_V);
+ surface->expectKey(AKEYCODE_V);
}
TEST_F(InputSurfacesTest, rotate_surface) {
@@ -781,4 +816,209 @@
surface->expectTap(1, 1);
}
+TEST_F(InputSurfacesTest, strict_unobscured_input_unobscured_window) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction(
+ [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+ surface->showAt(100, 100);
+
+ injectTap(101, 101);
+
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+
+ surface->requestFocus();
+ surface->assertFocusChange(true);
+ injectKey(AKEYCODE_V);
+ surface->expectKey(AKEYCODE_V);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
+ t.setMatrix(sc, 2.0, 0, 0, 2.0);
+ });
+ surface->showAt(100, 100);
+
+ injectTap(101, 101);
+
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+
+ surface->requestFocus();
+ surface->assertFocusChange(true);
+ injectKey(AKEYCODE_V);
+ surface->expectKey(AKEYCODE_V);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->mInputInfo.ownerUid = 11111;
+ surface->doTransaction(
+ [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+ surface->showAt(100, 100);
+ std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
+ obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+ obscuringSurface->mInputInfo.ownerUid = 22222;
+ obscuringSurface->showAt(100, 100);
+ injectTap(101, 101);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ surface->requestFocus();
+ surface->assertFocusChange(true);
+ injectKey(AKEYCODE_V);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->mInputInfo.ownerUid = 11111;
+ surface->doTransaction(
+ [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+ surface->showAt(100, 100);
+ std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
+ obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+ obscuringSurface->mInputInfo.ownerUid = 22222;
+ obscuringSurface->showAt(190, 190);
+
+ injectTap(101, 101);
+
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ surface->requestFocus();
+ surface->assertFocusChange(true);
+ injectKey(AKEYCODE_V);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) {
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
+ parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
+
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
+ t.reparent(sc, parentSurface->mSurfaceControl);
+ t.setAlpha(parentSurface->mSurfaceControl, 0.9f);
+ });
+
+ injectTap(101, 101);
+
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ surface->requestFocus();
+ surface->assertFocusChange(true);
+ injectKey(AKEYCODE_V);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) {
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
+ parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
+
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
+ t.reparent(sc, parentSurface->mSurfaceControl);
+ t.setCrop(parentSurface->mSurfaceControl, Rect(10, 10, 100, 100));
+ });
+ surface->showAt(100, 100);
+
+ injectTap(111, 111);
+
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ surface->requestFocus();
+ surface->assertFocusChange(true);
+ injectKey(AKEYCODE_V);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, drop_input_policy) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction(
+ [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::ALL); });
+ surface->showAt(100, 100);
+
+ injectTap(101, 101);
+
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ surface->requestFocus();
+ surface->assertFocusChange(true);
+ injectKey(AKEYCODE_V);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+class MultiDisplayTests : public InputSurfacesTest {
+public:
+ MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
+ void TearDown() {
+ if (mVirtualDisplay) {
+ SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+ }
+ InputSurfacesTest::TearDown();
+ }
+
+ void createDisplay(int32_t width, int32_t height, bool isSecure, ui::LayerStack layerStack) {
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(width, height);
+
+ mVirtualDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), isSecure);
+ SurfaceComposerClient::Transaction t;
+ t.setDisplaySurface(mVirtualDisplay, mProducer);
+ t.setDisplayFlags(mVirtualDisplay, 0x01 /* DisplayDevice::eReceivesInput */);
+ t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+ t.apply(true);
+ }
+
+ sp<IBinder> mVirtualDisplay;
+ sp<IGraphicBufferProducer> mProducer;
+};
+
+TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+ createDisplay(1000, 1000, false /*isSecure*/, layerStack);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+ t.setLayerStack(sc, layerStack);
+ });
+ surface->showAt(100, 100);
+
+ injectTap(101, 101);
+
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+ createDisplay(1000, 1000, true /*isSecure*/, layerStack);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+ t.setLayerStack(sc, layerStack);
+ });
+ surface->showAt(100, 100);
+
+ injectTapOnDisplay(101, 101, layerStack.id);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+
+ surface->expectKey(AKEYCODE_V);
+}
+
} // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index d1ad478..a9f4d09 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -694,6 +694,7 @@
bool /*secure*/) override { return nullptr; }
void destroyDisplay(const sp<IBinder>& /*display */) override {}
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
+ status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override { return NO_ERROR; }
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/,
const Vector<ComposerState>& /*state*/,
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index a4f436c..dcdf76f 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -60,9 +60,6 @@
i.globalScaleFactor = 0.3;
i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
- i.displayOrientation = ui::Transform::ROT_0;
- i.displayWidth = 1000;
- i.displayHeight = 2000;
i.visible = false;
i.focusable = false;
i.hasWallpaper = false;
@@ -100,8 +97,6 @@
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
ASSERT_EQ(i.alpha, i2.alpha);
ASSERT_EQ(i.transform, i2.transform);
- ASSERT_EQ(i.displayWidth, i2.displayWidth);
- ASSERT_EQ(i.displayHeight, i2.displayHeight);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.focusable, i2.focusable);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 1e8ff94..a1542c8 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,10 +20,8 @@
#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
#include <inttypes.h>
-#include <limits.h>
#include <string.h>
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <gui/constants.h>
#include <input/Input.h>
@@ -43,15 +41,6 @@
namespace {
-// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
-// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-bool isPerWindowInputRotationEnabled() {
- static const bool PER_WINDOW_INPUT_ROTATION =
- base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
-
- return PER_WINDOW_INPUT_ROTATION;
-}
-
float transformAngle(const ui::Transform& transform, float angleRadians) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
@@ -76,36 +65,13 @@
return result;
}
-// Rotates the given point to the specified orientation. If the display width and height are
-// provided, the point is rotated in the screen space. Otherwise, the point is rotated about the
-// origin. This helper is used to avoid the extra overhead of creating new Transforms.
-vec2 rotatePoint(uint32_t orientation, float x, float y, int32_t displayWidth = 0,
- int32_t displayHeight = 0) {
- if (orientation == ui::Transform::ROT_0) {
- return {x, y};
- }
-
- vec2 xy(x, y);
- if (orientation == ui::Transform::ROT_90) {
- xy.x = displayHeight - y;
- xy.y = x;
- } else if (orientation == ui::Transform::ROT_180) {
- xy.x = displayWidth - x;
- xy.y = displayHeight - y;
- } else if (orientation == ui::Transform::ROT_270) {
- xy.x = y;
- xy.y = displayWidth - x;
- }
- return xy;
-}
-
-vec2 applyTransformWithoutTranslation(const ui::Transform& transform, float x, float y) {
+vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) {
const vec2 transformedXy = transform.transform(x, y);
const vec2 transformedOrigin = transform.transform(0, 0);
return transformedXy - transformedOrigin;
}
-bool shouldDisregardWindowTranslation(uint32_t source) {
+bool shouldDisregardTranslation(uint32_t source) {
// Pointer events are the only type of events that refer to absolute coordinates on the display,
// so we should apply the entire window transform. For other types of events, we should make
// sure to not apply the window translation/offset.
@@ -431,8 +397,7 @@
int32_t buttonState, MotionClassification classification,
const ui::Transform& transform, float xPrecision, float yPrecision,
float rawXCursorPosition, float rawYCursorPosition,
- uint32_t displayOrientation, int32_t displayWidth,
- int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+ const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
size_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputEvent::initialize(id, deviceId, source, displayId, hmac);
@@ -448,9 +413,7 @@
mYPrecision = yPrecision;
mRawXCursorPosition = rawXCursorPosition;
mRawYCursorPosition = rawYCursorPosition;
- mDisplayOrientation = displayOrientation;
- mDisplayWidth = displayWidth;
- mDisplayHeight = displayHeight;
+ mRawTransform = rawTransform;
mDownTime = downTime;
mPointerProperties.clear();
mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -474,9 +437,7 @@
mYPrecision = other->mYPrecision;
mRawXCursorPosition = other->mRawXCursorPosition;
mRawYCursorPosition = other->mRawYCursorPosition;
- mDisplayOrientation = other->mDisplayOrientation;
- mDisplayWidth = other->mDisplayWidth;
- mDisplayHeight = other->mDisplayHeight;
+ mRawTransform = other->mRawTransform;
mDownTime = other->mDownTime;
mPointerProperties = other->mPointerProperties;
@@ -539,23 +500,20 @@
size_t historicalIndex) const {
const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
- if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis);
-
if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- // For compatibility, convert raw coordinates into "oriented screen space". Once app
- // developers are educated about getRaw, we can consider removing this.
- const vec2 xy = shouldDisregardWindowTranslation(mSource)
- ? rotatePoint(mDisplayOrientation, coords->getX(), coords->getY())
- : rotatePoint(mDisplayOrientation, coords->getX(), coords->getY(), mDisplayWidth,
- mDisplayHeight);
+ // For compatibility, convert raw coordinates into logical display space.
+ const vec2 xy = shouldDisregardTranslation(mSource)
+ ? transformWithoutTranslation(mRawTransform, coords->getX(), coords->getY())
+ : mRawTransform.transform(coords->getX(), coords->getY());
static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
return xy[axis];
}
if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
- // For compatibility, since we convert raw coordinates into "oriented screen space", we
+ // For compatibility, since we report raw coordinates in logical display space, we
// need to convert the relative axes into the same orientation for consistency.
- const vec2 relativeXy = rotatePoint(mDisplayOrientation,
+ const vec2 relativeXy =
+ transformWithoutTranslation(mRawTransform,
coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
@@ -569,8 +527,8 @@
const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- const vec2 xy = shouldDisregardWindowTranslation(mSource)
- ? applyTransformWithoutTranslation(mTransform, coords->getX(), coords->getY())
+ const vec2 xy = shouldDisregardTranslation(mSource)
+ ? transformWithoutTranslation(mTransform, coords->getX(), coords->getY())
: mTransform.transform(coords->getXYValue());
static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
return xy[axis];
@@ -578,11 +536,9 @@
if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
const vec2 relativeXy =
- applyTransformWithoutTranslation(mTransform,
- coords->getAxisValue(
- AMOTION_EVENT_AXIS_RELATIVE_X),
- coords->getAxisValue(
- AMOTION_EVENT_AXIS_RELATIVE_Y));
+ transformWithoutTranslation(mTransform,
+ coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
}
@@ -607,6 +563,8 @@
void MotionEvent::scale(float globalScaleFactor) {
mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
+ mRawTransform.set(mRawTransform.tx() * globalScaleFactor,
+ mRawTransform.ty() * globalScaleFactor);
mXPrecision *= globalScaleFactor;
mYPrecision *= globalScaleFactor;
@@ -708,9 +666,11 @@
mYPrecision = parcel->readFloat();
mRawXCursorPosition = parcel->readFloat();
mRawYCursorPosition = parcel->readFloat();
- mDisplayOrientation = parcel->readUint32();
- mDisplayWidth = parcel->readInt32();
- mDisplayHeight = parcel->readInt32();
+
+ result = android::readFromParcel(mRawTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
mDownTime = parcel->readInt64();
mPointerProperties.clear();
@@ -770,9 +730,11 @@
parcel->writeFloat(mYPrecision);
parcel->writeFloat(mRawXCursorPosition);
parcel->writeFloat(mRawYCursorPosition);
- parcel->writeUint32(mDisplayOrientation);
- parcel->writeInt32(mDisplayWidth);
- parcel->writeInt32(mDisplayHeight);
+
+ result = android::writeToParcel(mRawTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
parcel->writeInt64(mDownTime);
for (size_t i = 0; i < pointerCount; i++) {
@@ -834,9 +796,9 @@
case AMOTION_EVENT_ACTION_OUTSIDE:
return "OUTSIDE";
case AMOTION_EVENT_ACTION_POINTER_DOWN:
- return "POINTER_DOWN";
+ return StringPrintf("POINTER_DOWN(%" PRId32 ")", MotionEvent::getActionIndex(action));
case AMOTION_EVENT_ACTION_POINTER_UP:
- return "POINTER_UP";
+ return StringPrintf("POINTER_UP(%" PRId32 ")", MotionEvent::getActionIndex(action));
case AMOTION_EVENT_ACTION_HOVER_MOVE:
return "HOVER_MOVE";
case AMOTION_EVENT_ACTION_SCROLL:
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 91ab008..02a5a08 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -203,6 +203,8 @@
case InputMessage::Type::MOTION: {
// int32_t eventId
msg->body.motion.eventId = body.motion.eventId;
+ // uint32_t pointerCount
+ msg->body.motion.pointerCount = body.motion.pointerCount;
// nsecs_t eventTime
msg->body.motion.eventTime = body.motion.eventTime;
// int32_t deviceId
@@ -245,14 +247,14 @@
msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
// float yCursorPosition
msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
- // uint32_t displayOrientation
- msg->body.motion.displayOrientation = body.motion.displayOrientation;
- // int32_t displayWidth
- msg->body.motion.displayWidth = body.motion.displayWidth;
- // int32_t displayHeight
- msg->body.motion.displayHeight = body.motion.displayHeight;
- // uint32_t pointerCount
- msg->body.motion.pointerCount = body.motion.pointerCount;
+
+ msg->body.motion.dsdxRaw = body.motion.dsdxRaw;
+ msg->body.motion.dtdxRaw = body.motion.dtdxRaw;
+ msg->body.motion.dtdyRaw = body.motion.dtdyRaw;
+ msg->body.motion.dsdyRaw = body.motion.dsdyRaw;
+ msg->body.motion.txRaw = body.motion.txRaw;
+ msg->body.motion.tyRaw = body.motion.tyRaw;
+
//struct Pointer pointers[MAX_POINTERS]
for (size_t i = 0; i < body.motion.pointerCount; i++) {
// PointerProperties properties
@@ -542,8 +544,8 @@
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation,
- int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+ float yPrecision, float xCursorPosition, float yCursorPosition,
+ const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
@@ -603,9 +605,12 @@
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.xCursorPosition = xCursorPosition;
msg.body.motion.yCursorPosition = yCursorPosition;
- msg.body.motion.displayOrientation = displayOrientation;
- msg.body.motion.displayWidth = displayWidth;
- msg.body.motion.displayHeight = displayHeight;
+ msg.body.motion.dsdxRaw = rawTransform.dsdx();
+ msg.body.motion.dtdxRaw = rawTransform.dtdx();
+ msg.body.motion.dtdyRaw = rawTransform.dtdy();
+ msg.body.motion.dsdyRaw = rawTransform.dsdy();
+ msg.body.motion.txRaw = rawTransform.tx();
+ msg.body.motion.tyRaw = rawTransform.ty();
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
@@ -1391,6 +1396,10 @@
ui::Transform transform;
transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
+ ui::Transform displayTransform;
+ displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw,
+ msg->body.motion.txRaw, msg->body.motion.dtdyRaw,
+ msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1});
event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
msg->body.motion.actionButton, msg->body.motion.flags,
@@ -1398,9 +1407,8 @@
msg->body.motion.buttonState, msg->body.motion.classification, transform,
msg->body.motion.xPrecision, msg->body.motion.yPrecision,
msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
- msg->body.motion.displayOrientation, msg->body.motion.displayWidth,
- msg->body.motion.displayHeight, msg->body.motion.downTime,
- msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+ displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,
+ pointerCount, pointerProperties, pointerCoords);
}
void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) {
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index b1ef753..caf3a61 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -226,39 +226,23 @@
static constexpr float Y_SCALE = 3.0;
static constexpr float X_OFFSET = 1;
static constexpr float Y_OFFSET = 1.1;
-
- static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
+ static constexpr float RAW_X_SCALE = 4.0;
+ static constexpr float RAW_Y_SCALE = -5.0;
+ static constexpr float RAW_X_OFFSET = 12;
+ static constexpr float RAW_Y_OFFSET = -41.1;
int32_t mId;
ui::Transform mTransform;
-
- void SetUp() override;
- void TearDown() override;
+ ui::Transform mRawTransform;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-const std::optional<bool> MotionEventTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
- !base::GetProperty("persist.debug.per_window_input_rotation", "").empty()
- ? std::optional(base::GetBoolProperty("persist.debug.per_window_input_rotation", false))
- : std::nullopt;
-
-void MotionEventTest::SetUp() {
- // Ensure per_window_input_rotation is enabled.
- base::SetProperty("persist.debug.per_window_input_rotation", "true");
-}
-
-void MotionEventTest::TearDown() {
- const auto val = INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE.has_value()
- ? (*INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE ? "true" : "false")
- : "";
- base::SetProperty("persist.debug.per_window_input_rotation", val);
-}
-
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
mId = InputEvent::nextId();
mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
+ mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1});
PointerProperties pointerProperties[2];
pointerProperties[0].clear();
@@ -294,9 +278,8 @@
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
- pointerCoords);
+ mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
+ pointerProperties, pointerCoords);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -373,39 +356,37 @@
ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
- ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(211, event->getRawPointerCoords(0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(221, event->getRawPointerCoords(1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
- ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
- ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
- ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
- ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
- ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
- ASSERT_EQ(10, event->getHistoricalRawX(0, 0));
- ASSERT_EQ(20, event->getHistoricalRawX(1, 0));
- ASSERT_EQ(110, event->getHistoricalRawX(0, 1));
- ASSERT_EQ(120, event->getHistoricalRawX(1, 1));
- ASSERT_EQ(210, event->getRawX(0));
- ASSERT_EQ(220, event->getRawX(1));
+ ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0));
+ ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0));
+ ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1));
+ ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1));
+ ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0));
+ ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1));
- ASSERT_EQ(11, event->getHistoricalRawY(0, 0));
- ASSERT_EQ(21, event->getHistoricalRawY(1, 0));
- ASSERT_EQ(111, event->getHistoricalRawY(0, 1));
- ASSERT_EQ(121, event->getHistoricalRawY(1, 1));
- ASSERT_EQ(211, event->getRawY(0));
- ASSERT_EQ(221, event->getRawY(1));
+ ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0));
+ ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1));
ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
@@ -543,8 +524,8 @@
ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
- ASSERT_EQ(210 * 2, event.getRawX(0));
- ASSERT_EQ(211 * 2, event.getRawY(0));
+ ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0));
+ ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0));
ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
@@ -592,10 +573,10 @@
// The geometrical representation is irrelevant to the test, it's just easy to generate
// and check rotation. We set the orientation to the same angle.
// Coordinate system: down is increasing Y, right is increasing X.
- const float PI_180 = float(M_PI / 180);
- const float RADIUS = 10;
- const float ARC = 36;
- const float ROTATION = ARC * 2;
+ static constexpr float PI_180 = float(M_PI / 180);
+ static constexpr float RADIUS = 10;
+ static constexpr float ARC = 36;
+ static constexpr float ROTATION = ARC * 2;
const size_t pointerCount = 11;
PointerProperties pointerProperties[pointerCount];
@@ -616,9 +597,8 @@
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
- pointerCoords);
+ identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
+ pointerProperties, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -661,7 +641,7 @@
MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
const ui::Transform& transform,
- uint32_t displayOrientation = ui::Transform::ROT_0) {
+ const ui::Transform& rawTransform) {
std::vector<PointerProperties> pointerProperties;
pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
std::vector<PointerCoords> pointerCoords;
@@ -677,19 +657,18 @@
/* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
/* buttonState */ 0, MotionClassification::NONE, transform,
/* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, displayOrientation,
- /* displayWidth */ 400,
- /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
- pointerProperties.data(), pointerCoords.data());
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, rawTransform, eventTime, eventTime,
+ pointerCoords.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
TEST_F(MotionEventTest, ApplyTransform) {
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform identity;
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+ ui::Transform rawTransform(ui::Transform::ROT_90, 800, 400);
+ MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform);
ASSERT_EQ(700, event.getRawX(0));
ASSERT_EQ(60, event.getRawY(0));
ASSERT_NE(event.getRawX(0), event.getX(0));
@@ -698,10 +677,10 @@
ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity);
- const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
- xform[0][1], xform[1][1], xform[2][1],
- xform[0][2], xform[1][2], xform[2][2]};
+ MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity, identity);
+ const std::array<float, 9> rowMajor{transform[0][0], transform[1][0], transform[2][0],
+ transform[0][1], transform[1][1], transform[2][1],
+ transform[0][2], transform[1][2], transform[2][2]};
changedEvent.applyTransform(rowMajor);
// transformContent effectively rotates the raw coordinates, so those should now include
@@ -727,9 +706,9 @@
AINPUT_SOURCE_JOYSTICK};
for (uint32_t source : NON_POINTER_SOURCES) {
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+ MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, transform);
event.setSource(source);
// Since this event comes from a non-pointer source, it should include rotation but not
@@ -741,72 +720,34 @@
}
}
-TEST_F(MotionEventTest, RawCompatTransform) {
- {
- // Make sure raw is raw regardless of transform translation.
- ui::Transform xform;
- xform.set(20, 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform);
- ASSERT_EQ(60, event.getRawX(0));
- ASSERT_EQ(100, event.getRawY(0));
- ASSERT_NE(event.getRawX(0), event.getX(0));
- ASSERT_NE(event.getRawY(0), event.getY(0));
- // Relative values should not be modified.
- ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+TEST_F(MotionEventTest, AxesAreCorrectlyTransformed) {
+ const ui::Transform identity;
+ ui::Transform transform;
+ transform.set({1.1, -2.2, 3.3, -4.4, 5.5, -6.6, 0, 0, 1});
+ ui::Transform rawTransform;
+ rawTransform.set({-6.6, 5.5, -4.4, 3.3, -2.2, 1.1, 0, 0, 1});
+ auto transformWithoutTranslation = [](const ui::Transform& t, float x, float y) {
+ auto newPoint = t.transform(x, y);
+ auto newOrigin = t.transform(0, 0);
+ return newPoint - newOrigin;
+ };
- // Next check that getRaw contains rotation (for compatibility) but otherwise is still
- // "Screen-space". The following tests check all 3 rotations.
- {
- // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
- ASSERT_EQ(700, event.getRawX(0));
- ASSERT_EQ(60, event.getRawY(0));
- ASSERT_NE(event.getRawX(0), event.getX(0));
- ASSERT_NE(event.getRawY(0), event.getY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ const MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform);
- {
- // Same as above, but check rotate-180.
- ui::Transform xform(ui::Transform::ROT_180, 400, 800);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_180);
- ASSERT_EQ(340, event.getRawX(0));
- ASSERT_EQ(700, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // The x and y axes should have the window transform applied.
+ const auto newPoint = transform.transform(60, 100);
+ ASSERT_EQ(newPoint.x, event.getX(0));
+ ASSERT_EQ(newPoint.y, event.getY(0));
- {
- // Same as above, but check rotate-270.
- ui::Transform xform(ui::Transform::ROT_270, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_270);
- ASSERT_EQ(100, event.getRawX(0));
- ASSERT_EQ(340, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // The raw values should have the display transform applied.
+ const auto raw = rawTransform.transform(60, 100);
+ ASSERT_EQ(raw.x, event.getRawX(0));
+ ASSERT_EQ(raw.y, event.getRawY(0));
- {
- // Finally, check that raw isn't effected by transform
- ui::Transform xform(ui::Transform::ROT_270, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
- ASSERT_EQ(700, event.getRawX(0));
- ASSERT_EQ(60, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // Relative values should have the window transform applied without any translation.
+ const auto rel = transformWithoutTranslation(transform, 42, 96);
+ ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+ ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
}
TEST_F(MotionEventTest, Initialize_SetsClassification) {
@@ -832,8 +773,7 @@
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
}
@@ -854,9 +794,9 @@
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
- pointerCount, pointerProperties, pointerCoords);
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identityTransform,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
ASSERT_EQ(540, event.getRawYCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 8db5bf1..d09f2ac 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -160,13 +160,14 @@
constexpr float yScale = 3;
constexpr float xOffset = -10;
constexpr float yOffset = -20;
+ constexpr float rawXScale = 4;
+ constexpr float rawYScale = -5;
+ constexpr float rawXOffset = -11;
+ constexpr float rawYOffset = 42;
constexpr float xPrecision = 0.25;
constexpr float yPrecision = 0.5;
constexpr float xCursorPosition = 1.3;
constexpr float yCursorPosition = 50.6;
- constexpr uint32_t displayOrientation = ui::Transform::ROT_0;
- constexpr int32_t displayWidth = 1000;
- constexpr int32_t displayHeight = 2000;
constexpr nsecs_t downTime = 3;
constexpr size_t pointerCount = 3;
constexpr nsecs_t eventTime = 4;
@@ -192,12 +193,14 @@
ui::Transform transform;
transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
+ ui::Transform rawTransform;
+ rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1});
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
classification, transform, xPrecision, yPrecision,
- xCursorPosition, yCursorPosition, displayOrientation,
- displayWidth, displayHeight, downTime, eventTime,
- pointerCount, pointerProperties, pointerCoords);
+ xCursorPosition, yCursorPosition, rawTransform,
+ downTime, eventTime, pointerCount, pointerProperties,
+ pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -234,9 +237,7 @@
EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
- EXPECT_EQ(displayOrientation, motionEvent->getDisplayOrientation());
- EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x);
- EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y);
+ EXPECT_EQ(rawTransform, motionEvent->getRawTransform());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -247,28 +248,18 @@
EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- motionEvent->getRawX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- motionEvent->getRawY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
- motionEvent->getX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
- motionEvent->getY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- motionEvent->getPressure(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- motionEvent->getSize(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- motionEvent->getTouchMajor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- motionEvent->getTouchMinor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- motionEvent->getToolMajor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- motionEvent->getToolMinor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
- motionEvent->getOrientation(i));
+ const auto& pc = pointerCoords[i];
+ EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i));
+ EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i));
+ EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i));
+ EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getOrientation(i));
}
status = mConsumer->sendFinishedSignal(seq, false);
@@ -505,12 +496,12 @@
}
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -522,12 +513,12 @@
PointerCoords pointerCoords[pointerCount];
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -544,12 +535,12 @@
}
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 18289a5..2f88704 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -49,7 +49,7 @@
CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
- CHECK_OFFSET(InputMessage::Body::Motion, empty1, 4);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 4);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -74,11 +74,13 @@
CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
- CHECK_OFFSET(InputMessage::Body::Motion, displayOrientation, 136);
- CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 140);
- CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 144);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 148);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdxRaw, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdxRaw, 140);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdyRaw, 144);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdyRaw, 148);
+ CHECK_OFFSET(InputMessage::Body::Motion, txRaw, 152);
+ CHECK_OFFSET(InputMessage::Body::Motion, tyRaw, 156);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 160);
CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 13e2b02..3039362 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -184,8 +184,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
events.emplace_back(event);
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index b29c9a4..f2b59ea 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -43,12 +43,12 @@
ui::Transform transform;
transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
+ ui::Transform identity;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 100 /*downTime*/,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identity, 100 /*downTime*/,
200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
return event;
}
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index ba5a64f..36d001f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -54,7 +54,7 @@
target: {
windows: {
enabled: true,
- }
+ },
},
defaults: [
@@ -86,6 +86,7 @@
export_include_dirs: [
"include",
+ "include_mock",
"include_private",
"include_types",
],
@@ -158,7 +159,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.allocator@4.0",
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
@@ -175,7 +176,7 @@
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libgralloctypes",
],
@@ -265,6 +266,6 @@
"Rect.cpp",
"Region.cpp",
"PixelFormat.cpp",
- "Transform.cpp"
+ "Transform.cpp",
],
}
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 9dc9beb..80f6c82 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -939,7 +939,7 @@
<< "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
<< std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
<< ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
- << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace)
+ << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace) << std::dec
<< ", compressed: ";
if (less) {
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index cd68c1c..b34d906 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -396,6 +396,11 @@
result.mMatrix[1][0] = -b*idet;
result.mMatrix[1][1] = a*idet;
result.mType = mType;
+ if (getOrientation() & ROT_90) {
+ // Recalculate the type if there is a 90-degree rotation component, since the inverse
+ // of ROT_90 is ROT_270 and vice versa.
+ result.mType |= UNKNOWN_TYPE;
+ }
vec2 T(-x, -y);
T = result.transform(T);
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 6efecd3..7634007 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -26,6 +26,10 @@
namespace android {
+namespace mock {
+class MockFence;
+}
+
class String8;
// ===========================================================================
@@ -109,7 +113,7 @@
// fence transitioned to the signaled state. If the fence is not signaled
// then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an
// error occurs then SIGNAL_TIME_INVALID is returned.
- nsecs_t getSignalTime() const;
+ virtual nsecs_t getSignalTime() const;
enum class Status {
Invalid, // Fence is invalid
@@ -144,7 +148,10 @@
private:
// Only allow instantiation using ref counting.
friend class LightRefBase<Fence>;
- ~Fence() = default;
+ virtual ~Fence() = default;
+
+ // Allow mocking for unit testing
+ friend class mock::MockFence;
base::unique_fd mFenceFd;
};
diff --git a/libs/binder/include/binder/RpcCertificateVerifier.h b/libs/ui/include_mock/ui/MockFence.h
similarity index 61%
copy from libs/binder/include/binder/RpcCertificateVerifier.h
copy to libs/ui/include_mock/ui/MockFence.h
index 97af31c..162ec02 100644
--- a/libs/binder/include/binder/RpcCertificateVerifier.h
+++ b/libs/ui/include_mock/ui/MockFence.h
@@ -16,17 +16,17 @@
#pragma once
-#include <openssl/ssl.h>
-#include <utils/Errors.h>
+#include <gmock/gmock.h>
+#include <ui/Fence.h>
-namespace android {
+namespace android::mock {
-// An interface with a function that verifies a peer certificate. It is a wrapper over the custom
-// verify function (see SSL_CTX_set_custom_verify).
-class RpcCertificateVerifier {
+class MockFence : public android::Fence {
public:
- virtual ~RpcCertificateVerifier() = default;
- virtual status_t verify(const X509* peerCert, uint8_t* outAlert) = 0;
+ MockFence() = default;
+ virtual ~MockFence() = default;
+
+ MOCK_METHOD(nsecs_t, getSignalTime, (), (const, override));
};
-} // namespace android
+}; // namespace android::mock
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 516aad8..0ee15f2 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -27,28 +27,40 @@
name: "Region_test",
shared_libs: ["libui"],
srcs: ["Region_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "colorspace_test",
shared_libs: ["libui"],
srcs: ["colorspace_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "DisplayId_test",
shared_libs: ["libui"],
srcs: ["DisplayId_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "FlattenableHelpers_test",
shared_libs: ["libui"],
srcs: ["FlattenableHelpers_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -68,7 +80,10 @@
"GraphicBufferAllocator_test.cpp",
"mock/MockGrallocAllocator.cpp",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -83,14 +98,20 @@
"libutils",
],
srcs: ["GraphicBuffer_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
// This test has a main method, and requires a separate binary to be built.
cc_test {
name: "GraphicBufferOverBinder_test",
srcs: ["GraphicBufferOverBinder_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbinder",
"libgui",
@@ -105,7 +126,10 @@
test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Rect_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -113,5 +137,29 @@
test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Size_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "MockFence_test",
+ shared_libs: ["libui"],
+ static_libs: ["libgmock"],
+ srcs: ["MockFence_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "Transform_test",
+ shared_libs: ["libui"],
+ srcs: ["Transform_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/libs/ui/tests/MockFence_test.cpp b/libs/ui/tests/MockFence_test.cpp
new file mode 100644
index 0000000..6e520b1
--- /dev/null
+++ b/libs/ui/tests/MockFence_test.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 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 <ui/MockFence.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+using testing::Return;
+
+class MockFenceTest : public testing::Test {
+public:
+ sp<Fence> getFenceForTesting() const { return mMockFence; }
+
+ const mock::MockFence& getMockFence() const { return *mMockFence; }
+
+private:
+ sp<mock::MockFence> mMockFence = sp<mock::MockFence>::make();
+};
+
+TEST_F(MockFenceTest, getSignalTime) {
+ sp<Fence> fence = getFenceForTesting();
+
+ EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(Fence::SIGNAL_TIME_PENDING));
+ EXPECT_EQ(Fence::SIGNAL_TIME_PENDING, fence->getSignalTime());
+
+ EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(1234));
+ EXPECT_EQ(1234, fence->getSignalTime());
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/Transform_test.cpp b/libs/ui/tests/Transform_test.cpp
new file mode 100644
index 0000000..6964284
--- /dev/null
+++ b/libs/ui/tests/Transform_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 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 <ui/Transform.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(TransformTest, inverseRotation_hasCorrectType) {
+ const auto testRotationFlagsForInverse = [](Transform::RotationFlags rotation,
+ Transform::RotationFlags expectedInverse,
+ bool isRotation) {
+ const Transform t(rotation, 0, 0);
+ EXPECT_EQ(t.getOrientation(), rotation);
+ const Transform inverse = t.inverse();
+ EXPECT_EQ(inverse.getOrientation(), expectedInverse);
+
+ if (isRotation) {
+ EXPECT_TRUE(t.getType() & Transform::ROTATE);
+ EXPECT_TRUE(inverse.getType() & Transform::ROTATE);
+ } else {
+ EXPECT_FALSE(t.getType() & Transform::ROTATE);
+ EXPECT_FALSE(inverse.getType() & Transform::ROTATE);
+ }
+ };
+
+ testRotationFlagsForInverse(Transform::ROT_0, Transform::ROT_0, false);
+ testRotationFlagsForInverse(Transform::ROT_90, Transform::ROT_270, true);
+ testRotationFlagsForInverse(Transform::ROT_180, Transform::ROT_180, true);
+ testRotationFlagsForInverse(Transform::ROT_270, Transform::ROT_90, true);
+ testRotationFlagsForInverse(Transform::FLIP_H, Transform::FLIP_H, false);
+ testRotationFlagsForInverse(Transform::FLIP_V, Transform::FLIP_V, false);
+}
+
+} // namespace android::ui
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 4c07c22..f053568 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -30,7 +30,6 @@
#include <input/InputTransport.h>
#include <android/os/BnInputFlinger.h>
-#include <android/os/IInputFlinger.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 6ce0313..68d25f9 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -236,8 +236,8 @@
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
identityTransform, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, currentTime, currentTime,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, currentTime,
+ currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
return event;
}
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 571c126..bcb0071 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -309,15 +309,14 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor,
- uint32_t displayOrientation, int2 displaySize)
+ const ui::Transform& transform, const ui::Transform& rawTransform,
+ float globalScaleFactor)
: seq(nextSeq()),
eventEntry(std::move(eventEntry)),
targetFlags(targetFlags),
transform(transform),
+ rawTransform(rawTransform),
globalScaleFactor(globalScaleFactor),
- displayOrientation(displayOrientation),
- displaySize(displaySize),
deliveryTime(0),
resolvedAction(0),
resolvedFlags(0) {}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 5365a78..7a121ce 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -226,9 +226,8 @@
std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
int32_t targetFlags;
ui::Transform transform;
+ ui::Transform rawTransform;
float globalScaleFactor;
- uint32_t displayOrientation;
- int2 displaySize;
// Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
// and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
@@ -241,8 +240,8 @@
int32_t resolvedFlags;
DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor, uint32_t displayOrientation,
- int2 displaySize);
+ const ui::Transform& transform, const ui::Transform& rawTransform,
+ float globalScaleFactor);
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 058e099..f094fee 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -55,6 +55,7 @@
using android::base::HwTimeoutMultiplier;
using android::base::Result;
using android::base::StringPrintf;
+using android::gui::DisplayInfo;
using android::gui::FocusRequest;
using android::gui::TouchOcclusionMode;
using android::gui::WindowInfo;
@@ -345,18 +346,15 @@
// Use identity transform for joystick and position-based (touchpad) events because they
// don't depend on the window transform.
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
- 1.0f /*globalScaleFactor*/,
- inputTarget.displayOrientation,
- inputTarget.displaySize);
+ identityTransform, 1.0f /*globalScaleFactor*/);
}
}
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
- inputTarget.globalScaleFactor,
- inputTarget.displayOrientation,
- inputTarget.displaySize);
+ inputTarget.displayTransform,
+ inputTarget.globalScaleFactor);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -407,24 +405,11 @@
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
- firstPointerTransform, inputTarget.globalScaleFactor,
- inputTarget.displayOrientation,
- inputTarget.displaySize);
+ firstPointerTransform, inputTarget.displayTransform,
+ inputTarget.globalScaleFactor);
return dispatchEntry;
}
-void addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
- float yOffset = 0) {
- if (monitors.empty()) {
- return;
- }
- outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
- for (const Monitor& monitor : monitors) {
- outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
- }
-}
-
status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel,
std::unique_ptr<InputChannel>& clientChannel) {
std::unique_ptr<InputChannel> uniqueServerChannel;
@@ -539,6 +524,16 @@
return true;
}
+bool isFromSource(uint32_t source, uint32_t test) {
+ return (source & test) == test;
+}
+
+vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) {
+ const vec2 transformedXy = transform.transform(x, y);
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ return transformedXy - transformedOrigin;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -946,10 +941,9 @@
}
// Alternatively, maybe there's a gesture monitor that could handle this event
- std::vector<TouchedMonitor> gestureMonitors = findTouchedGestureMonitorsLocked(displayId);
- for (TouchedMonitor& gestureMonitor : gestureMonitors) {
+ for (const auto& monitor : getValueByKey(mGestureMonitorsByDisplay, displayId)) {
sp<Connection> connection =
- getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
+ getConnectionLocked(monitor.inputChannel->getConnectionToken());
if (connection != nullptr && connection->responsive) {
// This monitor could take more input. Drop all events preceding this
// event, so that gesture monitor could get a chance to receive the stream
@@ -1076,15 +1070,6 @@
return nullptr;
}
-std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
- int32_t displayId) const {
- std::vector<TouchedMonitor> touchedMonitors;
-
- std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
- addGestureMonitors(monitors, touchedMonitors);
- return touchedMonitors;
-}
-
void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
@@ -1706,13 +1691,13 @@
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
", policyFlags=0x%x, "
- "action=0x%x, actionButton=0x%x, flags=0x%x, "
+ "action=%s, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
- entry.policyFlags, entry.action, entry.actionButton, entry.flags, entry.metaState,
- entry.buttonState, entry.edgeFlags, entry.xPrecision, entry.yPrecision,
- entry.downTime);
+ entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
+ entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
+ entry.xPrecision, entry.yPrecision, entry.downTime);
for (uint32_t i = 0; i < entry.pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
@@ -1868,6 +1853,11 @@
return InputEventInjectionResult::FAILED;
}
+ // Drop key events if requested by input feature
+ if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
+ return InputEventInjectionResult::FAILED;
+ }
+
// Compatibility behavior: raise ANR if there is a focused application, but no focused window.
// Only start counting when we have a focused event to dispatch. The ANR is canceled if we
// start interacting with another application via touch (app switch). This code can be removed
@@ -1941,16 +1931,16 @@
* Given a list of monitors, remove the ones we cannot find a connection for, and the ones
* that are currently unresponsive.
*/
-std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked(
- const std::vector<TouchedMonitor>& monitors) const {
- std::vector<TouchedMonitor> responsiveMonitors;
+std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked(
+ const std::vector<Monitor>& monitors) const {
+ std::vector<Monitor> responsiveMonitors;
std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
- [this](const TouchedMonitor& monitor) REQUIRES(mLock) {
- sp<Connection> connection = getConnectionLocked(
- monitor.monitor.inputChannel->getConnectionToken());
+ [this](const Monitor& monitor) REQUIRES(mLock) {
+ sp<Connection> connection =
+ getConnectionLocked(monitor.inputChannel->getConnectionToken());
if (connection == nullptr) {
ALOGE("Could not find connection for monitor %s",
- monitor.monitor.inputChannel->getName().c_str());
+ monitor.inputChannel->getName().c_str());
return false;
}
if (!connection->responsive) {
@@ -2051,14 +2041,10 @@
x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
}
- bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
+ const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
isDown /*addOutsideTargets*/);
- std::vector<TouchedMonitor> newGestureMonitors = isDown
- ? findTouchedGestureMonitorsLocked(displayId)
- : std::vector<TouchedMonitor>{};
-
// Figure out whether splitting will be allowed for this window.
if (newTouchedWindowHandle != nullptr &&
newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2113,8 +2099,15 @@
}
}
- // Also don't send the new touch event to unresponsive gesture monitors
- newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
+ // Drop touch events if requested by input feature
+ if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) {
+ newTouchedWindowHandle = nullptr;
+ }
+
+ const std::vector<Monitor> newGestureMonitors = isDown
+ ? selectResponsiveMonitorsLocked(
+ getValueByKey(mGestureMonitorsByDisplay, displayId))
+ : std::vector<Monitor>{};
if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
ALOGI("Dropping event because there is no touchable window or gesture monitor at "
@@ -2178,6 +2171,13 @@
sp<WindowInfoHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+
+ // Drop touch events if requested by input feature
+ if (newTouchedWindowHandle != nullptr &&
+ shouldDropInput(entry, newTouchedWindowHandle)) {
+ newTouchedWindowHandle = nullptr;
+ }
+
if (oldTouchedWindowHandle != newTouchedWindowHandle &&
oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
@@ -2327,9 +2327,8 @@
touchedWindow.pointerIds, inputTargets);
}
- for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
- addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
- touchedMonitor.yOffset, inputTargets);
+ for (const auto& monitor : tempTouchState.gestureMonitors) {
+ addMonitoringTargetLocked(monitor, displayId, inputTargets);
}
// Drop the outside or hover touch windows since we will not care about them
@@ -2506,9 +2505,13 @@
inputTarget.inputChannel = inputChannel;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
- inputTarget.displayOrientation = windowInfo->displayOrientation;
- inputTarget.displaySize =
- int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
+ const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId);
+ if (displayInfoIt != mDisplayInfos.end()) {
+ inputTarget.displayTransform = displayInfoIt->second.transform;
+ } else {
+ ALOGI_IF(isPerWindowInputRotationEnabled(),
+ "DisplayInfo not found for window on display: %d", windowInfo->displayId);
+ }
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
}
@@ -2520,27 +2523,44 @@
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
- int32_t displayId, float xOffset,
- float yOffset) {
+ int32_t displayId) {
std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it =
mGlobalMonitorsByDisplay.find(displayId);
if (it != mGlobalMonitorsByDisplay.end()) {
const std::vector<Monitor>& monitors = it->second;
for (const Monitor& monitor : monitors) {
- addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets);
+ addMonitoringTargetLocked(monitor, displayId, inputTargets);
}
}
}
-void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset,
- float yOffset,
+void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId,
std::vector<InputTarget>& inputTargets) {
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
ui::Transform t;
- t.set(xOffset, yOffset);
+ if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
+ // Input monitors always get un-rotated display coordinates. We undo the display
+ // rotation that is present in the display transform so that display rotation is not
+ // applied to these input targets.
+ const auto& displayInfo = it->second;
+ int32_t width = displayInfo.logicalWidth;
+ int32_t height = displayInfo.logicalHeight;
+ const auto orientation = displayInfo.transform.getOrientation();
+ uint32_t inverseOrientation = orientation;
+ if (orientation == ui::Transform::ROT_90) {
+ inverseOrientation = ui::Transform::ROT_270;
+ std::swap(width, height);
+ } else if (orientation == ui::Transform::ROT_270) {
+ inverseOrientation = ui::Transform::ROT_90;
+ std::swap(width, height);
+ }
+ target.displayTransform =
+ ui::Transform(inverseOrientation, width, height) * displayInfo.transform;
+ t = t * target.displayTransform;
+ }
target.setDefaultPointerTransform(t);
inputTargets.push_back(target);
}
@@ -3226,9 +3246,7 @@
motionEntry.xPrecision, motionEntry.yPrecision,
motionEntry.xCursorPosition,
motionEntry.yCursorPosition,
- dispatchEntry->displayOrientation,
- dispatchEntry->displaySize.x,
- dispatchEntry->displaySize.y,
+ dispatchEntry->rawTransform,
motionEntry.downTime, motionEntry.eventTime,
motionEntry.pointerCount,
motionEntry.pointerProperties, usingCoords);
@@ -3954,17 +3972,21 @@
mLock.lock();
if (shouldSendMotionToInputFilterLocked(args)) {
+ ui::Transform displayTransform;
+ if (const auto it = mDisplayInfos.find(args->displayId); it != mDisplayInfos.end()) {
+ displayTransform = it->second.transform;
+ }
+
mLock.unlock();
MotionEvent event;
- ui::Transform transform;
event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
args->action, args->actionButton, args->flags, args->edgeFlags,
- args->metaState, args->buttonState, args->classification, transform,
- args->xPrecision, args->yPrecision, args->xCursorPosition,
- args->yCursorPosition, ui::Transform::ROT_0, INVALID_DISPLAY_SIZE,
- INVALID_DISPLAY_SIZE, args->downTime, args->eventTime,
- args->pointerCount, args->pointerProperties, args->pointerCoords);
+ args->metaState, args->buttonState, args->classification,
+ displayTransform, args->xPrecision, args->yPrecision,
+ args->xCursorPosition, args->yCursorPosition, displayTransform,
+ args->downTime, args->eventTime, args->pointerCount,
+ args->pointerProperties, args->pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -4212,6 +4234,7 @@
pointerProperties, samplePointerCoords,
motionEvent.getXOffset(),
motionEvent.getYOffset());
+ transformMotionEntryForInjectionLocked(*injectedEntry);
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
@@ -4233,6 +4256,7 @@
uint32_t(pointerCount), pointerProperties,
samplePointerCoords, motionEvent.getXOffset(),
motionEvent.getYOffset());
+ transformMotionEntryForInjectionLocked(*nextInjectedEntry);
injectedEntries.push(std::move(nextInjectedEntry));
}
break;
@@ -4396,6 +4420,38 @@
}
}
+void InputDispatcher::transformMotionEntryForInjectionLocked(MotionEntry& entry) const {
+ const bool isRelativeMouseEvent = isFromSource(entry.source, AINPUT_SOURCE_MOUSE_RELATIVE);
+ if (!isRelativeMouseEvent && !isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ return;
+ }
+
+ // Input injection works in the logical display coordinate space, but the input pipeline works
+ // display space, so we need to transform the injected events accordingly.
+ const auto it = mDisplayInfos.find(entry.displayId);
+ if (it == mDisplayInfos.end()) return;
+ const auto& transformToDisplay = it->second.transform.inverse();
+
+ for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ PointerCoords& pc = entry.pointerCoords[i];
+ const auto xy = isRelativeMouseEvent
+ ? transformWithoutTranslation(transformToDisplay, pc.getX(), pc.getY())
+ : transformToDisplay.transform(pc.getXYValue());
+ pc.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ // Axes with relative values never represent points on a screen, so they should never have
+ // translation applied. If a device does not report relative values, these values are always
+ // 0, and will remain unaffected by the following operation.
+ const auto rel =
+ transformWithoutTranslation(transformToDisplay,
+ pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+ pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, rel.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, rel.y);
+ }
+}
+
void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
InjectionState* injectionState = entry.injectionState;
if (injectionState) {
@@ -4562,6 +4618,7 @@
void InputDispatcher::setInputWindows(
const std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>>& handlesPerDisplay) {
+ // TODO(b/198444055): Remove setInputWindows from InputDispatcher.
{ // acquire lock
std::scoped_lock _l(mLock);
for (const auto& [displayId, handles] : handlesPerDisplay) {
@@ -4642,6 +4699,18 @@
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"touched window was removed");
synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
+ // Since we are about to drop the touch, cancel the events for the wallpaper as
+ // well.
+ if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND &&
+ touchedWindow.windowHandle->getInfo()->hasWallpaper) {
+ sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
+ if (wallpaper != nullptr) {
+ sp<Connection> wallpaperConnection =
+ getConnectionLocked(wallpaper->getToken());
+ synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
+ options);
+ }
+ }
}
state.windows.erase(state.windows.begin() + i);
} else {
@@ -5082,9 +5151,17 @@
}
if (!mWindowHandlesByDisplay.empty()) {
- for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<WindowInfoHandle>> windowHandles = it.second;
- dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
+ for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
+ dump += StringPrintf(INDENT "Display: %" PRId32 "\n", displayId);
+ if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
+ const auto& displayInfo = it->second;
+ dump += StringPrintf(INDENT2 "logicalSize=%dx%d\n", displayInfo.logicalWidth,
+ displayInfo.logicalHeight);
+ displayInfo.transform.dump(dump, "transform", INDENT4);
+ } else {
+ dump += INDENT2 "No DisplayInfo found!\n";
+ }
+
if (!windowHandles.empty()) {
dump += INDENT2 "Windows:\n";
for (size_t i = 0; i < windowHandles.size(); i++) {
@@ -5116,13 +5193,12 @@
windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
"ms, trustedOverlay=%s, hasToken=%s, "
- "touchOcclusionMode=%s, displayOrientation=%d\n",
+ "touchOcclusionMode=%s\n",
windowInfo->ownerPid, windowInfo->ownerUid,
millis(windowInfo->dispatchingTimeout),
toString(windowInfo->trustedOverlay),
toString(windowInfo->token != nullptr),
- toString(windowInfo->touchOcclusionMode).c_str(),
- windowInfo->displayOrientation);
+ toString(windowInfo->touchOcclusionMode).c_str());
windowInfo->transform.dump(dump, "transform", INDENT4);
}
} else {
@@ -5435,9 +5511,9 @@
TouchState& state = stateIt->second;
std::shared_ptr<InputChannel> requestingChannel;
std::optional<int32_t> foundDeviceId;
- for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
- if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
- requestingChannel = touchedMonitor.monitor.inputChannel;
+ for (const auto& monitor : state.gestureMonitors) {
+ if (monitor.inputChannel->getConnectionToken() == token) {
+ requestingChannel = monitor.inputChannel;
foundDeviceId = state.deviceId;
}
}
@@ -6168,16 +6244,44 @@
mLooper->wake();
}
-void InputDispatcher::onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) {
+void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+ const std::vector<DisplayInfo>& displayInfos) {
// 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;
-
for (const auto& info : windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info));
}
- setInputWindows(handlesPerDisplay);
+
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ mDisplayInfos.clear();
+ for (const auto& displayInfo : displayInfos) {
+ mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
+ }
+
+ for (const auto& [displayId, handles] : handlesPerDisplay) {
+ setInputWindowsLocked(handles, displayId);
+ }
+ }
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
+}
+
+bool InputDispatcher::shouldDropInput(
+ const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const {
+ if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) ||
+ (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) &&
+ isWindowObscuredLocked(windowHandle))) {
+ ALOGW("Dropping %s event targeting %s as requested by input feature %s on display "
+ "%" PRId32 ".",
+ ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
+ windowHandle->getInfo()->inputFeatures.string().c_str(),
+ windowHandle->getInfo()->displayId);
+ return true;
+ }
+ return false;
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index c09a89c..2282d91 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -143,7 +143,8 @@
void displayRemoved(int32_t displayId) override;
- void onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) override;
+ void onWindowInfosChanged(const std::vector<gui::WindowInfo>&,
+ const std::vector<gui::DisplayInfo>&) override;
private:
enum class DropReason {
@@ -279,6 +280,7 @@
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
void setInjectionResult(EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
+ void transformMotionEntryForInjectionLocked(MotionEntry&) const REQUIRES(mLock);
std::condition_variable mInjectionSyncFinished;
void incrementPendingForegroundDispatches(EventEntry& entry);
@@ -337,8 +339,10 @@
float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
- std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>>
+ std::unordered_map<int32_t /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>>
mWindowHandlesByDisplay GUARDED_BY(mLock);
+ std::unordered_map<int32_t /*displayId*/, android::gui::DisplayInfo> mDisplayInfos
+ GUARDED_BY(mLock);
void setInputWindowsLocked(
const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
int32_t displayId) REQUIRES(mLock);
@@ -513,18 +517,16 @@
android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
- std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId) const
- REQUIRES(mLock);
- std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
- const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
+ std::vector<Monitor> selectResponsiveMonitorsLocked(
+ const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
+ void addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId,
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
- float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
+ void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
+ REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<android::gui::WindowInfoHandle>& windowHandle,
const InjectionState* injectionState);
@@ -554,6 +556,10 @@
std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
const sp<android::gui::WindowInfoHandle>& windowHandle);
+ bool shouldDropInput(const EventEntry& entry,
+ const sp<android::gui::WindowInfoHandle>& windowHandle) const
+ REQUIRES(mLock);
+
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
// with the mutex held makes it easier to ensure that connection invariants are maintained.
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 7c463c8..5b76eee 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -101,11 +101,8 @@
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- // Current display orientation
- uint32_t displayOrientation = ui::Transform::ROT_0;
-
- // Display-size in its natural rotation. Used for compatibility transform of raw coordinates.
- int2 displaySize = {INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE};
+ // Current display transform. Used for compatibility for raw coordinates.
+ ui::Transform displayTransform;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index bbce759..43a82d5 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -22,8 +22,4 @@
Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid)
: inputChannel(inputChannel), pid(pid) {}
-// --- TouchedMonitor ---
-TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
- : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {}
-
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index 7be0760..365d5be 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -29,15 +29,6 @@
explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid);
};
-// For tracking the offsets we need to apply when adding gesture monitor targets.
-struct TouchedMonitor {
- Monitor monitor;
- float xOffset = 0.f;
- float yOffset = 0.f;
-
- explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
-};
-
} // namespace android::inputdispatcher
#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index b7ed658..759b3e7 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -75,7 +75,7 @@
windows.push_back(touchedWindow);
}
-void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) {
+void TouchState::addGestureMonitors(const std::vector<Monitor>& newMonitors) {
const size_t newSize = gestureMonitors.size() + newMonitors.size();
gestureMonitors.reserve(newSize);
gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors),
@@ -134,4 +134,14 @@
return haveSlipperyForegroundWindow;
}
+sp<WindowInfoHandle> TouchState::getWallpaperWindow() const {
+ for (size_t i = 0; i < windows.size(); i++) {
+ const TouchedWindow& window = windows[i];
+ if (window.windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) {
+ return window.windowHandle;
+ }
+ }
+ return nullptr;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 579b868..4a62051 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -36,7 +36,7 @@
int32_t displayId; // id to the display that currently has a touch, others are rejected
std::vector<TouchedWindow> windows;
- std::vector<TouchedMonitor> gestureMonitors;
+ std::vector<Monitor> gestureMonitors;
TouchState();
~TouchState();
@@ -45,12 +45,13 @@
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
void addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle);
- void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
+ void addGestureMonitors(const std::vector<Monitor>& monitors);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
void filterNonMonitors();
sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
+ sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index 827e31a..0dfe7f1 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -31,7 +31,7 @@
#define DEBUG_VIRTUAL_KEYS 0
// Log debug messages about pointers.
-#define DEBUG_POINTERS 0
+static constexpr bool DEBUG_POINTERS = false;
// Log debug messages about pointer assignment calculations.
#define DEBUG_POINTER_ASSIGNMENT 0
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index fab7f4c..4bd1cd8 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -95,13 +95,13 @@
}
if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
-#if DEBUG_POINTERS
- if (newSlot) {
- ALOGW("MultiTouch device emitted invalid slot index %d but it "
- "should be between 0 and %zd; ignoring this slot.",
- mCurrentSlot, mSlotCount - 1);
+ if (DEBUG_POINTERS) {
+ if (newSlot) {
+ ALOGW("MultiTouch device emitted invalid slot index %d but it "
+ "should be between 0 and %zd; ignoring this slot.",
+ mCurrentSlot, mSlotCount - 1);
+ }
}
-#endif
} else {
Slot* slot = &mSlots[mCurrentSlot];
// If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
@@ -273,19 +273,19 @@
if (id) {
outState->rawPointerData.canceledIdBits.markBit(id.value());
}
-#if DEBUG_POINTERS
- ALOGI("Stop processing slot %zu for it received a palm event from device %s", inIndex,
- getDeviceName().c_str());
-#endif
+ if (DEBUG_POINTERS) {
+ ALOGI("Stop processing slot %zu for it received a palm event from device %s",
+ inIndex, getDeviceName().c_str());
+ }
continue;
}
if (outCount >= MAX_POINTERS) {
-#if DEBUG_POINTERS
- ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
- "ignoring the rest.",
- getDeviceName().c_str(), MAX_POINTERS);
-#endif
+ if (DEBUG_POINTERS) {
+ ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
+ "ignoring the rest.",
+ getDeviceName().c_str(), MAX_POINTERS);
+ }
break; // too many fingers!
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 419b0d0..22a0b57 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -749,16 +749,17 @@
mPhysicalLeft = naturalPhysicalLeft;
mPhysicalTop = naturalPhysicalTop;
- const int32_t oldSurfaceWidth = mRawSurfaceWidth;
- const int32_t oldSurfaceHeight = mRawSurfaceHeight;
- mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
- mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
- mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
- mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
- mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
- mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
-
if (isPerWindowInputRotationEnabled()) {
+ // When per-window input rotation is enabled, InputReader works in the display
+ // space, so the surface bounds are the bounds of the display device.
+ const int32_t oldSurfaceWidth = mRawSurfaceWidth;
+ const int32_t oldSurfaceHeight = mRawSurfaceHeight;
+ mRawSurfaceWidth = naturalDeviceWidth;
+ mRawSurfaceHeight = naturalDeviceHeight;
+ mSurfaceLeft = 0;
+ mSurfaceTop = 0;
+ mSurfaceRight = mRawSurfaceWidth;
+ mSurfaceBottom = mRawSurfaceHeight;
// When per-window input rotation is enabled, InputReader works in the un-rotated
// coordinate space, so we don't need to do anything if the device is already
// orientation-aware. If the device is not orientation-aware, then we need to apply
@@ -774,6 +775,14 @@
mRawSurfaceWidth == oldSurfaceWidth &&
mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged;
} else {
+ mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
+ mRawSurfaceHeight =
+ naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
+ mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
+ mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
+ mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
+ mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
+
mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
: DISPLAY_ORIENTATION_0;
}
@@ -3772,6 +3781,12 @@
const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
+ if (isPerWindowInputRotationEnabled()) {
+ return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
+ xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
+ y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
+ yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
+ }
return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight &&
y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index a56468f..3340672 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -435,6 +435,7 @@
// The surface origin specifies how the surface coordinates should be translated
// to align with the logical display coordinate space.
+ // TODO(b/188939842): Remove surface coordinates when Per-Window Input Rotation is enabled.
int32_t mSurfaceLeft;
int32_t mSurfaceTop;
int32_t mSurfaceRight;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 0a91bde..7fb2ccf 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -74,6 +74,12 @@
return event;
}
+static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) {
+ ASSERT_EQ(expectedAction, receivedAction)
+ << "expected " << MotionEvent::actionToString(expectedAction) << ", got "
+ << MotionEvent::actionToString(receivedAction);
+}
+
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -86,13 +92,29 @@
FakeInputDispatcherPolicy() {}
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
- assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
- args.displayId);
+ assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
+ ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_KEY);
+ EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+ const auto& keyEvent = static_cast<const KeyEvent&>(event);
+ EXPECT_EQ(keyEvent.getEventTime(), args.eventTime);
+ EXPECT_EQ(keyEvent.getAction(), args.action);
+ });
}
- void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
- assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
- args.displayId);
+ void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point) {
+ assertFilterInputEventWasCalledInternal([&](const InputEvent& event) {
+ ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_MOTION);
+ EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+ const auto& motionEvent = static_cast<const MotionEvent&>(event);
+ EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
+ EXPECT_EQ(motionEvent.getAction(), args.action);
+ EXPECT_EQ(motionEvent.getX(0), point.x);
+ EXPECT_EQ(motionEvent.getY(0), point.y);
+ EXPECT_EQ(motionEvent.getRawX(0), point.x);
+ EXPECT_EQ(motionEvent.getRawY(0), point.y);
+ });
}
void assertFilterInputEventWasNotCalled() {
@@ -419,26 +441,11 @@
mDropTargetWindowToken = token;
}
- void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
- int32_t displayId) {
+ void assertFilterInputEventWasCalledInternal(
+ const std::function<void(const InputEvent&)>& verify) {
std::scoped_lock lock(mLock);
ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
- ASSERT_EQ(mFilteredEvent->getType(), type);
-
- if (type == AINPUT_EVENT_TYPE_KEY) {
- const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
- EXPECT_EQ(keyEvent.getEventTime(), eventTime);
- EXPECT_EQ(keyEvent.getAction(), action);
- EXPECT_EQ(keyEvent.getDisplayId(), displayId);
- } else if (type == AINPUT_EVENT_TYPE_MOTION) {
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
- EXPECT_EQ(motionEvent.getEventTime(), eventTime);
- EXPECT_EQ(motionEvent.getAction(), action);
- EXPECT_EQ(motionEvent.getDisplayId(), displayId);
- } else {
- FAIL() << "Unknown type: " << type;
- }
-
+ verify(*mFilteredEvent);
mFilteredEvent = nullptr;
}
};
@@ -536,8 +543,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
/*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -550,8 +557,7 @@
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -563,8 +569,7 @@
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -577,8 +582,7 @@
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -590,8 +594,7 @@
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -602,8 +605,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -613,8 +616,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -626,8 +629,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -638,8 +641,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -652,8 +655,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -801,7 +804,8 @@
}
case AINPUT_EVENT_TYPE_MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- EXPECT_EQ(expectedAction, motionEvent.getAction());
+ assertMotionAction(expectedAction, motionEvent.getAction());
+
if (expectedFlags.has_value()) {
EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
}
@@ -982,6 +986,10 @@
mInfo.addTouchableRegion(frame);
}
+ void setType(WindowInfo::Type type) { mInfo.type = type; }
+
+ void setHasWallpaper(bool hasWallpaper) { mInfo.hasWallpaper = hasWallpaper; }
+
void addFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags |= flags; }
void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; }
@@ -1285,9 +1293,8 @@
mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE,
mButtonState, MotionClassification::NONE, identityTransform,
/* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
- mRawYCursorPosition, mDisplayOrientation, mDisplayWidth, mDisplayHeight,
- mEventTime, mEventTime, mPointers.size(), pointerProperties.data(),
- pointerCoords.data());
+ mRawYCursorPosition, identityTransform, mEventTime, mEventTime,
+ mPointers.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
@@ -1302,9 +1309,6 @@
int32_t mFlags{0};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
- uint32_t mDisplayOrientation{ui::Transform::ROT_0};
- int32_t mDisplayWidth{INVALID_DISPLAY_SIZE};
- int32_t mDisplayHeight{INVALID_DISPLAY_SIZE};
std::vector<PointerBuilder> mPointers;
};
@@ -1485,6 +1489,197 @@
windowSecond->assertNoEvents();
}
+/**
+ * Two windows: A top window, and a wallpaper behind the window.
+ * Touch goes to the top window, and then top window disappears. Ensure that wallpaper window
+ * gets ACTION_CANCEL.
+ * 1. foregroundWindow <-- has wallpaper (hasWallpaper=true)
+ * 2. wallpaperWindow <-- is wallpaper (type=InputWindowInfo::Type::WALLPAPER)
+ */
+TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> foregroundWindow =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ foregroundWindow->setHasWallpaper(true);
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ foregroundWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ foregroundWindow->consumeMotionMove();
+ wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Now the foreground window goes away, but the wallpaper stays
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ foregroundWindow->consumeMotionCancel();
+ // Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
+/**
+ * A single window that receives touch (on top), and a wallpaper window underneath it.
+ * The top window gets a multitouch gesture.
+ * Ensure that wallpaper gets the same gesture.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ window->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+
+ // Touch down on top window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both top window and its wallpaper should receive the touch down
+ window->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Second finger down on the top window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(150)
+ .y(150))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionPointerDown(1 /* pointerIndex */);
+ wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
+ expectedWallpaperFlags);
+ window->assertNoEvents();
+ wallpaperWindow->assertNoEvents();
+}
+
+/**
+ * Two windows: a window on the left and window on the right.
+ * A third window, wallpaper, is behind both windows, and spans both top windows.
+ * The first touch down goes to the left window. A second pointer touches down on the right window.
+ * The touch is split, so both left and right windows should receive ACTION_DOWN.
+ * The wallpaper will get the full event, so it should receive ACTION_DOWN followed by
+ * ACTION_POINTER_DOWN(1).
+ */
+TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+ leftWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
+ leftWindow->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+ rightWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
+ rightWindow->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+
+ // Touch down on left window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ leftWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Second finger down on the right window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(300)
+ .y(100))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ leftWindow->consumeMotionMove();
+ // Since the touch is split, right window gets ACTION_DOWN
+ rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
+ expectedWallpaperFlags);
+
+ // Now, leftWindow, which received the first finger, disappears.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightWindow, wallpaperWindow}}});
+ leftWindow->consumeMotionCancel();
+ // Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // The pointer that's still down on the right window moves, and goes to the right window only.
+ // As far as the dispatcher's concerned though, both pointers are still present.
+ const MotionEvent secondFingerMoveEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(310)
+ .y(110))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT));
+ rightWindow->consumeMotionMove();
+
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+ wallpaperWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft =
@@ -2301,11 +2496,21 @@
expectedDisplayId, expectedFlags);
}
+ void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE,
+ expectedDisplayId, expectedFlags);
+ }
+
void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP,
expectedDisplayId, expectedFlags);
}
+ void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+ expectedDisplayId, expectedFlags);
+ }
+
MotionEvent* consumeMotion() {
InputEvent* event = mInputReceiver->consume();
if (!event) {
@@ -2325,6 +2530,57 @@
std::unique_ptr<FakeInputReceiver> mInputReceiver;
};
+/**
+ * Two entities that receive touch: A window, and a global monitor.
+ * The touch goes to the window, and then the window disappears.
+ * The monitor does not get cancel right away. But if more events come in, the touch gets canceled
+ * for the monitor, as well.
+ * 1. foregroundWindow
+ * 2. monitor <-- global monitor (doesn't observe z order, receives all events)
+ */
+TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsCanceled) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "GlobalMonitor", ADISPLAY_ID_DEFAULT,
+ false /*isGestureMonitor*/);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both the foreground window and the global monitor should receive the touch down
+ window->consumeMotionDown();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionMove();
+ monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+
+ // Now the foreground window goes away
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ window->consumeMotionCancel();
+ monitor.assertNoEvents(); // Global monitor does not get a cancel yet
+
+ // If more events come in, there will be no more foreground window to send them to. This will
+ // cause a cancel for the monitor, as well.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {120, 200}))
+ << "Injection should fail because the window was removed";
+ window->assertNoEvents();
+ // Global monitor now gets the cancel
+ monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+}
+
// Tests for gesture monitors
TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -3226,7 +3482,8 @@
class InputFilterTest : public InputDispatcherTest {
protected:
- void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+ void testNotifyMotion(int32_t displayId, bool expectToBeFiltered,
+ const ui::Transform& transform = ui::Transform()) {
NotifyMotionArgs motionArgs;
motionArgs =
@@ -3237,7 +3494,8 @@
mDispatcher->notifyMotion(&motionArgs);
ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
- mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
+ const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue());
+ mFakePolicy->assertFilterInputEventWasCalled(motionArgs, xy);
} else {
mFakePolicy->assertFilterInputEventWasNotCalled();
}
@@ -3295,6 +3553,30 @@
testNotifyKey(/*expectToBeFiltered*/ false);
}
+// Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the
+// logical display coordinate space.
+TEST_F(InputFilterTest, MotionEvent_UsesLogicalDisplayCoordinates_notifyMotion) {
+ ui::Transform firstDisplayTransform;
+ firstDisplayTransform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set({-6.6, -5.5, -4.4, -3.3, -2.2, -1.1, 0, 0, 1});
+
+ std::vector<gui::DisplayInfo> displayInfos(2);
+ displayInfos[0].displayId = ADISPLAY_ID_DEFAULT;
+ displayInfos[0].transform = firstDisplayTransform;
+ displayInfos[1].displayId = SECOND_DISPLAY_ID;
+ displayInfos[1].transform = secondDisplayTransform;
+
+ mDispatcher->onWindowInfosChanged({}, displayInfos);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+
+ // Ensure the correct transforms are used for the displays.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform);
+}
+
class InputFilterInjectionPolicyTest : public InputDispatcherTest {
protected:
virtual void SetUp() override {
@@ -3359,8 +3641,7 @@
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- 0 /*INVALID_DISPLAY_SIZE*/, 0 /*INVALID_DISPLAY_SIZE*/, eventTime,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, eventTime,
eventTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
@@ -3567,7 +3848,7 @@
<< " event, got " << inputEventTypeToString(event->getType()) << " event";
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- EXPECT_EQ(expectedAction, motionEvent.getAction());
+ assertMotionAction(expectedAction, motionEvent.getAction());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
@@ -5471,4 +5752,134 @@
mSecondWindow->assertNoEvents();
}
+class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
+
+TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+ window->setInputFeatures(WindowInfo::Feature::DROP_INPUT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+ // With the flag set, window should not get any input
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->assertNoEvents();
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->assertNoEvents();
+
+ // With the flag cleared, the window should get input
+ window->setInputFeatures({});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) {
+ std::shared_ptr<FakeApplicationHandle> obscuringApplication =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> obscuringWindow =
+ new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow",
+ ADISPLAY_ID_DEFAULT);
+ obscuringWindow->setFrame(Rect(0, 0, 50, 50));
+ obscuringWindow->setOwnerInfo(111, 111);
+ obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+ window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+ window->setOwnerInfo(222, 222);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+ // With the flag set, window should not get any input
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->assertNoEvents();
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->assertNoEvents();
+
+ // With the flag cleared, the window should get input
+ window->setInputFeatures({});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) {
+ std::shared_ptr<FakeApplicationHandle> obscuringApplication =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> obscuringWindow =
+ new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow",
+ ADISPLAY_ID_DEFAULT);
+ obscuringWindow->setFrame(Rect(0, 0, 50, 50));
+ obscuringWindow->setOwnerInfo(111, 111);
+ obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+ window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+ window->setOwnerInfo(222, 222);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+ // With the flag set, window should not get any input
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->assertNoEvents();
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->assertNoEvents();
+
+ // When the window is no longer obscured because it went on top, it should get input
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}});
+
+ keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 5c430f5..b28c1e2 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -250,14 +250,35 @@
return mConfig.getDisplayViewportByPort(displayPort);
}
+ void addDisplayViewport(DisplayViewport viewport) {
+ mViewports.push_back(std::move(viewport));
+ mConfig.setDisplayViewports(mViewports);
+ }
+
void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
bool isActive, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType viewportType) {
- const DisplayViewport viewport =
- createDisplayViewport(displayId, width, height, orientation, isActive, uniqueId,
- physicalPort, viewportType);
- mViewports.push_back(viewport);
- mConfig.setDisplayViewports(mViewports);
+ std::optional<uint8_t> physicalPort, ViewportType type) {
+ const bool isRotated =
+ (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
+ DisplayViewport v;
+ v.displayId = displayId;
+ v.orientation = orientation;
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = isRotated ? height : width;
+ v.logicalBottom = isRotated ? width : height;
+ v.physicalLeft = 0;
+ v.physicalTop = 0;
+ v.physicalRight = isRotated ? height : width;
+ v.physicalBottom = isRotated ? width : height;
+ v.deviceWidth = isRotated ? height : width;
+ v.deviceHeight = isRotated ? width : height;
+ v.isActive = isActive;
+ v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
+ v.type = type;
+
+ addDisplayViewport(v);
}
bool updateViewport(const DisplayViewport& viewport) {
@@ -330,32 +351,6 @@
private:
uint32_t mNextPointerCaptureSequenceNumber = 0;
- DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, bool isActive,
- const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType type) {
- bool isRotated = (orientation == DISPLAY_ORIENTATION_90
- || orientation == DISPLAY_ORIENTATION_270);
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = 0;
- v.logicalTop = 0;
- v.logicalRight = isRotated ? height : width;
- v.logicalBottom = isRotated ? width : height;
- v.physicalLeft = 0;
- v.physicalTop = 0;
- v.physicalRight = isRotated ? height : width;
- v.physicalBottom = isRotated ? width : height;
- v.deviceWidth = isRotated ? height : width;
- v.deviceHeight = isRotated ? width : height;
- v.isActive = isActive;
- v.uniqueId = uniqueId;
- v.physicalPort = physicalPort;
- v.type = type;
- return v;
- }
-
void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
*outConfig = mConfig;
}
@@ -6296,6 +6291,172 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
}
+// --- TouchDisplayProjectionTest ---
+
+class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
+public:
+ // The values inside DisplayViewport are expected to be pre-rotated. This updates the current
+ // DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
+ // rotated equivalent of the given un-rotated physical display bounds.
+ void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) {
+ uint32_t inverseRotationFlags;
+ auto width = DISPLAY_WIDTH;
+ auto height = DISPLAY_HEIGHT;
+ switch (orientation) {
+ case DISPLAY_ORIENTATION_90:
+ inverseRotationFlags = ui::Transform::ROT_270;
+ std::swap(width, height);
+ break;
+ case DISPLAY_ORIENTATION_180:
+ inverseRotationFlags = ui::Transform::ROT_180;
+ break;
+ case DISPLAY_ORIENTATION_270:
+ inverseRotationFlags = ui::Transform::ROT_90;
+ std::swap(width, height);
+ break;
+ case DISPLAY_ORIENTATION_0:
+ inverseRotationFlags = ui::Transform::ROT_0;
+ break;
+ default:
+ FAIL() << "Invalid orientation: " << orientation;
+ }
+
+ const ui::Transform rotation(inverseRotationFlags, width, height);
+ const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay);
+
+ std::optional<DisplayViewport> internalViewport =
+ *mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ DisplayViewport& v = *internalViewport;
+ v.displayId = DISPLAY_ID;
+ v.orientation = orientation;
+
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = 100;
+ v.logicalBottom = 100;
+
+ v.physicalLeft = rotatedPhysicalDisplay.left;
+ v.physicalTop = rotatedPhysicalDisplay.top;
+ v.physicalRight = rotatedPhysicalDisplay.right;
+ v.physicalBottom = rotatedPhysicalDisplay.bottom;
+
+ v.deviceWidth = width;
+ v.deviceHeight = height;
+
+ v.isActive = true;
+ v.uniqueId = UNIQUE_ID;
+ v.type = ViewportType::INTERNAL;
+ mFakePolicy->updateViewport(v);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ }
+
+ void assertReceivedMove(const Point& point) {
+ NotifyMotionArgs motionArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], point.x, point.y,
+ 1, 0, 0, 0, 0, 0, 0, 0));
+ }
+};
+
+TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // Configure the DisplayViewport such that the logical display maps to a subsection of
+ // the display panel called the physical display. Here, the physical display is bounded by the
+ // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
+ static const Rect kPhysicalDisplay{10, 20, 70, 160};
+ static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{
+ {{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}};
+
+ for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+ DISPLAY_ORIENTATION_270}) {
+ configurePhysicalDisplay(orientation, kPhysicalDisplay);
+
+ // Touches outside the physical display should be ignored, and should not generate any
+ // events. Ensure touches at the following points that lie outside of the physical display
+ // area do not generate any events.
+ for (const auto& point : kPointsOutsidePhysicalDisplay) {
+ processDown(mapper, toRawX(point.x), toRawY(point.y));
+ processSync(mapper);
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled())
+ << "Unexpected event generated for touch outside physical display at point: "
+ << point.x << ", " << point.y;
+ }
+ }
+}
+
+TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // Configure the DisplayViewport such that the logical display maps to a subsection of
+ // the display panel called the physical display. Here, the physical display is bounded by the
+ // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
+ static const Rect kPhysicalDisplay{10, 20, 70, 160};
+
+ for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+ DISPLAY_ORIENTATION_270}) {
+ configurePhysicalDisplay(orientation, kPhysicalDisplay);
+
+ // Touches that start outside the physical display should be ignored until it enters the
+ // physical display bounds, at which point it should generate a down event. Start a touch at
+ // the point (5, 100), which is outside the physical display bounds.
+ static const Point kOutsidePoint{5, 100};
+ processDown(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y));
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Move the touch into the physical display area. This should generate a pointer down.
+ processMove(mapper, toRawX(11), toRawY(21));
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(motionArgs.pointerCoords[0], 11, 21, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // Move the touch inside the physical display area. This should generate a pointer move.
+ processMove(mapper, toRawX(69), toRawY(159));
+ processSync(mapper);
+ assertReceivedMove({69, 159});
+
+ // Move outside the physical display area. Since the pointer is already down, this should
+ // now continue generating events.
+ processMove(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y));
+ processSync(mapper);
+ assertReceivedMove(kOutsidePoint);
+
+ // Release. This should generate a pointer up.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], kOutsidePoint.x,
+ kOutsidePoint.y, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // Ensure no more events were generated.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+}
+
// --- MultiTouchInputMapperTest ---
class MultiTouchInputMapperTest : public TouchInputMapperTest {
@@ -8629,173 +8790,6 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}
-/**
- * Test touch should not work if outside of surface.
- */
-class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest {
-protected:
- void halfDisplayToCenterHorizontal(int32_t orientation) {
- std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
-
- // Half display to (width/4, 0, width * 3/4, height) to make display has offset.
- internalViewport->orientation = orientation;
- if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) {
- internalViewport->logicalLeft = 0;
- internalViewport->logicalTop = 0;
- internalViewport->logicalRight = DISPLAY_HEIGHT;
- internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
-
- internalViewport->physicalLeft = 0;
- internalViewport->physicalTop = DISPLAY_WIDTH / 4;
- internalViewport->physicalRight = DISPLAY_HEIGHT;
- internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4;
-
- internalViewport->deviceWidth = DISPLAY_HEIGHT;
- internalViewport->deviceHeight = DISPLAY_WIDTH;
- } else {
- internalViewport->logicalLeft = 0;
- internalViewport->logicalTop = 0;
- internalViewport->logicalRight = DISPLAY_WIDTH / 2;
- internalViewport->logicalBottom = DISPLAY_HEIGHT;
-
- internalViewport->physicalLeft = DISPLAY_WIDTH / 4;
- internalViewport->physicalTop = 0;
- internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4;
- internalViewport->physicalBottom = DISPLAY_HEIGHT;
-
- internalViewport->deviceWidth = DISPLAY_WIDTH;
- internalViewport->deviceHeight = DISPLAY_HEIGHT;
- }
-
- mFakePolicy->updateViewport(internalViewport.value());
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-
- void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xOutside, int32_t yOutside,
- int32_t xInside, int32_t yInside, int32_t xExpected,
- int32_t yExpected) {
- // touch on outside area should not work.
- processPosition(mapper, toRawX(xOutside), toRawY(yOutside));
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // touch on inside area should receive the event.
- NotifyMotionArgs args;
- processPosition(mapper, toRawX(xInside), toRawY(yInside));
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
- // Reset.
- mapper.reset(ARBITRARY_TIME);
- }
-};
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Touch on center of normal display should work.
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
- processPosition(mapper, toRawX(x), toRawY(y));
- processSync(mapper);
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 0.0f, 0.0f, 0.0f));
- // Reset.
- mapper.reset(ARBITRARY_TIME);
-
- // Let physical display be different to device, and make surface and physical could be 1:1 in
- // all four orientations.
- for (int orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
- halfDisplayToCenterHorizontal(orientation);
-
- const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
- const int32_t yExpected = y;
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
- }
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- // Since InputReader works in the un-rotated coordinate space, only devices that are not
- // orientation-aware are affected by display rotation.
- addConfigurationProperty("touch.orientationAware", "0");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
- halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90);
-
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
-
- // expect x/y = swap x/y then reverse x.
- constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
- constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- // Since InputReader works in the un-rotated coordinate space, only devices that are not
- // orientation-aware are affected by display rotation.
- addConfigurationProperty("touch.orientationAware", "0");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
- halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
-
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
-
- // expect x/y = swap x/y then reverse y.
- const int32_t xExpected = y;
- const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- // Since InputReader works in the un-rotated coordinate space, only devices that are not
- // orientation-aware are affected by display rotation.
- addConfigurationProperty("touch.orientationAware", "0");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- const int32_t x = 0;
- const int32_t y = 0;
-
- const int32_t xExpected = x;
- const int32_t yExpected = y;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected, yExpected);
-
- clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
- // expect x/y = swap x/y then reverse x.
- const int32_t xExpected90 = DISPLAY_HEIGHT - 1;
- const int32_t yExpected90 = x;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90);
-
- clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
- // expect x/y = swap x/y then reverse y.
- const int32_t xExpected270 = y;
- const int32_t yExpected270 = DISPLAY_WIDTH - 1;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270);
-}
-
TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
// we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
std::shared_ptr<FakePointerController> fakePointerController =
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index af86d09..fae16f6 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -32,7 +32,6 @@
: mService(service), mUid(uid), mMem(*mem),
mHalChannelHandle(halChannelHandle),
mOpPackageName(opPackageName), mDestroyed(false) {
- mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName);
mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -197,8 +196,8 @@
if (mService->isSensorInCappedSet(s.getType())) {
// Back up the rates that the app is allowed to have if the mic toggle is off
// This is used in the uncapRates() function.
- if (!mIsRateCappedBasedOnPermission ||
- requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
+ if ((requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) ||
+ !isRateCappedBasedOnPermission()) {
mMicRateBackup[handle] = requestedRateLevel;
} else {
mMicRateBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index a3f348b..d39a073 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
#define ANDROID_SENSOR_DIRECT_CONNECTION_H
+#include <optional>
#include <stdint.h>
#include <sys/types.h>
@@ -100,10 +101,19 @@
std::unordered_map<int, int> mActivatedBackup;
std::unordered_map<int, int> mMicRateBackup;
- std::atomic_bool mIsRateCappedBasedOnPermission;
mutable Mutex mDestroyLock;
bool mDestroyed;
userid_t mUserId;
+
+ std::optional<bool> mIsRateCappedBasedOnPermission;
+
+ bool isRateCappedBasedOnPermission() {
+ if (!mIsRateCappedBasedOnPermission.has_value()) {
+ mIsRateCappedBasedOnPermission =
+ mService->isRateCappedBasedOnPermission(mOpPackageName);
+ }
+ return mIsRateCappedBasedOnPermission.value();
+ }
};
} // namepsace android
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index c58e992..6948895 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -44,7 +44,6 @@
mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
mPackageName(packageName), mOpPackageName(opPackageName), mAttributionTag(attributionTag),
mTargetSdk(kTargetSdkUnknown), mDestroyed(false) {
- mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName);
mUserId = multiuser_get_user_id(mUid);
mChannel = new BitTube(mService->mSocketBufferSize);
#if DEBUG_CONNECTIONS
@@ -706,8 +705,8 @@
err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs,
reservedFlags, mOpPackageName);
if (err == OK && isSensorCapped) {
- if (!mIsRateCappedBasedOnPermission ||
- requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
+ !isRateCappedBasedOnPermission()) {
mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
} else {
mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
@@ -745,8 +744,8 @@
}
status_t ret = mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
if (ret == OK && isSensorCapped) {
- if (!mIsRateCappedBasedOnPermission ||
- requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
+ !isRateCappedBasedOnPermission()) {
mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
} else {
mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 909053b..6a98a40 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -18,6 +18,7 @@
#define ANDROID_SENSOR_EVENT_CONNECTION_H
#include <atomic>
+#include <optional>
#include <stdint.h>
#include <sys/types.h>
#include <unordered_map>
@@ -148,7 +149,6 @@
sp<SensorService> const mService;
sp<BitTube> mChannel;
uid_t mUid;
- std::atomic_bool mIsRateCappedBasedOnPermission;
mutable Mutex mConnectionLock;
// Number of events from wake up sensors which are still pending and haven't been delivered to
// the corresponding application. It is incremented by one unit for each write to the socket.
@@ -201,6 +201,16 @@
// Mapping of sensor handles to its rate before being capped by the mic toggle.
std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup;
userid_t mUserId;
+
+ std::optional<bool> mIsRateCappedBasedOnPermission;
+
+ bool isRateCappedBasedOnPermission() {
+ if (!mIsRateCappedBasedOnPermission.has_value()) {
+ mIsRateCappedBasedOnPermission
+ = mService->isRateCappedBasedOnPermission(mOpPackageName);
+ }
+ return mIsRateCappedBasedOnPermission.value();
+ }
};
} // namepsace android
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3081b1a..8c3a24f 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2190,10 +2190,10 @@
status_t SensorService::adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs,
const String16& opPackageName) {
uid_t uid = IPCThreadState::self()->getCallingUid();
- bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
if (*requestedPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
return OK;
}
+ bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
if (shouldCapBasedOnPermission) {
*requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
if (isPackageDebuggable(opPackageName)) {
@@ -2211,11 +2211,10 @@
status_t SensorService::adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel,
const String16& opPackageName) {
uid_t uid = IPCThreadState::self()->getCallingUid();
- bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
-
if (*requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
return OK;
}
+ bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
if (shouldCapBasedOnPermission) {
*requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
if (isPackageDebuggable(opPackageName)) {
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e4f642e..d51db88 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,7 +17,6 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "BufferQueueLayer"
@@ -171,7 +170,7 @@
expectedPresentTime = 0;
}
- for (int i = 1; i < mQueueItems.size(); i++) {
+ for (size_t i = 1; i < mQueueItems.size(); i++) {
const bool fenceSignaled =
mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
@@ -243,7 +242,7 @@
uint64_t lastSignaledFrameNumber = mLastFrameNumberReceived;
{
Mutex::Autolock lock(mQueueItemLock);
- for (int i = 0; i < mQueueItems.size(); i++) {
+ for (size_t i = 0; i < mQueueItems.size(); i++) {
bool fenceSignaled =
mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
@@ -629,4 +628,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 1ef8f78..e7b8995 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -82,10 +82,13 @@
return false;
}
- status_t err = token->linkToDeath(mDeathRecipient);
- if (err != NO_ERROR) {
- ALOGE("failed to cache buffer: could not link to death");
- return false;
+ // Only call linkToDeath if not a local binder
+ if (token->localBinder() == nullptr) {
+ status_t err = token->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("failed to cache buffer: could not link to death");
+ return false;
+ }
}
auto [itr, success] =
mBuffers.emplace(processToken,
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 2f8a90f..3397118 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -139,6 +139,34 @@
return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
}
+std::pair<gui::DisplayInfo, ui::Transform> DisplayDevice::getInputInfo() const {
+ gui::DisplayInfo info;
+ info.displayId = getLayerStack().id;
+
+ // The physical orientation is set when the orientation of the display panel is
+ // different than the default orientation of the device. Other services like
+ // InputFlinger do not know about this, so we do not need to expose the physical
+ // orientation of the panel outside of SurfaceFlinger.
+ const ui::Rotation inversePhysicalOrientation = ui::ROTATION_0 - mPhysicalOrientation;
+ auto width = getWidth();
+ auto height = getHeight();
+ if (inversePhysicalOrientation == ui::ROTATION_90 ||
+ inversePhysicalOrientation == ui::ROTATION_270) {
+ std::swap(width, height);
+ }
+ const ui::Transform undoPhysicalOrientation(ui::Transform::toRotationFlags(
+ inversePhysicalOrientation),
+ width, height);
+ const auto& displayTransform = undoPhysicalOrientation * getTransform();
+ // Send the inverse display transform to input so it can convert display coordinates to
+ // logical display.
+ info.transform = displayTransform.inverse();
+
+ info.logicalWidth = getLayerStackSpaceRect().width();
+ info.logicalHeight = getLayerStackSpaceRect().height();
+ return {info, displayTransform};
+}
+
// ----------------------------------------------------------------------------
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
mPowerMode = mode;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 9de578f..4a731bd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -167,6 +167,10 @@
return mDeviceProductInfo;
}
+ // Get the DisplayInfo that will be sent to InputFlinger, and the display transform that should
+ // be applied to all the input windows on the display.
+ std::pair<gui::DisplayInfo, ui::Transform> getInputInfo() const;
+
/* ------------------------------------------------------------------------
* Display power mode management.
*/
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 27146ab..e21b0da 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -283,8 +283,21 @@
return Error::NONE;
}
+bool Display::hasCapability(hal::DisplayCapability capability) const {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ if (mDisplayCapabilities) {
+ return mDisplayCapabilities->count(capability) > 0;
+ }
+
+ ALOGW("Can't query capability %d."
+ " Display Capabilities were not queried from HWC yet",
+ static_cast<int>(capability));
+
+ return false;
+}
+
Error Display::supportsDoze(bool* outSupport) const {
- *outSupport = mDisplayCapabilities.count(DisplayCapability::DOZE) > 0;
+ *outSupport = hasCapability(DisplayCapability::DOZE);
return Error::NONE;
}
@@ -447,17 +460,21 @@
auto error =
static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
if (error == Error::NONE) {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ mDisplayCapabilities.emplace();
for (auto capability : tmpCapabilities) {
- mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
+ mDisplayCapabilities->emplace(static_cast<DisplayCapability>(capability));
}
} else if (error == Error::UNSUPPORTED) {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ mDisplayCapabilities.emplace();
if (mCapabilities.count(Capability::SKIP_CLIENT_COLOR_TRANSFORM)) {
- mDisplayCapabilities.emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+ mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
}
bool dozeSupport = false;
error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
if (error == Error::NONE && dozeSupport) {
- mDisplayCapabilities.emplace(DisplayCapability::DOZE);
+ mDisplayCapabilities->emplace(DisplayCapability::DOZE);
}
}
});
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 871465d..a65efb2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -17,6 +17,7 @@
#pragma once
#include <android-base/expected.h>
+#include <android-base/thread_annotations.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/HdrCapabilities.h>
@@ -79,7 +80,7 @@
virtual hal::HWDisplayId getId() const = 0;
virtual bool isConnected() const = 0;
virtual void setConnected(bool connected) = 0; // For use by Device only
- virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0;
+ virtual bool hasCapability(hal::DisplayCapability) const = 0;
virtual bool isVsyncPeriodSwitchSupported() const = 0;
virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
@@ -175,7 +176,7 @@
hal::DisplayRequest* outDisplayRequests,
std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override;
hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
- hal::Error supportsDoze(bool* outSupport) const override;
+ hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex);
hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
hal::Dataspace* outDataspace,
@@ -214,9 +215,7 @@
hal::HWDisplayId getId() const override { return mId; }
bool isConnected() const override { return mIsConnected; }
void setConnected(bool connected) override; // For use by Device only
- const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override {
- return mDisplayCapabilities;
- };
+ bool hasCapability(hal::DisplayCapability) const override EXCLUDES(mDisplayCapabilitiesMutex);
bool isVsyncPeriodSwitchSupported() const override;
void onLayerDestroyed(hal::HWLayerId layerId) override;
@@ -243,8 +242,10 @@
using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>;
Layers mLayers;
+ mutable std::mutex mDisplayCapabilitiesMutex;
std::once_flag mDisplayCapabilityQueryFlag;
- std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
+ std::optional<std::unordered_set<hal::DisplayCapability>> mDisplayCapabilities
+ GUARDED_BY(mDisplayCapabilitiesMutex);
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 256bca9..c63ba0e 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -183,7 +183,7 @@
bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
hal::DisplayCapability capability) const {
RETURN_IF_INVALID_DISPLAY(displayId, false);
- return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
+ return mDisplayData.at(displayId).hwcDisplay->hasCapability(capability);
}
std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId,
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
index f0c5b58..7602e6d 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -33,6 +33,7 @@
void FlagManager::dump(std::string& result) const {
base::StringAppendF(&result, "FlagManager values: \n");
base::StringAppendF(&result, "demo_flag: %" PRId64 "\n", demo_flag());
+ base::StringAppendF(&result, "use_adpf_cpu_hint: %s\n", use_adpf_cpu_hint() ? "true" : "false");
}
namespace {
@@ -90,4 +91,10 @@
std::optional<int64_t> sysPropVal = std::nullopt;
return getValue("DemoFeature__demo_flag", sysPropVal, kDemoFlag);
}
+
+bool FlagManager::use_adpf_cpu_hint() const {
+ std::optional<bool> sysPropVal = std::nullopt;
+ return getValue("AdpfFeature__adpf_cpu_hint", sysPropVal, false);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
index 65e30a4..24d83a2 100644
--- a/services/surfaceflinger/FlagManager.h
+++ b/services/surfaceflinger/FlagManager.h
@@ -31,6 +31,8 @@
int64_t demo_flag() const;
+ bool use_adpf_cpu_hint() const;
+
private:
friend class FlagManagerTest;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f070d87..87afa22 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -135,6 +135,7 @@
mDrawingState.postTime = -1;
mDrawingState.destinationFrame.makeInvalid();
mDrawingState.isTrustedOverlay = false;
+ mDrawingState.dropInputMode = gui::DropInputMode::NONE;
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -1912,27 +1913,48 @@
}
Layer::RoundedCornerState Layer::getRoundedCornerState() const {
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- RoundedCornerState parentState = p->getRoundedCornerState();
- if (parentState.radius > 0) {
+ // Get parent settings
+ RoundedCornerState parentSettings;
+ const auto& parent = mDrawingParent.promote();
+ if (parent != nullptr) {
+ parentSettings = parent->getRoundedCornerState();
+ if (parentSettings.radius > 0) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
- parentState.cropRect = t.transform(parentState.cropRect);
+ parentSettings.cropRect = t.transform(parentSettings.cropRect);
// The rounded corners shader only accepts 1 corner radius for performance reasons,
// but a transform matrix can define horizontal and vertical scales.
// Let's take the average between both of them and pass into the shader, practically we
// never do this type of transformation on windows anyway.
auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
- parentState.radius *= (scaleX + scaleY) / 2.0f;
- return parentState;
+ parentSettings.radius *= (scaleX + scaleY) / 2.0f;
}
}
+
+ // Get layer settings
+ Rect layerCropRect = getCroppedBufferSize(getDrawingState());
const float radius = getDrawingState().cornerRadius;
- return radius > 0 && getCroppedBufferSize(getDrawingState()).isValid()
- ? RoundedCornerState(getCroppedBufferSize(getDrawingState()).toFloatRect(), radius)
- : RoundedCornerState();
+ RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
+
+ if (layerSettings.radius > 0 && parentSettings.radius > 0) {
+ // If the parent and the layer have rounded corner settings, use the parent settings if the
+ // parent crop is entirely inside the layer crop.
+ // This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
+ if (parentSettings.cropRect.left > layerCropRect.left &&
+ parentSettings.cropRect.top > layerCropRect.top &&
+ parentSettings.cropRect.right < layerCropRect.right &&
+ parentSettings.cropRect.bottom < layerCropRect.bottom) {
+ return parentSettings;
+ } else {
+ return layerSettings;
+ }
+ } else if (layerSettings.radius > 0) {
+ return layerSettings;
+ } else if (parentSettings.radius > 0) {
+ return parentSettings;
+ }
+ return {};
}
void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
@@ -2132,7 +2154,7 @@
if (traceFlags & SurfaceTracing::TRACE_INPUT) {
WindowInfo info;
if (useDrawing) {
- info = fillInputInfo(nullptr);
+ info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true);
} else {
info = state.inputInfo;
}
@@ -2266,7 +2288,75 @@
}
}
-WindowInfo Layer::fillInputInfo(const DisplayDevice* display) {
+gui::DropInputMode Layer::getDropInputMode() const {
+ gui::DropInputMode mode = mDrawingState.dropInputMode;
+ if (mode == gui::DropInputMode::ALL) {
+ return mode;
+ }
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent) {
+ gui::DropInputMode parentMode = parent->getDropInputMode();
+ if (parentMode != gui::DropInputMode::NONE) {
+ return parentMode;
+ }
+ }
+ return mode;
+}
+
+void Layer::handleDropInputMode(gui::WindowInfo& info) const {
+ if (mDrawingState.inputInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
+ return;
+ }
+
+ // Check if we need to drop input unconditionally
+ gui::DropInputMode dropInputMode = getDropInputMode();
+ if (dropInputMode == gui::DropInputMode::ALL) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ ALOGV("Dropping input for %s as requested by policy.", getDebugName());
+ return;
+ }
+
+ // Check if we need to check if the window is obscured by parent
+ if (dropInputMode != gui::DropInputMode::OBSCURED) {
+ return;
+ }
+
+ // Check if the parent has set an alpha on the layer
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent && parent->getAlpha() != 1.0_hf) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
+ static_cast<float>(getAlpha()));
+ }
+
+ // Check if the parent has cropped the buffer
+ Rect bufferSize = getCroppedBufferSize(getDrawingState());
+ if (!bufferSize.isValid()) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+ return;
+ }
+
+ // Screenbounds are the layer bounds cropped by parents, transformed to screenspace.
+ // To check if the layer has been cropped, we take the buffer bounds, apply the local
+ // layer crop and apply the same set of transforms to move to screenspace. If the bounds
+ // match then the layer has not been cropped by its parents.
+ Rect bufferInScreenSpace(getTransform().transform(bufferSize));
+ bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds};
+
+ if (croppedByParent) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
+ getDebugName());
+ } else {
+ // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop
+ // input if the window is obscured. This check should be done in surfaceflinger but the
+ // logic currently resides in inputflinger. So pass the if_obscured check to input to only
+ // drop input events if the window is obscured.
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+ }
+}
+
+WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
mDrawingState.inputInfo.ownerUid = mOwnerUid;
@@ -2280,35 +2370,6 @@
info.id = sequence;
info.displayId = getLayerStack().id;
- // Transform that maps from LayerStack space to display space, e.g. rotated to non-rotated.
- // Used when InputFlinger operates in display space.
- ui::Transform displayTransform;
- if (display) {
- // The physical orientation is set when the orientation of the display panel is different
- // than the default orientation of the device. Other services like InputFlinger do not know
- // about this, so we do not need to expose the physical orientation of the panel outside of
- // SurfaceFlinger.
- const ui::Rotation inversePhysicalOrientation =
- ui::ROTATION_0 - display->getPhysicalOrientation();
- auto width = display->getWidth();
- auto height = display->getHeight();
- if (inversePhysicalOrientation == ui::ROTATION_90 ||
- inversePhysicalOrientation == ui::ROTATION_270) {
- std::swap(width, height);
- }
- const ui::Transform undoPhysicalOrientation(ui::Transform::toRotationFlags(
- inversePhysicalOrientation),
- width, height);
- displayTransform = undoPhysicalOrientation * display->getTransform();
-
- // Send the inverse of the display orientation so that input can transform points back to
- // the rotated display space.
- const ui::Rotation inverseOrientation = ui::ROTATION_0 - display->getOrientation();
- info.displayOrientation = ui::Transform::toRotationFlags(inverseOrientation);
-
- info.displayWidth = width;
- info.displayHeight = height;
- }
fillInputFrameInfo(info, displayTransform);
// For compatibility reasons we let layers which can receive input
@@ -2322,6 +2383,13 @@
info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
info.alpha = getAlpha();
fillTouchOcclusionMode(info);
+ handleDropInputMode(info);
+
+ // If the window will be blacked out on a display because the display does not have the secure
+ // flag and the layer has the secure flag set, then drop input.
+ if (!displayIsSecure && isSecure()) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ }
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
@@ -2570,6 +2638,14 @@
return handle->owner;
}
+bool Layer::setDropInputMode(gui::DropInputMode mode) {
+ if (mDrawingState.dropInputMode == mode) {
+ return false;
+ }
+ mDrawingState.dropInputMode = mode;
+ return true;
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4200be4..8c281d5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -17,6 +17,7 @@
#pragma once
+#include <android/gui/DropInputMode.h>
#include <compositionengine/LayerFE.h>
#include <gui/BufferQueue.h>
#include <gui/ISurfaceComposerClient.h>
@@ -277,6 +278,8 @@
Rect destinationFrame;
sp<IBinder> releaseBufferEndpoint;
+
+ gui::DropInputMode dropInputMode;
};
/*
@@ -442,6 +445,8 @@
virtual bool setFrameRateSelectionPriority(int32_t priority);
virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
virtual void setAutoRefresh(bool /* autoRefresh */) {}
+ bool setDropInputMode(gui::DropInputMode);
+
// If the variable is not set on the layer, it traverses up the tree to inherit the frame
// rate priority from its parent.
virtual int32_t getFrameRateSelectionPriority() const;
@@ -601,10 +606,8 @@
virtual bool getTransformToDisplayInverse() const { return false; }
// Returns how rounded corners should be drawn for this layer.
- // This will traverse the hierarchy until it reaches its root, finding topmost rounded
- // corner definition and converting it into current layer's coordinates.
- // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
- // ignored.
+ // A layer can override its parent's rounded corner settings if the parent's rounded
+ // corner crop does not intersect with its own rounded corner crop.
virtual RoundedCornerState getRoundedCornerState() const;
bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
@@ -851,7 +854,7 @@
bool getPremultipledAlpha() const;
void setInputInfo(const gui::WindowInfo& info);
- gui::WindowInfo fillInputInfo(const DisplayDevice*);
+ gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure);
/**
* Returns whether this layer has an explicitly set input-info.
@@ -1060,6 +1063,8 @@
bool setFrameRateForLayerTree(FrameRate);
void setZOrderRelativeOf(const wp<Layer>& relativeOf);
bool isTrustedOverlay() const;
+ gui::DropInputMode getDropInputMode() const;
+ void handleDropInputMode(gui::WindowInfo& info) const;
// Find the root of the cloned hierarchy, this means the first non cloned parent.
// This will return null if first non cloned parent is not found.
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 0789e8d..ec81e63 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -30,6 +30,8 @@
#include <SkRect.h>
#include <SkSurface.h>
#include <gui/IProducerListener.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
#undef LOG_TAG
#define LOG_TAG "RefreshRateOverlay"
@@ -190,35 +192,30 @@
}
bool RefreshRateOverlay::createLayer() {
- int32_t layerId;
- const status_t ret =
- mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
- SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
- &mIBinder, &mGbp, nullptr, &layerId);
- if (ret) {
+ mSurfaceControl =
+ SurfaceComposerClient::getDefault()
+ ->createSurface(String8("RefreshRateOverlay"), SevenSegmentDrawer::getWidth(),
+ SevenSegmentDrawer::getHeight(), PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+
+ if (!mSurfaceControl) {
ALOGE("failed to create buffer state layer");
return false;
}
- mLayer = mClient->getLayerUser(mIBinder);
- mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote));
- mLayer->setIsAtRoot(true);
-
- // setting Layer's Z requires resorting layersSortedByZ
- ssize_t idx = mFlinger.mDrawingState.layersSortedByZ.indexOf(mLayer);
- if (mLayer->setLayer(INT32_MAX - 2) && idx >= 0) {
- mFlinger.mDrawingState.layersSortedByZ.removeAt(idx);
- mFlinger.mDrawingState.layersSortedByZ.add(mLayer);
- }
+ SurfaceComposerClient::Transaction t;
+ t.setFrameRate(mSurfaceControl, 0.0f,
+ static_cast<int8_t>(Layer::FrameRateCompatibility::NoVote),
+ static_cast<int8_t>(scheduler::Seamlessness::OnlySeamless));
+ t.setLayer(mSurfaceControl, INT32_MAX - 2);
+ t.apply();
return true;
}
-const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
-RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
- ui::Transform::RotationFlags transformHint = mLayer->getTransformHint();
+const std::vector<sp<GraphicBuffer>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
+ ui::Transform::RotationFlags transformHint =
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
// Tell SurfaceFlinger about the pre-rotation on the buffer.
const auto transform = [&] {
switch (transformHint) {
@@ -230,7 +227,10 @@
return ui::Transform::ROT_0;
}
}();
- mLayer->setTransform(transform);
+
+ SurfaceComposerClient::Transaction t;
+ t.setTransform(mSurfaceControl, transform);
+ t.apply();
if (mBufferCache.find(transformHint) == mBufferCache.end() ||
mBufferCache.at(transformHint).find(fps) == mBufferCache.at(transformHint).end()) {
@@ -249,16 +249,7 @@
colorBase.fA = ALPHA;
SkColor color = colorBase.toSkColor();
auto buffers = SevenSegmentDrawer::draw(fps, color, transformHint, mShowSpinner);
- std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
- std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
- [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
- return std::make_shared<
- renderengine::ExternalTexture>(buffer,
- mFlinger.getRenderEngine(),
- renderengine::ExternalTexture::
- Usage::READABLE);
- });
- mBufferCache[transformHint].emplace(fps, textures);
+ mBufferCache[transformHint].emplace(fps, buffers);
}
return mBufferCache[transformHint][fps];
@@ -271,30 +262,26 @@
Rect frame((3 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
- layer_state_t::matrix22_t matrix;
- matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
- matrix.dtdx = 0;
- matrix.dtdy = 0;
- matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight());
- mLayer->setMatrix(matrix, true);
- mLayer->setPosition(frame.left, frame.top);
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ SurfaceComposerClient::Transaction t;
+ t.setMatrix(mSurfaceControl,
+ frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth()), 0, 0,
+ frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight()));
+ t.setPosition(mSurfaceControl, frame.left, frame.top);
+ t.apply();
}
void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
- mLayer->setLayerStack(stack);
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ SurfaceComposerClient::Transaction t;
+ t.setLayerStack(mSurfaceControl, stack);
+ t.apply();
}
void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
mCurrentFps = fps.getIntValue();
auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
- mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
- std::nullopt /* dequeueTime */, FrameTimelineInfo{},
- nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
-
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ SurfaceComposerClient::Transaction t;
+ t.setBuffer(mSurfaceControl, buffer);
+ t.apply();
}
void RefreshRateOverlay::onInvalidate() {
@@ -303,12 +290,9 @@
const auto& buffers = getOrCreateBuffers(*mCurrentFps);
mFrame = (mFrame + 1) % buffers.size();
auto buffer = buffers[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
- mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
- std::nullopt /* dequeueTime */, FrameTimelineInfo{},
- nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
-
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ SurfaceComposerClient::Transaction t;
+ t.setBuffer(mSurfaceControl, buffer);
+ t.apply();
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 63ae383..354510a 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -37,6 +37,7 @@
class IGraphicBufferProducer;
class Layer;
class SurfaceFlinger;
+class SurfaceControl;
class RefreshRateOverlay {
public:
@@ -70,18 +71,16 @@
};
bool createLayer();
- const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& getOrCreateBuffers(
- uint32_t fps);
+
+ const std::vector<sp<GraphicBuffer>>& getOrCreateBuffers(uint32_t fps);
SurfaceFlinger& mFlinger;
const sp<Client> mClient;
- sp<Layer> mLayer;
sp<IBinder> mIBinder;
sp<IGraphicBufferProducer> mGbp;
- std::unordered_map<
- ui::Transform::RotationFlags,
- std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>>
+ std::unordered_map<ui::Transform::RotationFlags,
+ std::unordered_map<int, std::vector<sp<GraphicBuffer>>>>
mBufferCache;
std::optional<int> mCurrentFps;
int mFrame = 0;
@@ -94,6 +93,8 @@
// Interpolate the colors between these values.
const uint32_t mLowFps;
const uint32_t mHighFps;
+
+ sp<SurfaceControl> mSurfaceControl;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index e922d46..3f6bd6f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -133,27 +133,10 @@
return true;
}
-float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
- const RefreshRate& refreshRate,
- bool isSeamlessSwitch) const {
- if (!isVoteAllowed(layer, refreshRate)) {
- return 0;
- }
-
+float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(
+ const LayerRequirement& layer, const RefreshRate& refreshRate) const {
constexpr float kScoreForFractionalPairs = .8f;
- // Slightly prefer seamless switches.
- constexpr float kSeamedSwitchPenalty = 0.95f;
- const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
-
- // If the layer wants Max, give higher score to the higher refresh rate
- if (layer.vote == LayerVoteType::Max) {
- const auto ratio = refreshRate.getFps().getValue() /
- mAppRequestRefreshRates.back()->getFps().getValue();
- // use ratio^2 to get a lower score the more we get further from peak
- return ratio * ratio;
- }
-
const auto displayPeriod = refreshRate.getVsyncPeriod();
const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
if (layer.vote == LayerVoteType::ExplicitDefault) {
@@ -178,7 +161,7 @@
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) {
- return kScoreForFractionalPairs * seamlessness;
+ return kScoreForFractionalPairs;
}
// Calculate how many display vsyncs we need to present a single frame for this
@@ -188,7 +171,7 @@
static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
if (displayFramesRemainder == 0) {
// Layer desired refresh rate matches the display rate.
- return 1.0f * seamlessness;
+ return 1.0f;
}
if (displayFramesQuotient == 0) {
@@ -206,7 +189,29 @@
iter++;
}
- return (1.0f / iter) * seamlessness;
+ return (1.0f / iter);
+ }
+
+ return 0;
+}
+
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
+ const RefreshRate& refreshRate,
+ bool isSeamlessSwitch) const {
+ if (!isVoteAllowed(layer, refreshRate)) {
+ return 0;
+ }
+
+ // Slightly prefer seamless switches.
+ constexpr float kSeamedSwitchPenalty = 0.95f;
+ const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+ // If the layer wants Max, give higher score to the higher refresh rate
+ if (layer.vote == LayerVoteType::Max) {
+ const auto ratio = refreshRate.getFps().getValue() /
+ mAppRequestRefreshRates.back()->getFps().getValue();
+ // use ratio^2 to get a lower score the more we get further from peak
+ return ratio * ratio;
}
if (layer.vote == LayerVoteType::ExplicitExact) {
@@ -221,7 +226,18 @@
return divider == 1;
}
- return 0;
+ // If the layer frame rate is a divider of the refresh rate it should score
+ // the highest score.
+ if (getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+ return 1.0f * seamlessness;
+ }
+
+ // The layer frame rate is not a divider of the refresh rate,
+ // there is a small penalty attached to the score to favor the frame rates
+ // the exactly matches the display refresh rate or a multiple.
+ constexpr float kNonExactMatchingPenalty = 0.99f;
+ return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
+ kNonExactMatchingPenalty;
}
struct RefreshRateScore {
@@ -330,18 +346,28 @@
const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
+ const Policy* policy = getCurrentPolicyLocked();
+ const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
+ // If the default mode group is different from the group of current mode,
+ // this means a layer requesting a seamed mode switch just disappeared and
+ // we should switch back to the default group.
+ // However if a seamed layer is still present we anchor around the group
+ // of the current mode, in order to prevent unnecessary seamed mode switches
+ // (e.g. when pausing a video playback).
+ const auto anchorGroup = seamedFocusedLayers > 0 ? mCurrentRefreshRate->getModeGroup()
+ : defaultMode->getModeGroup();
+
// Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
// selected a refresh rate to see if we should apply touch boost.
if (globalSignals.touch && !hasExplicitVoteLayers) {
ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
setTouchConsidered();
- return getMaxRefreshRateByPolicyLocked();
+ return getMaxRefreshRateByPolicyLocked(anchorGroup);
}
// If the primary range consists of a single refresh rate then we can only
// move out the of range if layers explicitly request a different refresh
// rate.
- const Policy* policy = getCurrentPolicyLocked();
const bool primaryRangeIsSingleRate =
policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max);
@@ -353,7 +379,9 @@
}
if (layers.empty() || noVoteLayers == layers.size()) {
- return getMaxRefreshRateByPolicyLocked();
+ const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+ ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str());
+ return refreshRate;
}
// Only if all layers want Min we should return Min
@@ -370,8 +398,6 @@
scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
}
- const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
-
for (const auto& layer : layers) {
ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
layerVoteTypeString(layer.vote).c_str(), layer.weight,
@@ -409,10 +435,7 @@
// mode group otherwise. In second case, if the current mode group is different
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
- const bool isInPolicyForDefault = seamedFocusedLayers > 0
- ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
- : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
-
+ const bool isInPolicyForDefault = scores[i].refreshRate->getModeGroup() == anchorGroup;
if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
scores[i].refreshRate->toString().c_str(),
@@ -451,9 +474,9 @@
// range instead of picking a random score from the app range.
if (std::all_of(scores.begin(), scores.end(),
[](RefreshRateScore score) { return score.score == 0; })) {
- ALOGV("layers not scored - choose %s",
- getMaxRefreshRateByPolicyLocked().getName().c_str());
- return getMaxRefreshRateByPolicyLocked();
+ const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+ ALOGV("layers not scored - choose %s", refreshRate.getName().c_str());
+ return refreshRate;
} else {
return *bestRefreshRate;
}
@@ -463,7 +486,7 @@
// interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
// vote we should not change it if we get a touch event. Only apply touch boost if it will
// actually increase the refresh rate over the normal selection.
- const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
+ const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
const bool touchBoostForExplicitExact = [&] {
if (mSupportsFrameRateOverride) {
@@ -646,10 +669,10 @@
return getMaxRefreshRateByPolicyLocked();
}
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
const auto& refreshRate = (**it);
- if (mCurrentRefreshRate->getModeGroup() == refreshRate.getModeGroup()) {
+ if (anchorGroup == refreshRate.getModeGroup()) {
return refreshRate;
}
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0d75689..ffaa029 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -388,7 +388,11 @@
// Returns the highest refresh rate according to the current policy. May change at runtime. Only
// uses the primary range, not the app request range.
- const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock);
+ const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
+ return getMaxRefreshRateByPolicyLocked(mCurrentRefreshRate->getModeGroup());
+ }
+
+ const RefreshRate& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
// Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
// the policy.
@@ -405,6 +409,9 @@
float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
bool isSeamlessSwitch) const REQUIRES(mLock);
+ float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&,
+ const RefreshRate&) const REQUIRES(mLock);
+
void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
// The list of refresh rates, indexed by display modes ID. This may change after this
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 34b5cdc..88715e3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -169,6 +169,7 @@
using android::hardware::power::Boost;
using base::StringAppendF;
+using gui::DisplayInfo;
using gui::IWindowInfosListener;
using gui::WindowInfo;
using ui::ColorMode;
@@ -638,6 +639,12 @@
return displayIds;
}
+status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const {
+ Mutex::Autolock lock(mStateLock);
+ *id = getInternalDisplayIdLocked();
+ return NO_ERROR;
+}
+
sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
Mutex::Autolock lock(mStateLock);
return getPhysicalDisplayTokenLocked(displayId);
@@ -1709,7 +1716,15 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- ATRACE_CALL();
+ const std::string tracePeriod = [vsyncPeriod]() {
+ if (ATRACE_ENABLED() && vsyncPeriod) {
+ std::stringstream ss;
+ ss << "(" << *vsyncPeriod << ")";
+ return ss.str();
+ }
+ return std::string();
+ }();
+ ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
Mutex::Autolock lock(mStateLock);
const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
@@ -2715,6 +2730,7 @@
display->setProjection(state.orientation, state.layerStackSpaceRect,
state.orientedDisplaySpaceRect);
display->setDisplayName(state.displayName);
+ display->setFlags(state.flags);
return display;
}
@@ -3059,20 +3075,35 @@
void SurfaceFlinger::notifyWindowInfos() {
std::vector<WindowInfo> windowInfos;
+ std::vector<DisplayInfo> displayInfos;
+ std::unordered_map<const DisplayDevice*, const ui::Transform> displayTransforms;
+
+ if (enablePerWindowInputRotation()) {
+ for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+ const auto& [info, transform] = display->getInputInfo();
+ displayInfos.emplace_back(info);
+ displayTransforms.emplace(display.get(), transform);
+ }
+ }
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- const DisplayDevice* display = nullptr;
- if (enablePerWindowInputRotation()) {
- display = ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)).get();
- }
+ const DisplayDevice* display = ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)).get();
+ ui::Transform displayTransform = ui::Transform();
- // When calculating the screen bounds we ignore the transparent region since it may
- // result in an unwanted offset.
- windowInfos.push_back(layer->fillInputInfo(display));
+ if (enablePerWindowInputRotation()) {
+ // When calculating the screen bounds we ignore the transparent region since it may
+ // result in an unwanted offset.
+ const auto it = displayTransforms.find(display);
+ if (it != displayTransforms.end()) {
+ displayTransform = it->second;
+ }
+ }
+ const bool displayIsSecure = !display || display->isSecure();
+ windowInfos.push_back(layer->fillInputInfo(displayTransform, displayIsSecure));
});
- mWindowInfosListenerInvoker->windowInfosChanged(windowInfos,
+ mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos,
mInputWindowCommands.syncInputWindows);
}
@@ -4134,6 +4165,16 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eDropInputModeChanged) {
+ if (privileged) {
+ if (layer->setDropInputMode(s.dropInputMode)) {
+ flags |= eTraversalNeeded;
+ mInputInfoChanged = true;
+ }
+ } else {
+ ALOGE("Attempt to update DropInputMode without permission ACCESS_SURFACE_FLINGER");
+ }
+ }
// This has to happen after we reparent children because when we reparent to null we remove
// child layers from current state and remove its relative z. If the children are reparented in
// the same transaction, then we have to make sure we reparent the children first so we do not
@@ -5133,7 +5174,8 @@
continue;
}
- StringAppendF(&result, "Display %s HWC layers:\n", to_string(*displayId).c_str());
+ StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
+ (isDisplayActiveLocked(display) ? "active" : "inactive"));
Layer::miniDumpHeader(result);
const DisplayDevice& ref = *display;
@@ -5230,6 +5272,7 @@
case REMOVE_TUNNEL_MODE_ENABLED_LISTENER:
case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
+ case GET_PRIMARY_PHYSICAL_DISPLAY_ID:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
// ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
// which acquire the necessary permission dynamically. Don't use the permission cache
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9dedfa7..141d7ac 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -615,6 +615,8 @@
Mutex::Autolock lock(mStateLock);
return getPhysicalDisplayIdsLocked();
}
+ status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override EXCLUDES(mStateLock);
+
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
const Vector<ComposerState>& state,
@@ -1014,7 +1016,7 @@
sp<DisplayDevice> getDisplayWithInputByLayer(Layer* layer) const REQUIRES(mStateLock);
- bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) REQUIRES(mStateLock) {
+ bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
return display->getDisplayToken() == mActiveDisplayToken;
}
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 4a69c8f..e15eae8 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -34,6 +34,8 @@
using android::hardware::graphics::common::V1_2::PixelFormat;
using android::ui::DisplayPrimaries;
+// Keep logic in sync with WindowManagerService functions that query SurfaceFlinger properties.
+// Consider exposing properties via ISurfaceComposer instead.
int64_t vsync_event_phase_offset_ns(int64_t defaultValue) {
auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns();
if (temp.has_value()) {
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index dc2aa58..b93d127 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -21,6 +21,7 @@
namespace android {
+using gui::DisplayInfo;
using gui::IWindowInfosListener;
using gui::WindowInfo;
@@ -67,6 +68,7 @@
}
void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+ const std::vector<DisplayInfo>& displayInfos,
bool shouldSync) {
std::unordered_set<sp<IWindowInfosListener>, ISurfaceComposer::SpHash<IWindowInfosListener>>
windowInfosListeners;
@@ -81,7 +83,7 @@
mCallbacksPending = windowInfosListeners.size();
for (const auto& listener : windowInfosListeners) {
- listener->onWindowInfosChanged(windowInfos,
+ listener->onWindowInfosChanged(windowInfos, displayInfos,
shouldSync ? mWindowInfosReportedListener : nullptr);
}
}
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index 5e5796f..ecd797a 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -33,7 +33,8 @@
void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
- void windowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, bool shouldSync);
+ void windowInfosChanged(const std::vector<gui::WindowInfo>&,
+ const std::vector<gui::DisplayInfo>&, bool shouldSync);
protected:
void binderDied(const wp<IBinder>& who) override;
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 32ad873..7b86229 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -60,7 +60,7 @@
"android.hardware.graphics.composer@2.1",
],
shared_libs: [
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.common@1.2",
"libandroid",
"libbase",
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 4076253..e5872c1 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -465,6 +465,95 @@
}
}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, ChildCornerRadiusTakesPrecedence) {
+ sp<SurfaceControl> parent;
+ sp<SurfaceControl> child;
+ const uint32_t size = 64;
+ const uint32_t parentSize = size * 3;
+ const uint32_t testLength = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setCornerRadius(child, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, size, size)
+ .apply();
+
+ {
+ const uint32_t top = size - 1;
+ const uint32_t left = size - 1;
+ const uint32_t bottom = size * 2 - 1;
+ const uint32_t right = size * 2 - 1;
+ auto shot = getScreenCapture();
+ // Edges are transparent
+ // TL
+ shot->expectColor(Rect(left, top, testLength, testLength), Color::RED);
+ // TR
+ shot->expectColor(Rect(right - testLength, top, right, testLength), Color::RED);
+ // BL
+ shot->expectColor(Rect(left, bottom - testLength, testLength, bottom - testLength),
+ Color::RED);
+ // BR
+ shot->expectColor(Rect(right - testLength, bottom - testLength, right, bottom), Color::RED);
+ // Solid center
+ shot->expectColor(Rect(parentSize / 2 - testLength / 2, parentSize / 2 - testLength / 2,
+ parentSize / 2 + testLength / 2, parentSize / 2 + testLength / 2),
+ Color::GREEN);
+ }
+}
+
+// Test if ParentCornerRadiusTakesPrecedence if the parent corner radius crop is fully contained by
+// the child corner radius crop.
+TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusTakesPrecedence) {
+ sp<SurfaceControl> parent;
+ sp<SurfaceControl> child;
+ const uint32_t size = 64;
+ const uint32_t parentSize = size * 3;
+ const Rect parentCrop(size + 1, size + 1, size * 2 - 1, size * 2 - 1);
+ const uint32_t testLength = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setCrop(parent, parentCrop)
+ .setCornerRadius(child, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, size, size)
+ .apply();
+
+ {
+ const uint32_t top = size - 1;
+ const uint32_t left = size - 1;
+ const uint32_t bottom = size * 2 - 1;
+ const uint32_t right = size * 2 - 1;
+ auto shot = getScreenCapture();
+ // Edges are transparent
+ // TL
+ shot->expectColor(Rect(left, top, testLength, testLength), Color::BLACK);
+ // TR
+ shot->expectColor(Rect(right - testLength, top, right, testLength), Color::BLACK);
+ // BL
+ shot->expectColor(Rect(left, bottom - testLength, testLength, bottom - testLength),
+ Color::BLACK);
+ // BR
+ shot->expectColor(Rect(right - testLength, bottom - testLength, right, bottom),
+ Color::BLACK);
+ // Solid center
+ shot->expectColor(Rect(parentSize / 2 - testLength / 2, parentSize / 2 - testLength / 2,
+ parentSize / 2 + testLength / 2, parentSize / 2 + testLength / 2),
+ Color::GREEN);
+ }
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
if (!deviceSupportsBlurs()) GTEST_SKIP();
if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index de116f2..0069111 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -22,6 +22,7 @@
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
+using gui::DisplayInfo;
using gui::WindowInfo;
class WindowInfosListenerTest : public ::testing::Test {
@@ -40,7 +41,8 @@
struct SyncWindowInfosListener : public gui::WindowInfosListener {
public:
- void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override {
+ void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+ const std::vector<DisplayInfo>&) override {
windowInfosPromise.set_value(windowInfos);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 0f1cc67..8195ee7 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -233,7 +233,7 @@
// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
// 3) GpuVirtualDisplayIdType for virtual display without HWC backing.
template <typename DisplayIdType, int width, int height, Critical critical, Async async,
- Secure secure, Primary primary, int grallocUsage>
+ Secure secure, Primary primary, int grallocUsage, int displayFlags>
struct DisplayVariant {
using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
@@ -261,6 +261,8 @@
// Whether the display is primary
static constexpr Primary PRIMARY = primary;
+ static constexpr int DISPLAY_FLAGS = displayFlags;
+
static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
ceDisplayArgs.setId(DISPLAY_ID::get())
@@ -469,16 +471,19 @@
constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
+constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1;
+
template <typename PhysicalDisplay, int width, int height, Critical critical>
struct PhysicalDisplayVariant
: DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
- GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
- DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
- critical, Async::FALSE, Secure::TRUE,
- PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- PhysicalDisplay> {};
+ GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
+ HwcDisplayVariant<
+ PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+ DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
+ Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
+ PhysicalDisplay> {};
template <bool hasIdentificationData>
struct PrimaryDisplay {
@@ -520,13 +525,16 @@
// A virtual display not supported by the HWC.
constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
+constexpr int VIRTUAL_DISPLAY_FLAGS = 0x0;
+
template <int width, int height, Secure secure>
struct NonHwcVirtualDisplayVariant
: DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
- using Base =
- DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+ Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY,
+ VIRTUAL_DISPLAY_FLAGS> {
+ using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>;
static void injectHwcDisplay(DisplayTransactionTest*) {}
@@ -569,13 +577,16 @@
template <int width, int height, Secure secure>
struct HwcVirtualDisplayVariant
: DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
- HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
- DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
- Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
- GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+ secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY,
+ VIRTUAL_DISPLAY_FLAGS>,
+ HwcDisplayVariant<
+ HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+ DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>> {
using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
- Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
+ Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER,
+ VIRTUAL_DISPLAY_FLAGS>;
using Self = HwcVirtualDisplayVariant<width, height, secure>;
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index a6bfde7..a8e3e5e 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -175,7 +175,6 @@
RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)};
RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)};
-private:
DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
ui::Size resolution = ui::Size());
};
@@ -349,10 +348,19 @@
EXPECT_EQ(mExpected90Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
- 0);
+ ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+ NO_ERROR);
EXPECT_EQ(mExpected60Config,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+ // We select max even when this will cause a non-seamless switch.
+ refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_90, /*allowGroupSwitching*/ true, {Fps(0), Fps(90)}}),
+ NO_ERROR);
+ EXPECT_EQ(mExpected90DifferentGroupConfig,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
@@ -2170,6 +2178,44 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) {
+ constexpr int kMinRefreshRate = 10;
+ constexpr int kMaxRefreshRate = 240;
+
+ DisplayModes displayModes;
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ constexpr int32_t kGroup = 0;
+ const auto refreshRate = Fps(static_cast<float>(fps));
+ displayModes.push_back(
+ createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs()));
+ }
+
+ const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(displayModes,
+ /*currentConfigId=*/displayModes[0]->getId());
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
+ layers[0].desiredRefreshRate = fps;
+ layers[0].vote = vote;
+ EXPECT_EQ(fps.getIntValue(),
+ refreshRateConfigs->getBestRefreshRate(layers, globalSignals)
+ .getFps()
+ .getIntValue())
+ << "Failed for " << RefreshRateConfigs::layerVoteTypeString(vote);
+ };
+
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ const auto refreshRate = Fps(static_cast<float>(fps));
+ testRefreshRate(refreshRate, LayerVoteType::Heuristic);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
+ }
+}
+
TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
EXPECT_TRUE(mExpected60Config < mExpected90Config);
EXPECT_FALSE(mExpected60Config < mExpected60Config);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 7ead0af..94c76c7 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -257,6 +257,7 @@
}
state.isSecure = static_cast<bool>(Case::Display::SECURE);
+ state.flags = Case::Display::DISPLAY_FLAGS;
auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
displaySurface, producer);
@@ -279,6 +280,8 @@
EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
device->getSupportedPerFrameMetadata());
+ EXPECT_EQ(Case::Display::DISPLAY_FLAGS & DisplayDevice::eReceivesInput,
+ device->receivesInput());
if constexpr (Case::Display::CONNECTION_TYPE::value) {
EXPECT_EQ(1, device->getSupportedModes().size());
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index c3919d9..fe1544e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -30,8 +30,7 @@
MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override));
MOCK_METHOD(bool, isConnected, (), (const, override));
MOCK_METHOD(void, setConnected, (bool), (override));
- MOCK_METHOD(const std::unordered_set<hal::DisplayCapability> &, getCapabilities, (),
- (const, override));
+ MOCK_METHOD(bool, hasCapability, (hal::DisplayCapability), (const, override));
MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));