Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f75d7b2d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add a new InputClassifier stage"

parents c82e9ec1 473174ea
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -35,6 +35,14 @@ public:
            mWidth(width), mHeight(height), mData(std::move(data)), mTimestamp(timestamp) {
    }

    bool operator==(const TouchVideoFrame& rhs) const {
        return mWidth == rhs.mWidth
                && mHeight == rhs.mHeight
                && mData == rhs.mData
                && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec
                && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec;
    }

    /**
     * Width of the frame
     */
+7 −1
Original line number Diff line number Diff line
@@ -16,21 +16,25 @@ cc_library_shared {
    name: "libinputflinger",

    srcs: [
        "InputClassifier.cpp",
        "InputDispatcher.cpp",
        "InputManager.cpp",
    ],

    shared_libs: [
        "android.hardware.input.classifier@1.0",
        "libinputflinger_base",
        "libinputreporter",
        "libinputreader",
        "libbase",
        "libbinder",
        "libcutils",
        "libhidlbase",
        "libinput",
        "liblog",
        "libutils",
        "libui",
        "server_configurable_flags",
    ],

    cflags: [
@@ -38,7 +42,9 @@ cc_library_shared {
        "-Wextra",
        "-Werror",
        "-Wno-unused-parameter",
        // TODO: Move inputflinger to its own process and mark it hidden
        // TODO(b/123097103): annotate InputDispatcher and uncomment the following line
        //"-Wthread-safety",
        // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
        //-fvisibility=hidden
    ],

+98 −0
Original line number Diff line number Diff line
/*
 * 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_BLOCKING_QUEUE_H
#define _UI_INPUT_BLOCKING_QUEUE_H

#include <condition_variable>
#include <mutex>
#include <vector>

namespace android {

/**
 * A FIFO queue that stores up to <i>capacity</i> objects.
 * Objects can always be added. Objects are added immediately.
 * If the queue is full, new objects cannot be added.
 *
 * The action of retrieving an object will block until an element is available.
 */
template <class T>
class BlockingQueue {
public:
    BlockingQueue(size_t capacity) : mCapacity(capacity) {
        mQueue.reserve(mCapacity);
    };

    /**
     * Retrieve and remove the oldest object.
     * Blocks execution while queue is empty.
     */
    T pop() {
        std::unique_lock<std::mutex> lock(mLock);
        mHasElements.wait(lock, [this]{ return !this->mQueue.empty(); });
        T t = std::move(mQueue.front());
        mQueue.erase(mQueue.begin());
        return std::move(t);
    };

    /**
     * Add a new object to the queue.
     * Does not block.
     * Return true if an element was successfully added.
     * Return false if the queue is full.
     */
    bool push(T&& t) {
        std::unique_lock<std::mutex> lock(mLock);
        if (mQueue.size() == mCapacity) {
            return false;
        }
        mQueue.push_back(std::move(t));
        mHasElements.notify_one();
        return true;
    };

    void erase(const std::function<bool(const T&)>& lambda) {
        std::unique_lock<std::mutex> lock(mLock);
        mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
                [&lambda](const T& t) { return lambda(t); }), mQueue.end());
    }

    /**
     * Remove all elements.
     * Does not block.
     */
    void clear() {
        std::scoped_lock lock(mLock);
        mQueue.clear();
    };

private:
    size_t mCapacity;
    /**
     * Used to signal that mQueue is non-empty.
     */
    std::condition_variable mHasElements;
    /**
     * Lock for accessing and waiting on elements.
     */
    std::mutex mLock;
    std::vector<T> mQueue; //GUARDED_BY(mLock)
};


} // namespace android
#endif
+692 −0

File added.

Preview size limit exceeded, changes collapsed.

+204 −0
Original line number Diff line number Diff line
/*
 * 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 <utils/RefBase.h>
#include <unordered_map>
#include <thread>

#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;
    virtual void reset() = 0;
    virtual void reset(const NotifyDeviceResetArgs& args) = 0;
};

/**
 * Base interface for an InputListener stage.
 * Provides classification to events.
 */
class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
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 : public MotionClassifierInterface {
public:
    MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
    ~MotionClassifier();
    /**
     * Classifies events asynchronously; that is, it doesn't block events on a classification,
     * but instead sends them over to the classifier HAL
     * and after a classification is determined,
     * it then marks the next event it sees in the stream with it.
     *
     * 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;

private:
    // The events that need to be sent to the HAL.
    BlockingQueue<ClassifierEvent> mEvents;
    /**
     * 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;
    /**
     * Print an error message if the caller is not on the InputClassifier thread.
     * Caller must supply the name of the calling function as __function__
     */
    void ensureHalThread(const char* function);
    /**
     * Call the InputClassifier HAL
     */
    void callInputClassifierHal();
    /**
     * Access to the InputClassifier HAL
     */
    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);
    // Should only be accessed through isResetNeeded() and setResetNeeded()
    bool mResetNeeded = false; //GUARDED_BY(mLock);
    /**
     * Check whether reset should be performed. Reset should be performed
     * if the eventTime of the current event is older than mLastDownTime,
     * i.e. a new gesture has already begun, but an older gesture is still being processed.
     */
    bool isResetNeeded(nsecs_t eventTime);
    void setResetNeeded(bool isNeeded);

    /**
     * Exit the InputClassifier HAL thread.
     * Useful for tests to ensure proper cleanup.
     */
    void requestExit();
};

/**
 * 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);
    virtual void notifyKey(const NotifyKeyArgs* args);
    virtual void notifyMotion(const NotifyMotionArgs* args);
    virtual void notifySwitch(const NotifySwitchArgs* args);
    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);

private:
    std::unique_ptr<MotionClassifierInterface> mMotionClassifier = nullptr;
    // The next stage to pass input events to
    sp<InputListenerInterface> mListener;
};

} // namespace android
#endif
 No newline at end of file
Loading