Merge "Added helper functions in Transform and PointerCoords"
diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp
index 8ab09f3..b36e0de 100644
--- a/libs/vr/libpdx/fuzz/Android.bp
+++ b/libs/vr/libpdx/fuzz/Android.bp
@@ -1,5 +1,5 @@
cc_fuzz {
- name: "libpdx_fuzz",
+ name: "libpdx_service_dispatcher_fuzzer",
clang: true,
srcs: [
"service_dispatcher_fuzzer.cpp",
@@ -15,6 +15,48 @@
shared_libs: [
"libutils",
"liblog",
- "libcutils",
+ "libcutils"
+ ],
+}
+
+cc_fuzz {
+ name: "libpdx_message_fuzzer",
+ clang: true,
+ srcs: [
+ "message_fuzzer.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+ shared_libs: [
+ "libutils",
+ "liblog",
+ "libcutils"
+ ],
+}
+
+cc_fuzz {
+ name: "libpdx_serialization_fuzzer",
+ clang: true,
+ srcs: [
+ "serialization_fuzzer.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+ shared_libs: [
+ "libutils",
+ "liblog",
+ "libcutils"
],
}
diff --git a/libs/vr/libpdx/fuzz/message_fuzzer.cpp b/libs/vr/libpdx/fuzz/message_fuzzer.cpp
new file mode 100644
index 0000000..b627045
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/message_fuzzer.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2020 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.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+// brian.balling@leviathansecurity.com
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <helpers.h>
+#include <pdx/client_channel.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/eventfd.h>
+#include <thread>
+
+using namespace android::pdx;
+
+// Fuzzer for Message object functions.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+
+ FuzzEndpoint* endpoint = new FuzzEndpoint(&fdp);
+ std::shared_ptr<Service> service(
+ new Service("FuzzService", std::unique_ptr<Endpoint>(endpoint)));
+ std::shared_ptr<Channel> channel(nullptr);
+
+ // Generate a random Message object to call functions in.
+ MessageInfo info;
+ info.pid = fdp.ConsumeIntegral<int>();
+ info.tid = fdp.ConsumeIntegral<int>();
+ info.cid = fdp.ConsumeIntegral<int>();
+ info.mid = fdp.ConsumeIntegral<int>();
+ info.euid = fdp.ConsumeIntegral<int>();
+ info.egid = fdp.ConsumeIntegral<int>();
+ info.op = fdp.ConsumeIntegral<int32_t>();
+ info.flags = fdp.ConsumeIntegral<uint32_t>();
+ info.service = service.get();
+ info.channel = channel.get();
+ info.send_len = fdp.ConsumeIntegral<size_t>();
+ info.recv_len = fdp.ConsumeIntegral<size_t>();
+ info.fd_count = fdp.ConsumeIntegral<size_t>();
+ if (fdp.remaining_bytes() >= 32) {
+ std::vector<uint8_t> impulse_vec = fdp.ConsumeBytes<uint8_t>(32);
+ memcpy(info.impulse, impulse_vec.data(), 32);
+ }
+
+ Message message = Message(info);
+
+ // A bunch of getters that probably won't do much, but might as well
+ // get coverage, while we are here.
+ message.GetProcessId();
+ message.GetThreadId();
+ message.GetEffectiveUserId();
+ message.GetEffectiveGroupId();
+ message.GetChannelId();
+ message.GetMessageId();
+ message.GetOp();
+ message.GetFlags();
+ message.GetSendLength();
+ message.GetReceiveLength();
+ message.GetFileDescriptorCount();
+ message.ImpulseEnd();
+ message.replied();
+ message.IsChannelExpired();
+ message.IsServiceExpired();
+ message.GetState();
+ message.GetState();
+
+ // Some misc. functions.
+ unsigned int fd = fdp.ConsumeIntegral<unsigned int>();
+ int clear_mask = fdp.ConsumeIntegral<int>();
+ int set_mask = fdp.ConsumeIntegral<int>();
+ Status<void> status = {};
+ message.ModifyChannelEvents(clear_mask, set_mask);
+
+ // Fuzz the handle functions.
+ LocalHandle l_handle = {};
+ BorrowedHandle b_handle = {};
+ RemoteHandle r_handle = {};
+ LocalChannelHandle lc_handle = {};
+ BorrowedChannelHandle bc_handle = {};
+ RemoteChannelHandle rc_handle = {};
+ FileReference f_ref = fdp.ConsumeIntegral<int32_t>();
+ ChannelReference c_ref = fdp.ConsumeIntegral<int32_t>();
+
+ // These don't actually modify any state in the Message or params.
+ // They can be called in any order.
+ message.PushFileHandle(b_handle);
+ message.PushFileHandle(r_handle);
+ message.PushChannelHandle(lc_handle);
+ message.PushChannelHandle(bc_handle);
+ message.PushChannelHandle(rc_handle);
+ message.GetFileHandle(f_ref, &l_handle);
+ message.GetChannelHandle(c_ref, &lc_handle);
+
+ // Can only reply once, pick at random.
+ switch (fdp.ConsumeIntegral<uint8_t>()) {
+ case 0:
+ message.ReplyFileDescriptor(fd);
+ break;
+ case 1:
+ message.Reply(status);
+ break;
+ case 2:
+ message.Reply(l_handle);
+ break;
+ case 3:
+ message.Reply(b_handle);
+ break;
+ case 4:
+ message.Reply(r_handle);
+ break;
+ case 5:
+ message.Reply(lc_handle);
+ break;
+ case 6:
+ message.Reply(bc_handle);
+ break;
+ case 7:
+ message.Reply(rc_handle);
+ }
+
+ // Fuzz the channel functions.
+ int flags = fdp.ConsumeIntegral<int>();
+ int channel_id = 0;
+ message.PushChannel(flags, channel, &channel_id);
+ message.CheckChannel(service.get(), c_ref, &channel);
+ message.CheckChannel(c_ref, &channel);
+ message.PushChannel(service.get(), flags, channel, &channel_id);
+ size_t iovec_size = sizeof(iovec);
+ struct iovec* iovecs = nullptr;
+
+ // Fuzz the read/write functions. Needs at least one iovec, plus one byte.
+ if (fdp.remaining_bytes() >= iovec_size + 1) {
+ std::vector<uint8_t> tmp_vec = fdp.ConsumeBytes<uint8_t>(iovec_size);
+ struct iovec* vector = reinterpret_cast<struct iovec*>(tmp_vec.data());
+ std::vector<uint8_t> tmp_buf =
+ fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes());
+ void* buf = reinterpret_cast<void*>(tmp_buf.data());
+ size_t buf_size = fdp.ConsumeIntegral<size_t>();
+
+ // Capping num_vecs to 1024 so it doesn't allocate too much memory.
+ size_t num_vecs = fdp.ConsumeIntegralInRange<size_t>(0, 1024);
+
+ if (num_vecs > 0)
+ iovecs = new struct iovec[num_vecs];
+ for (size_t i = 0; i < num_vecs; i++) {
+ iovecs[i] = *vector;
+ }
+
+ message.ReadAll(vector, buf_size);
+ message.WriteAll(buf, buf_size);
+ message.ReadVectorAll(vector, num_vecs);
+ message.WriteVectorAll(vector, num_vecs);
+ message.ReadVector(vector, buf_size);
+ message.WriteVector(vector, buf_size);
+ }
+
+ if (iovecs != nullptr)
+ delete[] iovecs;
+ return 0;
+}
diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
new file mode 100644
index 0000000..afde5f7
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2020 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.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+// brian.balling@leviathansecurity.com
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+struct FuzzType {
+ int a;
+ float b;
+ std::string c;
+
+ FuzzType() {}
+ FuzzType(int a, float b, const std::string& c) : a(a), b(b), c(c) {}
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(FuzzType, a, b, c);
+};
+
+// Fuzzer for Serialization operations, this is mostly just lifted from the
+// existing test cases to use fuzzed values as inputs.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload result;
+
+ // Currently, only fuzzing subset of types. In the future, may want
+ // to add more difficult to generate types like array, map, enum, etc...
+ bool b_val = fdp.ConsumeBool();
+ uint8_t u8_val = fdp.ConsumeIntegral<uint8_t>();
+ uint16_t u16_val = fdp.ConsumeIntegral<uint16_t>();
+ uint32_t u32_val = fdp.ConsumeIntegral<uint32_t>();
+ uint64_t u64_val = fdp.ConsumeIntegral<uint64_t>();
+ int8_t i8_val = fdp.ConsumeIntegral<int8_t>();
+ int16_t i16_val = fdp.ConsumeIntegral<uint16_t>();
+ int32_t i32_val = fdp.ConsumeIntegral<uint32_t>();
+ int64_t i64_val = fdp.ConsumeIntegral<uint64_t>();
+ float f_val = fdp.ConsumeFloatingPoint<float>();
+ double d_val = fdp.ConsumeFloatingPoint<double>();
+ std::string s_val = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ std::vector<uint8_t> vec_val =
+ fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes());
+ FuzzType t1_val{reinterpret_cast<int>(i32_val), f_val, s_val};
+
+ // Types need to be individually fuzzed because code path changes depending
+ // on which type is being serialized/deserialized.
+ Serialize(b_val, &result);
+ Deserialize(&b_val, &result);
+ Serialize(u8_val, &result);
+ Deserialize(&u8_val, &result);
+ Serialize(u16_val, &result);
+ Deserialize(&u16_val, &result);
+ Serialize(u32_val, &result);
+ Deserialize(&u32_val, &result);
+ Serialize(u64_val, &result);
+ Deserialize(&u64_val, &result);
+ Serialize(i8_val, &result);
+ Deserialize(&i8_val, &result);
+ Serialize(i16_val, &result);
+ Deserialize(&i16_val, &result);
+ Serialize(i32_val, &result);
+ Deserialize(&i32_val, &result);
+ Serialize(i64_val, &result);
+ Deserialize(&i64_val, &result);
+ Serialize(f_val, &result);
+ Deserialize(&f_val, &result);
+ Serialize(d_val, &result);
+ Deserialize(&d_val, &result);
+ Serialize(s_val, &result);
+ Deserialize(&s_val, &result);
+ Serialize(WrapString(s_val), &result);
+ Deserialize(&s_val, &result);
+ Serialize(vec_val, &result);
+ Deserialize(&vec_val, &result);
+ Serialize(t1_val, &result);
+ Deserialize(&t1_val, &result);
+
+ return 0;
+}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0fa2332..9deea63 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1584,7 +1584,8 @@
// Update the touch state as needed based on the properties of the touch event.
int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- sp<InputWindowHandle> newHoverWindowHandle;
+ sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle);
+ sp<InputWindowHandle> newTouchedWindowHandle;
// Copy current touch state into tempTouchState.
// This state will be used to update mTouchStatesByDisplay at the end of this function.
@@ -1653,7 +1654,7 @@
y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
}
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- sp<InputWindowHandle> newTouchedWindowHandle =
+ newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
@@ -1722,10 +1723,10 @@
}
// Update hover state.
- if (isHoverAction) {
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ newHoverWindowHandle = nullptr;
+ } else if (isHoverAction) {
newHoverWindowHandle = newTouchedWindowHandle;
- } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
- newHoverWindowHandle = mLastHoverWindowHandle;
}
// Update the temporary touch state.
@@ -1760,8 +1761,7 @@
sp<InputWindowHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
- sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+ newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
if (oldTouchedWindowHandle != newTouchedWindowHandle &&
oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
@@ -1798,8 +1798,11 @@
}
if (newHoverWindowHandle != mLastHoverWindowHandle) {
- // Let the previous window know that the hover sequence is over.
- if (mLastHoverWindowHandle != nullptr) {
+ // Let the previous window know that the hover sequence is over, unless we already did it
+ // when dispatching it as is to newTouchedWindowHandle.
+ if (mLastHoverWindowHandle != nullptr &&
+ (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ mLastHoverWindowHandle != newTouchedWindowHandle)) {
#if DEBUG_HOVER
ALOGD("Sending hover exit event to window %s.",
mLastHoverWindowHandle->getName().c_str());
@@ -1808,8 +1811,11 @@
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
}
- // Let the new window know that the hover sequence is starting.
- if (newHoverWindowHandle != nullptr) {
+ // Let the new window know that the hover sequence is starting, unless we already did it
+ // when dispatching it as is to newTouchedWindowHandle.
+ if (newHoverWindowHandle != nullptr &&
+ (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ newHoverWindowHandle != newTouchedWindowHandle)) {
#if DEBUG_HOVER
ALOGD("Sending hover enter event to window %s.",
newHoverWindowHandle->getName().c_str());
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index cf5a3ab..86b4868 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -939,6 +939,126 @@
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
}
+class PointerBuilder {
+public:
+ PointerBuilder(int32_t id, int32_t toolType) {
+ mProperties.clear();
+ mProperties.id = id;
+ mProperties.toolType = toolType;
+ mCoords.clear();
+ }
+
+ PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); }
+
+ PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+
+ PointerBuilder& axis(int32_t axis, float value) {
+ mCoords.setAxisValue(axis, value);
+ return *this;
+ }
+
+ PointerProperties buildProperties() const { return mProperties; }
+
+ PointerCoords buildCoords() const { return mCoords; }
+
+private:
+ PointerProperties mProperties;
+ PointerCoords mCoords;
+};
+
+class MotionEventBuilder {
+public:
+ MotionEventBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ MotionEventBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ MotionEventBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ MotionEventBuilder& actionButton(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionEventBuilder& buttonState(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) {
+ mRawXCursorPosition = rawXCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) {
+ mRawYCursorPosition = rawYCursorPosition;
+ return *this;
+ }
+
+ MotionEventBuilder& pointer(PointerBuilder pointer) {
+ mPointers.push_back(pointer);
+ return *this;
+ }
+
+ MotionEvent build() {
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+ for (const PointerBuilder& pointer : mPointers) {
+ pointerProperties.push_back(pointer.buildProperties());
+ pointerCoords.push_back(pointer.buildCoords());
+ }
+
+ // Set mouse cursor position for the most common cases to avoid boilerplate.
+ if (mSource == AINPUT_SOURCE_MOUSE &&
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
+ mPointers.size() == 1) {
+ mRawXCursorPosition = pointerCoords[0].getX();
+ mRawYCursorPosition = pointerCoords[0].getY();
+ }
+
+ MotionEvent event;
+ event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
+ mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+ mButtonState, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1,
+ /* xOffset */ 0,
+ /* yOffset */ 0, /* xPrecision */ 0, /* yPrecision */ 0,
+ mRawXCursorPosition, mRawYCursorPosition, mEventTime, mEventTime,
+ mPointers.size(), pointerProperties.data(), pointerCoords.data());
+
+ return event;
+ }
+
+private:
+ int32_t mAction;
+ int32_t mSource;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ int32_t mActionButton{0};
+ int32_t mButtonState{0};
+ float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+
+ std::vector<PointerBuilder> mPointers;
+};
+
+static int32_t injectMotionEvent(
+ const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT) {
+ return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
+ injectionTimeout,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
static int32_t injectMotionEvent(
const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
const PointF& position,
@@ -947,32 +1067,18 @@
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
- MotionEvent event;
- PointerProperties pointerProperties[1];
- PointerCoords pointerCoords[1];
-
- pointerProperties[0].clear();
- pointerProperties[0].id = 0;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
-
- // Define a valid motion down event.
- event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action,
- /* actionButton */ 0,
- /* flags */ 0,
- /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
- /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
- eventTime, eventTime,
- /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ MotionEvent event = MotionEventBuilder(action, source)
+ .displayId(displayId)
+ .eventTime(eventTime)
+ .rawXCursorPosition(cursorPosition.x)
+ .rawYCursorPosition(cursorPosition.y)
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(position.x)
+ .y(position.y))
+ .build();
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
- injectionTimeout,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ return injectMotionEvent(dispatcher, event);
}
static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
@@ -1187,6 +1293,198 @@
windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
}
+TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> windowLeft =
+ new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ windowLeft->setFrame(Rect(0, 0, 600, 800));
+ windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ sp<FakeWindowHandle> windowRight =
+ new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ windowRight->setFrame(Rect(600, 0, 1200, 800));
+ windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
+
+ // Start cursor position in right window so that we can move the cursor to left window.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(900)
+ .y(400))
+ .build()));
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Move cursor into left window
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Inject a series of mouse events for a mouse click
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ windowLeft->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ // Move mouse cursor back to right window
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(900)
+ .y(400))
+ .build()));
+ windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+}
+
+// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
+// directly in this test.
+TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 1200, 800));
+ window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ // Inject a series of mouse events for a mouse click
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(400))
+ .build()));
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
+ ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+}
+
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index fd3f562..6d50519 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -402,7 +402,9 @@
} else {
android::ProcessState::self()->startThreadPool();
::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ int result = RUN_ALL_TESTS();
+ kill(forkPid, SIGKILL);
+ return result;
}
return 0;
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ecf2597..36433c2 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -20,6 +20,7 @@
#include "LayerHistory.h"
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -183,4 +184,11 @@
mActiveLayersEnd = 0;
}
+
+std::string LayerHistory::dump() const {
+ std::lock_guard lock(mLock);
+ return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
+ mActiveLayersEnd);
+}
+
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 228b8a0..128699b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -22,6 +22,7 @@
#include <memory>
#include <mutex>
+#include <string>
#include <utility>
#include <vector>
@@ -71,6 +72,7 @@
virtual Summary summarize(nsecs_t now) = 0;
virtual void clear() = 0;
+ virtual std::string dump() const = 0;
};
namespace impl {
@@ -97,6 +99,7 @@
android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
void clear() override;
+ std::string dump() const override;
private:
friend class android::scheduler::LayerHistoryTest;
@@ -155,6 +158,7 @@
android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
void clear() override;
+ std::string dump() const override;
private:
friend android::scheduler::LayerHistoryTestV2;
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index aa04bd7..37e67e1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -20,6 +20,7 @@
#include "LayerHistory.h"
+#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -31,9 +32,8 @@
#include <utility>
#include "../Layer.h"
-#include "SchedulerUtils.h"
-
#include "LayerInfoV2.h"
+#include "SchedulerUtils.h"
namespace android::scheduler::impl {
@@ -214,4 +214,10 @@
}
}
+std::string LayerHistoryV2::dump() const {
+ std::lock_guard lock(mLock);
+ return base::StringPrintf("LayerHistoryV2{size=%zu, active=%zu}", mLayerInfos.size(),
+ mActiveLayersEnd);
+}
+
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 27bf0ec..eed8486 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -280,6 +280,11 @@
RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId);
+ // Returns whether switching configs (refresh rate or resolution) is possible.
+ // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only
+ // differ in resolution.
+ bool canSwitch() const { return mRefreshRates.size() > 1; }
+
// Class to enumerate options around toggling the kernel timer on and off. We have an option
// for no change to avoid extra calls to kernel.
enum class KernelIdleTimerAction {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5c0ba01..ab63d64 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -61,6 +61,7 @@
} while (false)
namespace android {
+namespace {
std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) {
// TODO (140302863) remove this and use the vsync_reactor system.
@@ -97,25 +98,27 @@
}
}
+const char* toContentDetectionString(bool useContentDetection, bool useContentDetectionV2) {
+ if (!useContentDetection) return "off";
+ return useContentDetectionV2 ? "V2" : "V1";
+}
+
+} // namespace
+
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
- const scheduler::RefreshRateConfigs& refreshRateConfig,
+ const scheduler::RefreshRateConfigs& refreshRateConfigs,
ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
bool useContentDetection)
: mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
mPrimaryDispSync(createDispSync(mSupportKernelTimer)),
mEventControlThread(new impl::EventControlThread(std::move(function))),
+ mLayerHistory(createLayerHistory(refreshRateConfigs, useContentDetectionV2)),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(refreshRateConfig),
+ mRefreshRateConfigs(refreshRateConfigs),
mUseContentDetection(useContentDetection),
mUseContentDetectionV2(useContentDetectionV2) {
using namespace sysprop;
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(refreshRateConfig);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
-
const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
@@ -149,11 +152,13 @@
Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+ ISchedulerCallback& schedulerCallback,
+ std::unique_ptr<LayerHistory> layerHistory, bool useContentDetectionV2,
bool useContentDetection)
: mSupportKernelTimer(false),
mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
+ mLayerHistory(std::move(layerHistory)),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(configs),
mUseContentDetection(useContentDetection),
@@ -166,6 +171,17 @@
mIdleTimer.reset();
}
+std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
+ const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) {
+ if (!configs.canSwitch()) return nullptr;
+
+ if (useContentDetectionV2) {
+ return std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
+ }
+
+ return std::make_unique<scheduler::impl::LayerHistory>();
+}
+
DispSync& Scheduler::getPrimaryDispSync() {
return *mPrimaryDispSync;
}
@@ -499,16 +515,7 @@
}
void Scheduler::notifyTouchEvent() {
- if (!mTouchTimer) return;
-
- // Touch event will boost the refresh rate to performance.
- // Clear Layer History to get fresh FPS detection.
- // NOTE: Instead of checking all the layers, we should be checking the layer
- // that is currently on top. b/142507166 will give us this capability.
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mLayerHistory) {
- // Layer History will be cleared based on RefreshRateConfigs::getBestRefreshRate
-
+ if (mTouchTimer) {
mTouchTimer->reset();
if (mSupportKernelTimer && mIdleTimer) {
@@ -564,8 +571,14 @@
void Scheduler::touchTimerCallback(TimerState state) {
const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
+ // Touch event will boost the refresh rate to performance.
+ // Clear layer history to get fresh FPS detection.
+ // NOTE: Instead of checking all the layers, we should be checking the layer
+ // that is currently on top. b/142507166 will give us this capability.
if (handleTimerStateChanged(&mFeatures.touch, touch)) {
- mLayerHistory->clear();
+ if (mLayerHistory) {
+ mLayerHistory->clear();
+ }
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
@@ -577,14 +590,13 @@
void Scheduler::dump(std::string& result) const {
using base::StringAppendF;
- const char* const states[] = {"off", "on"};
- StringAppendF(&result, "+ Idle timer: %s\n",
- mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+ StringAppendF(&result, "+ Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Touch timer: %s\n",
- mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
- StringAppendF(&result, "+ Use content detection: %s\n\n",
- sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
+ mTouchTimer ? mTouchTimer->dump().c_str() : "off");
+ StringAppendF(&result, "+ Content detection: %s %s\n\n",
+ toContentDetectionString(mUseContentDetection, mUseContentDetectionV2),
+ mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
}
template <class T>
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 730ea8f..5fee4b4 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -174,8 +174,11 @@
// Used by tests to inject mocks.
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2, bool useContentDetection);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback&,
+ std::unique_ptr<LayerHistory>, bool useContentDetectionV2, bool useContentDetection);
+
+ static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&,
+ bool useContentDetectionV2);
std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
@@ -231,7 +234,7 @@
std::unique_ptr<EventControlThread> mEventControlThread;
// Used to choose refresh rate if content detection is enabled.
- std::unique_ptr<LayerHistory> mLayerHistory;
+ const std::unique_ptr<LayerHistory> mLayerHistory;
// Timer that records time between requests for next vsync.
std::optional<scheduler::OneShotTimer> mIdleTimer;
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 9355e61..9add6a5 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -150,9 +150,14 @@
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+ constexpr bool kHasMultipleConfigs = true;
mFlinger.setupScheduler(std::move(primaryDispSync),
std::make_unique<mock::EventControlThread>(),
- std::move(eventThread), std::move(sfEventThread));
+ std::move(eventThread), std::move(sfEventThread),
+ kHasMultipleConfigs);
+
+ // Layer history should be created if there are multiple configs.
+ ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
}
void setupForceGeometryDirty() {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index cae317b..3ab22d3 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -49,6 +49,8 @@
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index afd2b71..1705835 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -50,6 +50,8 @@
LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
+ void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
+
impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1aa7320..382199d 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -31,8 +31,10 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockEventThread.h"
+#include "mock/MockLayer.h"
using testing::_;
using testing::Return;
@@ -58,13 +60,18 @@
SchedulerTest();
~SchedulerTest() override;
- std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
- std::unique_ptr<TestableScheduler> mScheduler;
+ Hwc2::mock::Display mDisplay;
+ const scheduler::RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+ .setVsyncPeriod(16'666'667)
+ .setConfigGroup(0)
+ .build()},
+ HwcConfigIndexType(0)};
+
+ TestableScheduler mScheduler{mConfigs, false};
Scheduler::ConnectionHandle mConnectionHandle;
mock::EventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
- Hwc2::mock::Display mDisplay;
};
SchedulerTest::SchedulerTest() {
@@ -72,16 +79,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
- HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
- .setConfigGroup(0)
- .build()};
- mRefreshRateConfigs = std::make_unique<
- scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
-
- mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
-
auto eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -93,7 +90,7 @@
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
+ mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
EXPECT_TRUE(mConnectionHandle);
}
@@ -103,7 +100,6 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
@@ -111,67 +107,84 @@
TEST_F(SchedulerTest, invalidConnectionHandle) {
Scheduler::ConnectionHandle handle;
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(handle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler.createDisplayEventConnection(handle,
+ ISurfaceComposer::eConfigChangedSuppress);
+
EXPECT_FALSE(connection);
- EXPECT_FALSE(mScheduler->getEventConnection(handle));
+ EXPECT_FALSE(mScheduler.getEventConnection(handle));
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
+ mScheduler.onScreenAcquired(handle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
+ mScheduler.onScreenReleased(handle);
std::string output;
EXPECT_CALL(*mEventThread, dump(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
+ mScheduler.dump(handle, output);
EXPECT_TRUE(output.empty());
EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
+ mScheduler.setPhaseOffset(handle, 10);
}
TEST_F(SchedulerTest, validConnectionHandle) {
- sp<IDisplayEventConnection> connection;
- ASSERT_NO_FATAL_FAILURE(
- connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
- ISurfaceComposer::
- eConfigChangedSuppress));
+ const sp<IDisplayEventConnection> connection =
+ mScheduler.createDisplayEventConnection(mConnectionHandle,
+ ISurfaceComposer::eConfigChangedSuppress);
+
ASSERT_EQ(mEventThreadConnection, connection);
- EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
+ EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- ASSERT_NO_FATAL_FAILURE(
- mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+ mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
+ mScheduler.onScreenAcquired(mConnectionHandle);
EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
+ mScheduler.onScreenReleased(mConnectionHandle);
std::string output("dump");
EXPECT_CALL(*mEventThread, dump(output)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
+ mScheduler.dump(mConnectionHandle, output);
EXPECT_FALSE(output.empty());
EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
- ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+ mScheduler.setPhaseOffset(mConnectionHandle, 10);
static constexpr size_t kEventConnections = 5;
ON_CALL(*mEventThread, getEventThreadConnectionCount())
.WillByDefault(Return(kEventConnections));
- EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
+ EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle));
}
-} // namespace
+TEST_F(SchedulerTest, noLayerHistory) {
+ // Layer history should not be created if there is a single config.
+ ASSERT_FALSE(mScheduler.hasLayerHistory());
+
+ TestableSurfaceFlinger flinger;
+ mock::MockLayer layer(flinger.flinger());
+
+ // Content detection should be no-op.
+ mScheduler.registerLayer(&layer);
+ mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer);
+
+ constexpr bool kPowerStateNormal = true;
+ mScheduler.setDisplayPowerState(kPowerStateNormal);
+
+ constexpr uint32_t kDisplayArea = 999'999;
+ mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea);
+
+ mScheduler.chooseRefreshRateForContent();
+ EXPECT_EQ(0, mScheduler.refreshRateChangeCount());
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index b7067a6..c463294 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -29,41 +29,20 @@
class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
: Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
- useContentDetectionV2, true) {
- if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- } else {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
- }
- }
+ createLayerHistory(configs, useContentDetectionV2), useContentDetectionV2,
+ true) {}
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
return Scheduler::createConnection(std::move(eventThread));
}
- size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
- if (mUseContentDetectionV2) {
- return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
- ->mLayerInfos.size();
- } else {
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
- ->mLayerInfos.size();
- }
- }
-
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
@@ -73,14 +52,26 @@
auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- auto mutableLayerHistory() {
+ size_t refreshRateChangeCount() const { return mRefreshRateChangeCount; }
+
+ bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
+
+ auto* mutableLayerHistory() {
+ LOG_ALWAYS_FATAL_IF(mUseContentDetectionV2);
return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
}
- auto mutableLayerHistoryV2() {
+ auto* mutableLayerHistoryV2() {
+ LOG_ALWAYS_FATAL_IF(!mUseContentDetectionV2);
return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
}
+ size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
+ if (!mLayerHistory) return 0;
+ return mUseContentDetectionV2 ? mutableLayerHistoryV2()->mLayerInfos.size()
+ : mutableLayerHistory()->mLayerInfos.size();
+ }
+
void replaceTouchTimer(int64_t millis) {
if (mTouchTimer) {
mTouchTimer.reset();
@@ -108,9 +99,12 @@
}
private:
- void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
+ void changeRefreshRate(const RefreshRate&, ConfigEvent) override { mRefreshRateChangeCount++; }
+
void repaintEverythingForHWC() override {}
void kernelTimerChanged(bool /*expired*/) override {}
+
+ size_t mRefreshRateChangeCount = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f630103..256e048 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -202,13 +202,20 @@
std::unique_ptr<EventControlThread> eventControlThread,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
- bool useContentDetectionV2 = false) {
+ bool hasMultipleConfigs = false) {
std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(16666667))
+ .setVsyncPeriod(16'666'667)
.setConfigGroup(0)
.build()};
+ if (hasMultipleConfigs) {
+ configs.emplace_back(HWC2::Display::Config::Builder(mDisplay, 1)
+ .setVsyncPeriod(11'111'111)
+ .setConfigGroup(0)
+ .build());
+ }
+
mFlinger->mRefreshRateConfigs = std::make_unique<
scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
mFlinger->mRefreshRateStats = std::make_unique<
@@ -218,9 +225,10 @@
mFlinger->mPhaseConfiguration =
mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+ constexpr bool kUseContentDetectionV2 = false;
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
- *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
+ *mFlinger->mRefreshRateConfigs, kUseContentDetectionV2);
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 896c162..03e51ae 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -47,105 +47,77 @@
static constexpr int MAX_RETRIES = 1;
-template <typename T>
-using hal_connect_fn = std::function<sp<T>()>;
-
-template <typename T>
-sp<T> connectToHal(bool* halExists, const hal_connect_fn<T>& connectFn, const char* halName) {
- if (!*halExists) {
+std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
+ static bool gHalExists = true;
+ if (!gHalExists) {
+ // We already tried to connect to all of the vibrator HAL versions and none was available.
return nullptr;
}
- sp<T> hal = connectFn();
- if (hal) {
- ALOGV("Successfully connected to Vibrator HAL %s service.", halName);
- } else {
- ALOGV("Vibrator HAL %s service not available.", halName);
- *halExists = false;
- }
- return hal;
-}
-sp<Aidl::IVibrator> connectToAidl() {
- static bool gHalExists = true;
- static hal_connect_fn<Aidl::IVibrator> connectFn = []() {
- return waitForVintfService<Aidl::IVibrator>();
- };
- return connectToHal(&gHalExists, connectFn, "AIDL");
-}
-
-sp<V1_0::IVibrator> connectToHidl() {
- static bool gHalExists = true;
- static hal_connect_fn<V1_0::IVibrator> connectFn = []() {
- return V1_0::IVibrator::getService();
- };
- return connectToHal(&gHalExists, connectFn, "v1.0");
-}
-
-// -------------------------------------------------------------------------------------------------
-
-std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
- sp<Aidl::IVibrator> aidlHal = connectToAidl();
+ sp<Aidl::IVibrator> aidlHal = waitForVintfService<Aidl::IVibrator>();
if (aidlHal) {
+ ALOGV("Successfully connected to Vibrator HAL AIDL service.");
return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal);
}
- sp<V1_0::IVibrator> halV1_0 = connectToHidl();
+
+ sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
if (halV1_0 == nullptr) {
- // No Vibrator HAL service available.
+ ALOGV("Vibrator HAL service not available.");
+ gHalExists = false;
return nullptr;
}
+
sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
if (halV1_3) {
- ALOGV("Successfully converted to Vibrator HAL v1.3 service.");
+ ALOGV("Successfully connected to Vibrator HAL v1.3 service.");
return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
}
sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
if (halV1_2) {
- ALOGV("Successfully converted to Vibrator HAL v1.2 service.");
+ ALOGV("Successfully connected to Vibrator HAL v1.2 service.");
return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
}
sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
if (halV1_1) {
- ALOGV("Successfully converted to Vibrator HAL v1.1 service.");
+ ALOGV("Successfully connected to Vibrator HAL v1.1 service.");
return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
}
+ ALOGV("Successfully connected to Vibrator HAL v1.0 service.");
return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
}
// -------------------------------------------------------------------------------------------------
-std::shared_ptr<HalWrapper> HalController::initHal() {
- std::lock_guard<std::mutex> lock(mConnectedHalMutex);
- if (mConnectedHal == nullptr) {
- mConnectedHal = mHalConnector->connect(mCallbackScheduler);
- }
- return mConnectedHal;
-}
-
template <typename T>
HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
if (result.isFailed()) {
ALOGE("%s failed: Vibrator HAL not available", functionName);
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
- // Drop HAL handle. This will force future api calls to reconnect.
- mConnectedHal = nullptr;
+ mConnectedHal->tryReconnect();
}
return result;
}
template <typename T>
HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) {
- std::shared_ptr<HalWrapper> hal = initHal();
- if (hal == nullptr) {
- ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
- return HalResult<T>::unsupported();
+ std::shared_ptr<HalWrapper> hal = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ // Init was never called, so connect to HAL for the first time during this call.
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+
+ if (mConnectedHal == nullptr) {
+ ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
+ return HalResult<T>::unsupported();
+ }
+ }
+ hal = mConnectedHal;
}
HalResult<T> ret = processHalResult(halFn(hal), functionName);
for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
- hal = initHal();
- if (hal) {
- ret = processHalResult(halFn(hal), functionName);
- }
+ ret = processHalResult(halFn(hal), functionName);
}
return ret;
@@ -153,11 +125,27 @@
// -------------------------------------------------------------------------------------------------
+void HalController::init() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ }
+}
+
HalResult<void> HalController::ping() {
hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
return apply(pingFn, "ping");
}
+void HalController::tryReconnect() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect(mCallbackScheduler);
+ } else {
+ mConnectedHal->tryReconnect();
+ }
+}
+
HalResult<void> HalController::on(milliseconds timeout,
const std::function<void()>& completionCallback) {
hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) {
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 94db538..9d219ff 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -149,8 +149,16 @@
// -------------------------------------------------------------------------------------------------
HalResult<void> AidlHalWrapper::ping() {
- return IInterface::asBinder(mHandle)->pingBinder() ? HalResult<void>::ok()
- : HalResult<void>::failed();
+ return IInterface::asBinder(getHal())->pingBinder() ? HalResult<void>::ok()
+ : HalResult<void>::failed();
+}
+
+void AidlHalWrapper::tryReconnect() {
+ sp<Aidl::IVibrator> newHandle = checkVintfService<Aidl::IVibrator>();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
}
HalResult<void> AidlHalWrapper::on(milliseconds timeout,
@@ -160,7 +168,7 @@
static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
- auto ret = HalResult<void>::fromStatus(mHandle->on(timeout.count(), cb));
+ auto ret = HalResult<void>::fromStatus(getHal()->on(timeout.count(), cb));
if (!supportsCallback && ret.isOk()) {
mCallbackScheduler->schedule(completionCallback, timeout);
}
@@ -169,24 +177,24 @@
}
HalResult<void> AidlHalWrapper::off() {
- return HalResult<void>::fromStatus(mHandle->off());
+ return HalResult<void>::fromStatus(getHal()->off());
}
HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) {
float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max();
- return HalResult<void>::fromStatus(mHandle->setAmplitude(convertedAmplitude));
+ return HalResult<void>::fromStatus(getHal()->setAmplitude(convertedAmplitude));
}
HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
- return HalResult<void>::fromStatus(mHandle->setExternalControl(enabled));
+ return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled));
}
HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
- return HalResult<void>::fromStatus(mHandle->alwaysOnEnable(id, effect, strength));
+ return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength));
}
HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
- return HalResult<void>::fromStatus(mHandle->alwaysOnDisable(id));
+ return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
}
HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
@@ -210,7 +218,7 @@
auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
int32_t lengthMs;
- auto result = mHandle->perform(effect, strength, cb, &lengthMs);
+ auto result = getHal()->perform(effect, strength, cb, &lengthMs);
milliseconds length = milliseconds(lengthMs);
auto ret = HalResult<milliseconds>::fromStatus(result, length);
@@ -226,31 +234,47 @@
const std::function<void()>& completionCallback) {
// This method should always support callbacks, so no need to double check.
auto cb = new HalCallbackWrapper(completionCallback);
- return HalResult<void>::fromStatus(mHandle->compose(primitiveEffects, cb));
+ return HalResult<void>::fromStatus(getHal()->compose(primitiveEffects, cb));
}
HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
int32_t capabilities = 0;
- auto result = mHandle->getCapabilities(&capabilities);
+ auto result = getHal()->getCapabilities(&capabilities);
return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
}
HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
std::vector<Effect> supportedEffects;
- auto result = mHandle->getSupportedEffects(&supportedEffects);
+ auto result = getHal()->getSupportedEffects(&supportedEffects);
return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
}
+sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
// -------------------------------------------------------------------------------------------------
-HalResult<void> HidlHalWrapperV1_0::ping() {
- auto result = mHandleV1_0->ping();
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::ping() {
+ auto result = getHal()->ping();
return HalResult<void>::fromReturn(result);
}
-HalResult<void> HidlHalWrapperV1_0::on(milliseconds timeout,
- const std::function<void()>& completionCallback) {
- auto result = mHandleV1_0->on(timeout.count());
+template <typename I>
+void HidlHalWrapper<I>::tryReconnect() {
+ sp<I> newHandle = I::tryGetService();
+ if (newHandle) {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ mHandle = std::move(newHandle);
+ }
+}
+
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ auto result = getHal()->on(timeout.count());
auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
if (ret.isOk()) {
mCallbackScheduler->schedule(completionCallback, timeout);
@@ -258,69 +282,68 @@
return ret;
}
-HalResult<void> HidlHalWrapperV1_0::off() {
- auto result = mHandleV1_0->off();
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::off() {
+ auto result = getHal()->off();
return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
}
-HalResult<void> HidlHalWrapperV1_0::setAmplitude(int32_t amplitude) {
- auto result = mHandleV1_0->setAmplitude(static_cast<uint8_t>(amplitude));
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setAmplitude(int32_t amplitude) {
+ auto result = getHal()->setAmplitude(static_cast<uint8_t>(amplitude));
return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
}
-HalResult<void> HidlHalWrapperV1_0::setExternalControl(bool) {
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
return HalResult<void>::unsupported();
}
-HalResult<void> HidlHalWrapperV1_0::alwaysOnEnable(int32_t, Effect, EffectStrength) {
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) {
ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
return HalResult<void>::unsupported();
}
-HalResult<void> HidlHalWrapperV1_0::alwaysOnDisable(int32_t) {
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
return HalResult<void>::unsupported();
}
-HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilities() {
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() {
std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
- return loadCached<Capabilities>(std::bind(&HidlHalWrapperV1_0::getCapabilitiesInternal, this),
+ return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this),
mCapabilities);
}
-HalResult<std::vector<Effect>> HidlHalWrapperV1_0::getSupportedEffects() {
+template <typename I>
+HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() {
ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
return HalResult<std::vector<Effect>>::unsupported();
}
-HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternalV1_0(effect, strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-HalResult<void> HidlHalWrapperV1_0::performComposedEffect(const std::vector<CompositeEffect>&,
- const std::function<void()>&) {
+template <typename I>
+HalResult<void> HidlHalWrapper<I>::performComposedEffect(const std::vector<CompositeEffect>&,
+ const std::function<void()>&) {
ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
return HalResult<void>::unsupported();
}
-HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilitiesInternal() {
- hardware::Return<bool> result = mHandleV1_0->supportsAmplitudeControl();
+template <typename I>
+HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
+ hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
Capabilities capabilities =
result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
return HalResult<Capabilities>::fromReturn(result, capabilities);
}
-template <class I, class T>
-HalResult<milliseconds> HidlHalWrapperV1_0::performInternal(
- perform_fn<I, T> performFn, sp<I> handle, T effect, EffectStrength strength,
+template <typename I>
+template <typename T>
+HalResult<milliseconds> HidlHalWrapper<I>::performInternal(
+ perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength,
const std::function<void()>& completionCallback) {
V1_0::Status status;
int32_t lengthMs;
@@ -341,10 +364,24 @@
return ret;
}
-HalResult<milliseconds> HidlHalWrapperV1_0::performInternalV1_0(
+template <typename I>
+sp<I> HidlHalWrapper<I>::getHal() {
+ std::lock_guard<std::mutex> lock(mHandleMutex);
+ return mHandle;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- V1_0::Effect e = static_cast<V1_0::Effect>(effect);
- return performInternal(&V1_0::IVibrator::perform, mHandleV1_0, e, strength, completionCallback);
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternal(&V1_0::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
}
// -------------------------------------------------------------------------------------------------
@@ -352,10 +389,12 @@
HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternalV1_0(effect, strength, completionCallback);
+ return performInternal(&V1_1::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
}
if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternalV1_1(effect, strength, completionCallback);
+ return performInternal(&V1_1::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
}
ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -363,25 +402,21 @@
return HalResult<milliseconds>::unsupported();
}
-HalResult<milliseconds> HidlHalWrapperV1_1::performInternalV1_1(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
- return performInternal(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength,
- completionCallback);
-}
-
// -------------------------------------------------------------------------------------------------
HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternalV1_0(effect, strength, completionCallback);
+ return performInternal(&V1_2::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
}
if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternalV1_1(effect, strength, completionCallback);
+ return performInternal(&V1_2::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
}
if (isStaticCastValid<V1_2::Effect>(effect)) {
- return performInternalV1_2(effect, strength, completionCallback);
+ return performInternal(&V1_2::IVibrator::perform_1_2, getHal(),
+ static_cast<V1_2::Effect>(effect), strength, completionCallback);
}
ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -389,33 +424,30 @@
return HalResult<milliseconds>::unsupported();
}
-HalResult<milliseconds> HidlHalWrapperV1_2::performInternalV1_2(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- V1_2::Effect e = static_cast<V1_2::Effect>(effect);
- return performInternal(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength,
- completionCallback);
-}
-
// -------------------------------------------------------------------------------------------------
HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
- auto result = mHandleV1_3->setExternalControl(static_cast<uint32_t>(enabled));
+ auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
}
HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternalV1_0(effect, strength, completionCallback);
+ return performInternal(&V1_3::IVibrator::perform, getHal(),
+ static_cast<V1_0::Effect>(effect), strength, completionCallback);
}
if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternalV1_1(effect, strength, completionCallback);
+ return performInternal(&V1_3::IVibrator::perform_1_1, getHal(),
+ static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
}
if (isStaticCastValid<V1_2::Effect>(effect)) {
- return performInternalV1_2(effect, strength, completionCallback);
+ return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
+ static_cast<V1_2::Effect>(effect), strength, completionCallback);
}
if (isStaticCastValid<V1_3::Effect>(effect)) {
- return performInternalV1_3(effect, strength, completionCallback);
+ return performInternal(&V1_3::IVibrator::perform_1_3, getHal(),
+ static_cast<V1_3::Effect>(effect), strength, completionCallback);
}
ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
@@ -424,23 +456,23 @@
}
HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
- HalResult<Capabilities> parentResult = HidlHalWrapperV1_2::getCapabilitiesInternal();
- if (!parentResult.isOk()) {
- // Loading for previous HAL versions already failed, so propagate failure.
- return parentResult;
+ sp<V1_3::IVibrator> hal = getHal();
+ auto amplitudeResult = hal->supportsAmplitudeControl();
+ if (!amplitudeResult.isOk()) {
+ return HalResult<Capabilities>::failed();
}
- Capabilities capabilities = parentResult.value();
- auto result = mHandleV1_3->supportsExternalControl();
- capabilities |= result.withDefault(false) ? Capabilities::EXTERNAL_CONTROL : Capabilities::NONE;
- return HalResult<Capabilities>::fromReturn(result, capabilities);
-}
+ auto externalControlResult = hal->supportsExternalControl();
+ Capabilities capabilities = Capabilities::NONE;
-HalResult<milliseconds> HidlHalWrapperV1_3::performInternalV1_3(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- V1_3::Effect e = static_cast<V1_3::Effect>(effect);
- return performInternal(&V1_3::IVibrator::perform_1_3, mHandleV1_3, e, strength,
- completionCallback);
+ if (amplitudeResult.withDefault(false)) {
+ capabilities |= Capabilities::AMPLITUDE_CONTROL;
+ }
+ if (externalControlResult.withDefault(false)) {
+ capabilities |= Capabilities::EXTERNAL_CONTROL;
+ }
+
+ return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities);
}
// -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 1fb7d05..daf2c8c 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -51,7 +51,10 @@
mConnectedHal(nullptr) {}
virtual ~HalController() = default;
+ void init();
+
HalResult<void> ping() final override;
+ void tryReconnect() final override;
HalResult<void> on(std::chrono::milliseconds timeout,
const std::function<void()>& completionCallback) final override;
@@ -81,8 +84,6 @@
// Shared pointer to allow local copies to be used by different threads.
std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex);
- std::shared_ptr<HalWrapper> initHal();
-
template <typename T>
HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 5e3c275..a4fa869 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -20,6 +20,7 @@
#include <android-base/thread_annotations.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
#include <vibratorservice/VibratorCallbackScheduler.h>
@@ -131,6 +132,7 @@
virtual ~HalWrapper() = default;
virtual HalResult<void> ping() = 0;
+ virtual void tryReconnect() = 0;
virtual HalResult<void> on(std::chrono::milliseconds timeout,
const std::function<void()>& completionCallback) = 0;
@@ -167,34 +169,36 @@
: HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
virtual ~AidlHalWrapper() = default;
- virtual HalResult<void> ping() override;
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
- virtual HalResult<void> on(std::chrono::milliseconds timeout,
- const std::function<void()>& completionCallback) override;
- virtual HalResult<void> off() override;
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> off() override final;
- virtual HalResult<void> setAmplitude(int32_t amplitude) override;
- virtual HalResult<void> setExternalControl(bool enabled) override;
+ HalResult<void> setAmplitude(int32_t amplitude) override final;
+ HalResult<void> setExternalControl(bool enabled) override final;
- virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
- hardware::vibrator::EffectStrength strength) override;
- virtual HalResult<void> alwaysOnDisable(int32_t id) override;
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnDisable(int32_t id) override final;
- virtual HalResult<Capabilities> getCapabilities() override;
- virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override;
+ HalResult<Capabilities> getCapabilities() override final;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
- virtual HalResult<std::chrono::milliseconds> performEffect(
+ HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback) override;
+ const std::function<void()>& completionCallback) override final;
- virtual HalResult<void> performComposedEffect(
+ HalResult<void> performComposedEffect(
const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
- const std::function<void()>& completionCallback) override;
+ const std::function<void()>& completionCallback) override final;
private:
- const sp<hardware::vibrator::IVibrator> mHandle;
+ std::mutex mHandleMutex;
std::mutex mCapabilitiesMutex;
std::mutex mSupportedEffectsMutex;
+ sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
GUARDED_BY(mSupportedEffectsMutex);
@@ -202,125 +206,120 @@
// Loads directly from IVibrator handle, skipping caches.
HalResult<Capabilities> getCapabilitiesInternal();
HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+ sp<hardware::vibrator::IVibrator> getHal();
};
-// Wrapper for the HDIL Vibrator HAL v1.0.
-class HidlHalWrapperV1_0 : public HalWrapper {
+// Wrapper for the HDIL Vibrator HALs.
+template <typename I>
+class HidlHalWrapper : public HalWrapper {
public:
- HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_0::IVibrator> handle)
- : HalWrapper(std::move(scheduler)), mHandleV1_0(std::move(handle)) {}
- virtual ~HidlHalWrapperV1_0() = default;
+ HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
+ : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+ virtual ~HidlHalWrapper() = default;
- virtual HalResult<void> ping() override;
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
- virtual HalResult<void> on(std::chrono::milliseconds timeout,
- const std::function<void()>& completionCallback) override;
- virtual HalResult<void> off() override;
+ HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override final;
+ HalResult<void> off() override final;
- virtual HalResult<void> setAmplitude(int32_t amplitude) override;
+ HalResult<void> setAmplitude(int32_t amplitude) override final;
virtual HalResult<void> setExternalControl(bool enabled) override;
- virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
- hardware::vibrator::EffectStrength strength) override;
- virtual HalResult<void> alwaysOnDisable(int32_t id) override;
+ HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override final;
+ HalResult<void> alwaysOnDisable(int32_t id) override final;
- virtual HalResult<Capabilities> getCapabilities() override;
- virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override;
+ HalResult<Capabilities> getCapabilities() override final;
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final;
- virtual HalResult<std::chrono::milliseconds> performEffect(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback) override;
-
- virtual HalResult<void> performComposedEffect(
+ HalResult<void> performComposedEffect(
const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
- const std::function<void()>& completionCallback) override;
+ const std::function<void()>& completionCallback) override final;
protected:
- const sp<hardware::vibrator::V1_0::IVibrator> mHandleV1_0;
+ std::mutex mHandleMutex;
std::mutex mCapabilitiesMutex;
+ sp<I> mHandle GUARDED_BY(mHandleMutex);
std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
// Loads directly from IVibrator handle, skipping the mCapabilities cache.
virtual HalResult<Capabilities> getCapabilitiesInternal();
- template <class I, class T>
+ template <class T>
using perform_fn =
hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
hardware::vibrator::V1_0::IVibrator::perform_cb);
- template <class I, class T>
+ template <class T>
HalResult<std::chrono::milliseconds> performInternal(
- perform_fn<I, T> performFn, sp<I> handle, T effect,
+ perform_fn<T> performFn, sp<I> handle, T effect,
hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback);
- HalResult<std::chrono::milliseconds> performInternalV1_0(
+ sp<I> getHal();
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.0.
+class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> {
+public:
+ HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_0::IVibrator> handle)
+ : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback);
+ const std::function<void()>& completionCallback) override final;
};
// Wrapper for the HDIL Vibrator HAL v1.1.
-class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
+class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
public:
HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
sp<hardware::vibrator::V1_1::IVibrator> handle)
- : HidlHalWrapperV1_0(std::move(scheduler), handle), mHandleV1_1(handle) {}
+ : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
virtual ~HidlHalWrapperV1_1() = default;
- virtual HalResult<std::chrono::milliseconds> performEffect(
+ HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback) override;
-
-protected:
- const sp<hardware::vibrator::V1_1::IVibrator> mHandleV1_1;
-
- HalResult<std::chrono::milliseconds> performInternalV1_1(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback);
+ const std::function<void()>& completionCallback) override final;
};
// Wrapper for the HDIL Vibrator HAL v1.2.
-class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
+class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
public:
HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
sp<hardware::vibrator::V1_2::IVibrator> handle)
- : HidlHalWrapperV1_1(std::move(scheduler), handle), mHandleV1_2(handle) {}
+ : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
virtual ~HidlHalWrapperV1_2() = default;
- virtual HalResult<std::chrono::milliseconds> performEffect(
+ HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback) override;
-
-protected:
- const sp<hardware::vibrator::V1_2::IVibrator> mHandleV1_2;
-
- HalResult<std::chrono::milliseconds> performInternalV1_2(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback);
+ const std::function<void()>& completionCallback) override final;
};
// Wrapper for the HDIL Vibrator HAL v1.3.
-class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
+class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
public:
HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
sp<hardware::vibrator::V1_3::IVibrator> handle)
- : HidlHalWrapperV1_2(std::move(scheduler), handle), mHandleV1_3(handle) {}
+ : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler),
+ std::move(handle)) {}
virtual ~HidlHalWrapperV1_3() = default;
- virtual HalResult<void> setExternalControl(bool enabled) override;
+ HalResult<void> setExternalControl(bool enabled) override final;
- virtual HalResult<std::chrono::milliseconds> performEffect(
+ HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback) override;
+ const std::function<void()>& completionCallback) override final;
protected:
- const sp<hardware::vibrator::V1_3::IVibrator> mHandleV1_3;
-
- virtual HalResult<Capabilities> getCapabilitiesInternal() override;
- HalResult<std::chrono::milliseconds> performInternalV1_3(
- hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
- const std::function<void()>& completionCallback);
+ HalResult<Capabilities> getCapabilitiesInternal() override final;
};
// -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 24e6a1e..2d55549 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -53,6 +53,7 @@
virtual ~MockHalWrapper() = default;
MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
MOCK_METHOD(vibrator::HalResult<void>, on,
(milliseconds timeout, const std::function<void()>& completionCallback),
(override));
@@ -132,7 +133,6 @@
vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
vibrator::HalResult<std::vector<Effect>> effectsResult,
vibrator::HalResult<milliseconds> durationResult) {
- InSequence seq;
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(cardinality))
.WillRepeatedly(Return(voidResult));
@@ -167,11 +167,25 @@
EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
.Times(Exactly(cardinality))
.WillRepeatedly(Return(voidResult));
+
+ if (cardinality > 1) {
+ // One reconnection call after each failure.
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(11 * cardinality));
+ }
}
};
// -------------------------------------------------------------------------------------------------
+TEST_F(VibratorHalControllerTest, TestInit) {
+ mController->init();
+ ASSERT_EQ(1, mConnectCounter);
+
+ // Noop when wrapper was already initialized.
+ mController->init();
+ ASSERT_EQ(1, mConnectCounter);
+}
+
TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
std::vector<Effect> supportedEffects;
supportedEffects.push_back(Effect::CLICK);
@@ -211,7 +225,6 @@
ASSERT_TRUE(mController->performComposedEffect(compositeEffects, []() {}).isOk());
- // No reconnection attempt was made after the first one.
ASSERT_EQ(1, mConnectCounter);
}
@@ -239,7 +252,6 @@
ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
.isUnsupported());
- // No reconnection attempt was made after the first one.
ASSERT_EQ(1, mConnectCounter);
}
@@ -266,23 +278,24 @@
ASSERT_TRUE(
mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed());
- // One reconnection attempt + retry attempts per api call.
- ASSERT_EQ(11 * MAX_ATTEMPTS, mConnectCounter);
+ ASSERT_EQ(1, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) {
{
InSequence seq;
EXPECT_CALL(*mMockHal.get(), ping())
- .Times(Exactly(2))
- .WillOnce(Return(vibrator::HalResult<void>::failed()))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
.WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
}
ASSERT_EQ(0, mConnectCounter);
ASSERT_TRUE(mController->ping().isOk());
- // One connect + one retry.
- ASSERT_EQ(2, mConnectCounter);
+ ASSERT_EQ(1, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
@@ -323,7 +336,7 @@
ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
.isUnsupported());
- // One reconnection attempt per api call, no retry.
+ // One connection attempt per api call.
ASSERT_EQ(11, mConnectCounter);
}
@@ -337,19 +350,24 @@
return vibrator::HalResult<void>::ok();
});
EXPECT_CALL(*mMockHal.get(), ping())
- .Times(Exactly(MAX_ATTEMPTS))
+ .Times(Exactly(1))
.WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::failed()));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
}
std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
ASSERT_TRUE(mController->on(10ms, callback).isOk());
- ASSERT_TRUE(mController->ping().isFailed()); // Delete connected HAL pointer from controller.
- mMockHal.reset(); // Delete mock HAL pointer from test class.
+ ASSERT_TRUE(mController->ping().isFailed());
+ mMockHal.reset();
ASSERT_EQ(0, *callbackCounter.get());
- // Callback triggered even after HalWrapper was completely destroyed.
+ // Callback triggered even after HalWrapper was reconnected.
std::this_thread::sleep_for(15ms);
ASSERT_EQ(1, *callbackCounter.get());
}