You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
10 KiB
282 lines
10 KiB
/*
|
|
* Copyright (C) 2019 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.
|
|
*/
|
|
|
|
#ifndef _UI_INPUT_CLASSIFIER_H
|
|
#define _UI_INPUT_CLASSIFIER_H
|
|
|
|
#include <android-base/thread_annotations.h>
|
|
#include <utils/RefBase.h>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
|
|
#include "BlockingQueue.h"
|
|
#include "InputListener.h"
|
|
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
|
|
|
|
namespace android {
|
|
|
|
enum class ClassifierEventType : uint8_t {
|
|
MOTION = 0,
|
|
DEVICE_RESET = 1,
|
|
HAL_RESET = 2,
|
|
EXIT = 3,
|
|
};
|
|
|
|
struct ClassifierEvent {
|
|
ClassifierEventType type;
|
|
std::unique_ptr<NotifyArgs> args;
|
|
|
|
ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args);
|
|
ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args);
|
|
ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args);
|
|
ClassifierEvent(ClassifierEvent&& other);
|
|
ClassifierEvent& operator=(ClassifierEvent&& other);
|
|
|
|
// Convenience function to create a HAL_RESET event
|
|
static ClassifierEvent createHalResetEvent();
|
|
// Convenience function to create an EXIT event
|
|
static ClassifierEvent createExitEvent();
|
|
|
|
std::optional<int32_t> getDeviceId() const;
|
|
};
|
|
|
|
// --- Interfaces ---
|
|
|
|
/**
|
|
* Interface for adding a MotionClassification to NotifyMotionArgs.
|
|
*
|
|
* To implement, override the classify function.
|
|
*/
|
|
class MotionClassifierInterface {
|
|
public:
|
|
MotionClassifierInterface() { }
|
|
virtual ~MotionClassifierInterface() { }
|
|
/**
|
|
* Based on the motion event described by NotifyMotionArgs,
|
|
* provide a MotionClassification for the current gesture.
|
|
*/
|
|
virtual MotionClassification classify(const NotifyMotionArgs& args) = 0;
|
|
/**
|
|
* Reset all internal HAL state.
|
|
*/
|
|
virtual void reset() = 0;
|
|
/**
|
|
* Reset HAL state for a specific device.
|
|
*/
|
|
virtual void reset(const NotifyDeviceResetArgs& args) = 0;
|
|
|
|
/**
|
|
* Dump the state of the motion classifier
|
|
*/
|
|
virtual void dump(std::string& dump) = 0;
|
|
};
|
|
|
|
/**
|
|
* Base interface for an InputListener stage.
|
|
* Provides classification to events.
|
|
*/
|
|
class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
|
|
public:
|
|
virtual void setMotionClassifierEnabled(bool enabled) = 0;
|
|
/**
|
|
* Dump the state of the input classifier.
|
|
* This method may be called on any thread (usually by the input manager).
|
|
*/
|
|
virtual void dump(std::string& dump) = 0;
|
|
protected:
|
|
InputClassifierInterface() { }
|
|
virtual ~InputClassifierInterface() { }
|
|
};
|
|
|
|
// --- Implementations ---
|
|
|
|
/**
|
|
* Implementation of MotionClassifierInterface that calls the InputClassifier HAL
|
|
* in order to determine the classification for the current gesture.
|
|
*
|
|
* The InputClassifier HAL may keep track of the entire gesture in order to determine
|
|
* the classification, and may be hardware-specific. It may use the data in
|
|
* NotifyMotionArgs::videoFrames field to drive the classification decisions.
|
|
* The HAL is called from a separate thread.
|
|
*/
|
|
class MotionClassifier final : public MotionClassifierInterface {
|
|
public:
|
|
/*
|
|
* Create an instance of MotionClassifier.
|
|
* The death recipient, if provided, will be subscribed to the HAL death.
|
|
* The death recipient could be used to destroy MotionClassifier.
|
|
*
|
|
* This function should be called asynchronously, because getService takes a long time.
|
|
*/
|
|
static std::unique_ptr<MotionClassifierInterface> create(
|
|
sp<android::hardware::hidl_death_recipient> deathRecipient);
|
|
|
|
~MotionClassifier();
|
|
|
|
/**
|
|
* Classifies events asynchronously; that is, it doesn't block events on a classification,
|
|
* but instead sends them over to the classifier HAL. After a classification of a specific
|
|
* event is determined, MotionClassifier then marks the next event in the stream with this
|
|
* classification.
|
|
*
|
|
* Therefore, it is acceptable to have the classifications be delayed by 1-2 events
|
|
* in a particular gesture.
|
|
*/
|
|
virtual MotionClassification classify(const NotifyMotionArgs& args) override;
|
|
virtual void reset() override;
|
|
virtual void reset(const NotifyDeviceResetArgs& args) override;
|
|
|
|
virtual void dump(std::string& dump) override;
|
|
|
|
private:
|
|
friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
|
|
explicit MotionClassifier(
|
|
sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
|
|
|
|
// The events that need to be sent to the HAL.
|
|
BlockingQueue<ClassifierEvent> mEvents;
|
|
/**
|
|
* Add an event to the queue mEvents.
|
|
*/
|
|
void enqueueEvent(ClassifierEvent&& event);
|
|
/**
|
|
* Thread that will communicate with InputClassifier HAL.
|
|
* This should be the only thread that communicates with InputClassifier HAL,
|
|
* because this thread is allowed to block on the HAL calls.
|
|
*/
|
|
std::thread mHalThread;
|
|
/**
|
|
* Process events and call the InputClassifier HAL
|
|
*/
|
|
void processEvents();
|
|
/**
|
|
* Access to the InputClassifier HAL. May be null if init() hasn't completed yet.
|
|
* When init() successfully completes, mService is guaranteed to remain non-null and to not
|
|
* change its value until MotionClassifier is destroyed.
|
|
* This variable is *not* guarded by mLock in the InputClassifier thread, because
|
|
* that thread knows exactly when this variable is initialized.
|
|
* When accessed in any other thread, mService is checked for nullness with a lock.
|
|
*/
|
|
sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService;
|
|
std::mutex mLock;
|
|
/**
|
|
* Per-device input classifications. Should only be accessed using the
|
|
* getClassification / setClassification methods.
|
|
*/
|
|
std::unordered_map<int32_t /*deviceId*/, MotionClassification>
|
|
mClassifications GUARDED_BY(mLock);
|
|
/**
|
|
* Set the current classification for a given device.
|
|
*/
|
|
void setClassification(int32_t deviceId, MotionClassification classification);
|
|
/**
|
|
* Get the current classification for a given device.
|
|
*/
|
|
MotionClassification getClassification(int32_t deviceId);
|
|
void updateClassification(int32_t deviceId, nsecs_t eventTime,
|
|
MotionClassification classification);
|
|
/**
|
|
* Clear all current classifications
|
|
*/
|
|
void clearClassifications();
|
|
/**
|
|
* Per-device times when the last ACTION_DOWN was received.
|
|
* Used to reject late classifications that do not belong to the current gesture.
|
|
*
|
|
* Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion.
|
|
*/
|
|
std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/> mLastDownTimes GUARDED_BY(mLock);
|
|
|
|
void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
|
|
|
|
void clearDeviceState(int32_t deviceId);
|
|
|
|
/**
|
|
* Exit the InputClassifier HAL thread.
|
|
* Useful for tests to ensure proper cleanup.
|
|
*/
|
|
void requestExit();
|
|
/**
|
|
* Return string status of mService
|
|
*/
|
|
const char* getServiceStatus() REQUIRES(mLock);
|
|
};
|
|
|
|
/**
|
|
* Implementation of the InputClassifierInterface.
|
|
* Represents a separate stage of input processing. All of the input events go through this stage.
|
|
* Acts as a passthrough for all input events except for motion events.
|
|
* The events of motion type are sent to MotionClassifier.
|
|
*/
|
|
class InputClassifier : public InputClassifierInterface {
|
|
public:
|
|
explicit InputClassifier(const sp<InputListenerInterface>& listener);
|
|
|
|
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
|
|
virtual void notifyKey(const NotifyKeyArgs* args) override;
|
|
virtual void notifyMotion(const NotifyMotionArgs* args) override;
|
|
virtual void notifySwitch(const NotifySwitchArgs* args) override;
|
|
virtual void notifySensor(const NotifySensorArgs* args) override;
|
|
virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
|
|
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
|
|
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
|
|
|
|
virtual void dump(std::string& dump) override;
|
|
|
|
~InputClassifier();
|
|
|
|
// Called from InputManager
|
|
virtual void setMotionClassifierEnabled(bool enabled) override;
|
|
|
|
private:
|
|
// Protect access to mMotionClassifier, since it may become null via a hidl callback
|
|
std::mutex mLock;
|
|
// The next stage to pass input events to
|
|
sp<InputListenerInterface> mListener;
|
|
|
|
std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
|
|
std::thread mInitializeMotionClassifierThread;
|
|
/**
|
|
* Set the value of mMotionClassifier.
|
|
* This is called from 2 different threads:
|
|
* 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier
|
|
* 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause
|
|
* mMotionClassifier to become nullptr.
|
|
*/
|
|
void setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier);
|
|
|
|
/**
|
|
* The deathRecipient will call setMotionClassifier(null) when the HAL dies.
|
|
*/
|
|
class HalDeathRecipient : public android::hardware::hidl_death_recipient {
|
|
public:
|
|
explicit HalDeathRecipient(InputClassifier& parent);
|
|
virtual void serviceDied(uint64_t cookie,
|
|
const wp<android::hidl::base::V1_0::IBase>& who) override;
|
|
|
|
private:
|
|
InputClassifier& mParent;
|
|
};
|
|
// We retain a reference to death recipient, because the death recipient will be calling
|
|
// ~MotionClassifier if the HAL dies.
|
|
// If we don't retain a reference, and MotionClassifier is the only owner of the death
|
|
// recipient, the serviceDied call will cause death recipient to call its own destructor.
|
|
sp<HalDeathRecipient> mHalDeathRecipient;
|
|
};
|
|
|
|
} // namespace android
|
|
#endif
|