|  | /* | 
|  | * Copyright (C) 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. | 
|  | */ | 
|  |  | 
|  | #include "PointerControllerContext.h" | 
|  | #include "PointerController.h" | 
|  |  | 
|  | namespace { | 
|  | // Time to wait before starting the fade when the pointer is inactive. | 
|  | const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds | 
|  | const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL;   // 3 seconds | 
|  |  | 
|  | // The number of events to be read at once for DisplayEventReceiver. | 
|  | const int EVENT_BUFFER_SIZE = 100; | 
|  | } // namespace | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | // --- PointerControllerContext --- | 
|  |  | 
|  | PointerControllerContext::PointerControllerContext( | 
|  | const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, | 
|  | const sp<SpriteController>& spriteController, PointerController& controller) | 
|  | : mPolicy(policy), | 
|  | mLooper(looper), | 
|  | mSpriteController(spriteController), | 
|  | mHandler(new MessageHandler()), | 
|  | mCallback(new LooperCallback()), | 
|  | mController(controller), | 
|  | mAnimator(*this) { | 
|  | std::scoped_lock lock(mLock); | 
|  | mLocked.inactivityTimeout = InactivityTimeout::NORMAL; | 
|  | } | 
|  |  | 
|  | PointerControllerContext::~PointerControllerContext() { | 
|  | mLooper->removeMessages(mHandler); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::setInactivityTimeout(InactivityTimeout inactivityTimeout) { | 
|  | std::scoped_lock lock(mLock); | 
|  |  | 
|  | if (mLocked.inactivityTimeout != inactivityTimeout) { | 
|  | mLocked.inactivityTimeout = inactivityTimeout; | 
|  | resetInactivityTimeoutLocked(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::resetInactivityTimeout() { | 
|  | std::scoped_lock lock(mLock); | 
|  | resetInactivityTimeoutLocked(); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::resetInactivityTimeoutLocked() REQUIRES(mLock) { | 
|  | mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); | 
|  |  | 
|  | nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT | 
|  | ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT | 
|  | : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; | 
|  | mLooper->sendMessageDelayed(timeout, mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::removeInactivityTimeout() { | 
|  | std::scoped_lock lock(mLock); | 
|  | mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); | 
|  | } | 
|  |  | 
|  | nsecs_t PointerControllerContext::getAnimationTime() REQUIRES(mAnimator.mLock) { | 
|  | return mAnimator.getAnimationTimeLocked(); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::setHandlerController(std::shared_ptr<PointerController> controller) { | 
|  | mHandler->pointerController = controller; | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::setCallbackController( | 
|  | std::shared_ptr<PointerController> controller) { | 
|  | mCallback->pointerController = controller; | 
|  | } | 
|  |  | 
|  | sp<PointerControllerPolicyInterface> PointerControllerContext::getPolicy() { | 
|  | return mPolicy; | 
|  | } | 
|  |  | 
|  | sp<SpriteController> PointerControllerContext::getSpriteController() { | 
|  | return mSpriteController; | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::handleDisplayEvents() { | 
|  | mAnimator.handleVsyncEvents(); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::MessageHandler::handleMessage(const Message& message) { | 
|  | std::shared_ptr<PointerController> controller = pointerController.lock(); | 
|  |  | 
|  | if (controller == nullptr) { | 
|  | ALOGE("PointerController instance was released before processing message: what=%d", | 
|  | message.what); | 
|  | return; | 
|  | } | 
|  | switch (message.what) { | 
|  | case MSG_INACTIVITY_TIMEOUT: | 
|  | controller->doInactivityTimeout(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | int PointerControllerContext::LooperCallback::handleEvent(int /* fd */, int events, | 
|  | void* /* data */) { | 
|  | std::shared_ptr<PointerController> controller = pointerController.lock(); | 
|  | if (controller == nullptr) { | 
|  | ALOGW("PointerController instance was released with pending callbacks.  events=0x%x", | 
|  | events); | 
|  | return 0; // Remove the callback, the PointerController is gone anyways | 
|  | } | 
|  | if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { | 
|  | ALOGE("Display event receiver pipe was closed or an error occurred.  events=0x%x", events); | 
|  | return 0; // remove the callback | 
|  | } | 
|  |  | 
|  | if (!(events & Looper::EVENT_INPUT)) { | 
|  | ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events); | 
|  | return 1; // keep the callback | 
|  | } | 
|  |  | 
|  | controller->mContext.handleDisplayEvents(); | 
|  | return 1; // keep the callback | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::addAnimationCallback(int32_t displayId, | 
|  | std::function<bool(nsecs_t)> callback) { | 
|  | mAnimator.addCallback(displayId, callback); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::removeAnimationCallback(int32_t displayId) { | 
|  | mAnimator.removeCallback(displayId); | 
|  | } | 
|  |  | 
|  | PointerControllerContext::PointerAnimator::PointerAnimator(PointerControllerContext& context) | 
|  | : mContext(context) { | 
|  | initializeDisplayEventReceiver(); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::PointerAnimator::initializeDisplayEventReceiver() { | 
|  | if (mDisplayEventReceiver.initCheck() == NO_ERROR) { | 
|  | mContext.mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, | 
|  | Looper::EVENT_INPUT, mContext.mCallback, nullptr); | 
|  | } else { | 
|  | ALOGE("Failed to initialize DisplayEventReceiver."); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::PointerAnimator::addCallback(int32_t displayId, | 
|  | std::function<bool(nsecs_t)> callback) { | 
|  | std::scoped_lock lock(mLock); | 
|  | mLocked.callbacks[displayId] = callback; | 
|  | startAnimationLocked(); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::PointerAnimator::removeCallback(int32_t displayId) { | 
|  | std::scoped_lock lock(mLock); | 
|  | auto it = mLocked.callbacks.find(displayId); | 
|  | if (it == mLocked.callbacks.end()) { | 
|  | return; | 
|  | } | 
|  | mLocked.callbacks.erase(it); | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::PointerAnimator::handleVsyncEvents() { | 
|  | bool gotVsync = false; | 
|  | ssize_t n; | 
|  | nsecs_t timestamp; | 
|  | DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; | 
|  | while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { | 
|  | for (size_t i = 0; i < static_cast<size_t>(n); ++i) { | 
|  | if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { | 
|  | timestamp = buf[i].header.timestamp; | 
|  | gotVsync = true; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (gotVsync) { | 
|  | std::scoped_lock lock(mLock); | 
|  | mLocked.animationPending = false; | 
|  | handleCallbacksLocked(timestamp); | 
|  | } | 
|  | } | 
|  |  | 
|  | nsecs_t PointerControllerContext::PointerAnimator::getAnimationTimeLocked() REQUIRES(mLock) { | 
|  | return mLocked.animationTime; | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::PointerAnimator::startAnimationLocked() REQUIRES(mLock) { | 
|  | if (!mLocked.animationPending) { | 
|  | mLocked.animationPending = true; | 
|  | mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); | 
|  | mDisplayEventReceiver.requestNextVsync(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PointerControllerContext::PointerAnimator::handleCallbacksLocked(nsecs_t timestamp) | 
|  | REQUIRES(mLock) { | 
|  | for (auto it = mLocked.callbacks.begin(); it != mLocked.callbacks.end();) { | 
|  | bool keepCallback = it->second(timestamp); | 
|  | if (!keepCallback) { | 
|  | it = mLocked.callbacks.erase(it); | 
|  | } else { | 
|  | ++it; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!mLocked.callbacks.empty()) { | 
|  | startAnimationLocked(); | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace android |