summaryrefslogtreecommitdiff
path: root/services/inputflinger/InputClassifier.cpp
diff options
context:
space:
mode:
author Siarhei Vishniakou <svv@google.com> 2020-03-04 17:48:39 -0800
committer Siarhei Vishniakou <svv@google.com> 2020-03-20 20:38:33 +0000
commit958c4d078d3d9d97a3577577a1aa606311d9544d (patch)
treeb18d71d004b0043cc4775c735f2ba600959b41da /services/inputflinger/InputClassifier.cpp
parentca3425964ae63a69dc1e8bf8d9a0995fd8cc120e (diff)
Properly initialize MotionClassifier
InputClassifier will now play a more active role in managing the lifecycle of MotionClassifier. In the previous version of the code, we had a circular reference: MotionClassifier owned a wp<InputClassifier>, that sometimes got promoted to sp<>. But the InputClassifier was the official owner of MotionClassifier, and owned a unique_ptr<MotionClassifier>. The owner of InputClassifier in real code is InputManager, and in test code, it's the test class. When the owner of InputClassifier destroyed InputClassifier, this could sometimes happen at the time when the MotionClassifier also held a reference to the InputClassifier. That meant that the proper owner of InputClassifier was not invoking the destructor. Instead, the MotionClassifier was invoking the InputClassifier destructor. To fix the situation, we now do the following: 1. InputClassifier will never die before MotionClassifier. 2. MotionClassifier constructor is now allowed to block. It would block for some time because calling getService takes a while. To account for this, InputClassifier launches a new thread to create MotionClassifier. 3. When MotionClassifier is ready to process events, InputClassifier updates mMotionClassifier, which makes it non-null. 4. We now create a separate death recipient, which is co-owned by InputClassifier and MotionClassifier. This is done so that the refcount of the deathrecipient does not affect the refcount of InputClassifier, and thus enforces the ownership of InputClassifier by the InputManager. Now, no one can call ~InputClassifier except for its real owner. 5. MotionClassifier will subscribe the death recipient to the death of the HAL. InputClassifier will delete MotionClassifier if HAL dies. MotionClassifier no longer holds on to the death recipient. 6. We move the loop of the MotionClassifier thread to focus only on processing events. That thread will no longer do any initialization. 7. Remove the thread check inside MotionClassifier. It isn't really useful, now that there's only 1 function for the entire thread. Ownership summary: Both InputClassifier and MotionClassifier own DeathRecipient. DeathRecipient has a reference to InputClassifier. Thus, we must guarantee that DeathRecipient dies before InputClassifier. InputClassifier owns MotionClassifier. This is OK, since even if InputClassifier dies, it will first delete MotionClassifier. That will cause MotionClassifier to release 1 refCount from DeathRecipient. That means the only reference remaining to DeathRecipient will be inside InputClassifier, so InputClassifier will always be alive until DeathRecipient is dead. Similar argument applies if MotionClassifier and DeathRecipient die in different order (as observed from InputClassifier). Tests: 1) Manually running inputflinger_tests on cuttlefish: build/launch cuttlefish using go/acloud m inputflinger_tests adb push out/target/product/vsoc_x86/data/nativetest/inputflinger_tests/inputflinger_tests /data/nativetest/inputflinger_tests/inputflinger_tests adb shell /data/nativetest/inputflinger_tests # ./inputflinger_tests --gtest_filter=*InputClassifierTest* --gtest_repeat=1000 --gtest_break_on_failure 2) Boot flame and open logcat. Observe in logcat: StartInputManagerService took to complete: 2ms Previously, in synchronous approach ( b/130184032) it was about 100 ms (so we did not regress). 3) Kill the HAL while system_server is running, and dumpsys input before and after: adb shell dumpsys input (observe that MotionClassifier is non-null on flame) adb shell -t killall android.hardware.input.classifier@1.0-service adb shell dumpsys input (observe that MotionCLassifier is null) Bug: 148311342 Test: see "Tests" section above Change-Id: Ic76b82bd5f2cd374e3b001400eb495ca36de7353 Merged-In: Ic76b82bd5f2cd374e3b001400eb495ca36de7353 (cherry picked from commit 1652397ab77904092509a0315ad026f0b4f10a18)
Diffstat (limited to 'services/inputflinger/InputClassifier.cpp')
-rw-r--r--services/inputflinger/InputClassifier.cpp134
1 files changed, 53 insertions, 81 deletions
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index ef1a2247e9..6e51fb2717 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -46,8 +46,6 @@ using namespace android::hardware::input;
namespace android {
-static constexpr bool DEBUG = false;
-
// Category (=namespace) name for the input settings that are applied at boot time
static const char* INPUT_NATIVE_BOOT = "input_native_boot";
// Feature flag name for the deep press feature
@@ -141,53 +139,46 @@ std::optional<int32_t> ClassifierEvent::getDeviceId() const {
// --- MotionClassifier ---
-MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) :
- mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
- mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
+MotionClassifier::MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
+ : mEvents(MAX_EVENTS), mService(service) {
+ // Under normal operation, we do not need to reset the HAL here. But in the case where system
+ // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
+ // have received events in the past. That means, that HAL could be in an inconsistent state
+ // once it receives events from the newly created MotionClassifier.
+ mEvents.push(ClassifierEvent::createHalResetEvent());
+
+ mHalThread = std::thread(&MotionClassifier::processEvents, this);
#if defined(__linux__)
// Set the thread name for debugging
pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
#endif
}
-/**
- * This function may block for some time to initialize the HAL, so it should only be called
- * from the "InputClassifier HAL" thread.
- */
-bool MotionClassifier::init() {
- ensureHalThread(__func__);
+std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient) {
+ if (!deepPressEnabled()) {
+ // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
+ // When MotionClassifier is null, InputClassifier will forward all events
+ // to the next InputListener, unmodified.
+ return nullptr;
+ }
sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
classifier::V1_0::IInputClassifier::getService();
if (!service) {
// Not really an error, maybe the device does not have this HAL,
// but somehow the feature flag is flipped
ALOGI("Could not obtain InputClassifier HAL");
- return false;
+ return nullptr;
}
- sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
- if (recipient != nullptr) {
- const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
- if (!linked) {
- ALOGE("Could not link MotionClassifier to the HAL death");
- return false;
- }
- }
-
- // Under normal operation, we do not need to reset the HAL here. But in the case where system
- // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
- // have received events in the past. That means, that HAL could be in an inconsistent state
- // once it receives events from the newly created MotionClassifier.
- mEvents.push(ClassifierEvent::createHalResetEvent());
-
- {
- std::scoped_lock lock(mLock);
- if (mService) {
- ALOGE("MotionClassifier::%s should only be called once", __func__);
- }
- mService = service;
+ const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
+ if (!linked) {
+ ALOGE("Could not link death recipient to the HAL death");
+ return nullptr;
}
- return true;
+ // Using 'new' to access a non-public constructor
+ return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
}
MotionClassifier::~MotionClassifier() {
@@ -195,14 +186,6 @@ MotionClassifier::~MotionClassifier() {
mHalThread.join();
}
-void MotionClassifier::ensureHalThread(const char* function) {
- if (DEBUG) {
- if (std::this_thread::get_id() != mHalThread.get_id()) {
- LOG_FATAL("Function %s should only be called from InputClassifier thread", function);
- }
- }
-}
-
/**
* Obtain the classification from the HAL for a given MotionEvent.
* Should only be called from the InputClassifier thread (mHalThread).
@@ -213,23 +196,7 @@ void MotionClassifier::ensureHalThread(const char* function) {
* To remove any possibility of negatively affecting the touch latency, the HAL
* is called from a dedicated thread.
*/
-void MotionClassifier::callInputClassifierHal() {
- ensureHalThread(__func__);
- const bool initialized = init();
- if (!initialized) {
- // MotionClassifier no longer useful.
- // Deliver death notification from a separate thread
- // because ~MotionClassifier may be invoked, which calls mHalThread.join()
- std::thread([deathRecipient = mDeathRecipient](){
- sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
- if (recipient != nullptr) {
- recipient->serviceDied(0 /*cookie*/, nullptr);
- }
- }).detach();
- return;
- }
- // From this point on, mService is guaranteed to be non-null.
-
+void MotionClassifier::processEvents() {
while (true) {
ClassifierEvent event = mEvents.pop();
bool halResponseOk = true;
@@ -383,24 +350,30 @@ void MotionClassifier::dump(std::string& dump) {
}
}
+// --- HalDeathRecipient
-// --- InputClassifier ---
+InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
-InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
- mListener(listener) {
- // The rest of the initialization is done in onFirstRef, because we need to obtain
- // an sp to 'this' in order to register for HAL death notifications
+void InputClassifier::HalDeathRecipient::serviceDied(
+ uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
+ sp<android::hidl::base::V1_0::IBase> service = who.promote();
+ if (service) {
+ service->unlinkToDeath(this);
+ }
+ mParent.setMotionClassifier(nullptr);
}
-void InputClassifier::onFirstRef() {
- if (!deepPressEnabled()) {
- // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
- // When MotionClassifier is null, InputClassifier will forward all events
- // to the next InputListener, unmodified.
- return;
- }
- std::scoped_lock lock(mLock);
- mMotionClassifier = std::make_unique<MotionClassifier>(this);
+// --- InputClassifier ---
+
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
+ : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {
+ mInitializeMotionClassifierThread = std::thread(
+ [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
+ "Create MotionClassifier");
+#endif
}
void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
@@ -441,15 +414,10 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
mListener->notifyDeviceReset(args);
}
-void InputClassifier::serviceDied(uint64_t /*cookie*/,
- const wp<android::hidl::base::V1_0::IBase>& who) {
+void InputClassifier::setMotionClassifier(
+ std::unique_ptr<MotionClassifierInterface> motionClassifier) {
std::scoped_lock lock(mLock);
- ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
- mMotionClassifier = nullptr;
- sp<android::hidl::base::V1_0::IBase> service = who.promote();
- if (service) {
- service->unlinkToDeath(this);
- }
+ mMotionClassifier = std::move(motionClassifier);
}
void InputClassifier::dump(std::string& dump) {
@@ -465,4 +433,8 @@ void InputClassifier::dump(std::string& dump) {
dump += "\n";
}
+InputClassifier::~InputClassifier() {
+ mInitializeMotionClassifierThread.join();
+}
+
} // namespace android