blob: fe25130bd82e95a964e0cbdea1891b2ee369e076 [file] [log] [blame]
/*
* Copyright 2023 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.
*/
#pragma once
#include <android-base/logging.h>
#include "../dispatcher/InputDispatcher.h"
using android::base::Result;
using android::gui::Pid;
using android::gui::TouchOcclusionMode;
using android::gui::Uid;
using android::gui::WindowInfo;
using android::gui::WindowInfoHandle;
namespace android {
namespace inputdispatcher {
namespace {
// The default pid and uid for windows created by the test.
constexpr gui::Pid WINDOW_PID{999};
constexpr gui::Uid WINDOW_UID{1001};
static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
} // namespace
class FakeInputReceiver {
public:
std::unique_ptr<InputEvent> consumeEvent(std::chrono::milliseconds timeout) {
uint32_t consumeSeq = 0;
std::unique_ptr<InputEvent> event;
std::chrono::time_point start = std::chrono::steady_clock::now();
status_t result = WOULD_BLOCK;
while (result == WOULD_BLOCK) {
InputEvent* rawEventPtr = nullptr;
result = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
&rawEventPtr);
event = std::unique_ptr<InputEvent>(rawEventPtr);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > timeout) {
if (timeout != 0ms) {
LOG(ERROR) << "Waited too long for consumer to produce an event, giving up";
}
break;
}
}
// Events produced by this factory are owned pointers.
if (result != OK) {
if (timeout == 0ms) {
// This is likely expected. No need to log.
} else {
LOG(ERROR) << "Received result = " << result << " from consume";
}
return nullptr;
}
result = mConsumer.sendFinishedSignal(consumeSeq, true);
if (result != OK) {
LOG(ERROR) << "Received result = " << result << " from sendFinishedSignal";
}
return event;
}
explicit FakeInputReceiver(std::unique_ptr<InputChannel> channel, const std::string name)
: mConsumer(std::move(channel)) {}
virtual ~FakeInputReceiver() {}
private:
std::unique_ptr<InputChannel> mClientChannel;
InputConsumer mConsumer;
DynamicInputEventFactory mEventFactory;
};
class FakeWindowHandle : public WindowInfoHandle {
public:
static const int32_t WIDTH = 600;
static const int32_t HEIGHT = 800;
FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
InputDispatcher& dispatcher, const std::string name, int32_t displayId)
: mName(name) {
Result<std::unique_ptr<InputChannel>> channel = dispatcher.createInputChannel(name);
mInfo.token = (*channel)->getConnectionToken();
mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
mInfo.id = sId++;
mInfo.name = name;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
mInfo.alpha = 1.0;
mInfo.frame.left = 0;
mInfo.frame.top = 0;
mInfo.frame.right = WIDTH;
mInfo.frame.bottom = HEIGHT;
mInfo.transform.set(0, 0);
mInfo.globalScaleFactor = 1.0;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
mInfo.ownerPid = WINDOW_PID;
mInfo.ownerUid = WINDOW_UID;
mInfo.displayId = displayId;
mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
}
sp<FakeWindowHandle> clone(int32_t displayId) {
sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)");
handle->mInfo = mInfo;
handle->mInfo.displayId = displayId;
handle->mInfo.id = sId++;
handle->mInputReceiver = mInputReceiver;
return handle;
}
void setTouchable(bool touchable) {
mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable);
}
void setFocusable(bool focusable) {
mInfo.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable);
}
void setVisible(bool visible) {
mInfo.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
}
void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
mInfo.dispatchingTimeout = timeout;
}
void setPaused(bool paused) {
mInfo.setInputConfig(WindowInfo::InputConfig::PAUSE_DISPATCHING, paused);
}
void setPreventSplitting(bool preventSplitting) {
mInfo.setInputConfig(WindowInfo::InputConfig::PREVENT_SPLITTING, preventSplitting);
}
void setSlippery(bool slippery) {
mInfo.setInputConfig(WindowInfo::InputConfig::SLIPPERY, slippery);
}
void setWatchOutsideTouch(bool watchOutside) {
mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
}
void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); }
void setInterceptsStylus(bool interceptsStylus) {
mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus);
}
void setDropInput(bool dropInput) {
mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput);
}
void setDropInputIfObscured(bool dropInputIfObscured) {
mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured);
}
void setNoInputChannel(bool noInputChannel) {
mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
}
void setDisableUserActivity(bool disableUserActivity) {
mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
}
void setAlpha(float alpha) { mInfo.alpha = alpha; }
void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) {
mInfo.frame.left = frame.left;
mInfo.frame.top = frame.top;
mInfo.frame.right = frame.right;
mInfo.frame.bottom = frame.bottom;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
const Rect logicalDisplayFrame = displayTransform.transform(frame);
ui::Transform translate;
translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top);
mInfo.transform = translate * displayTransform;
}
void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
void setIsWallpaper(bool isWallpaper) {
mInfo.setInputConfig(WindowInfo::InputConfig::IS_WALLPAPER, isWallpaper);
}
void setDupTouchToWallpaper(bool hasWallpaper) {
mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
}
void setTrustedOverlay(bool trustedOverlay) {
mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay);
}
void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
}
void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout) {
if (mInputReceiver == nullptr) {
return nullptr;
}
return mInputReceiver->consumeEvent(timeout);
}
void consumeMotion() {
std::unique_ptr<InputEvent> event = consume(100ms);
if (event == nullptr) {
LOG(FATAL) << mName << ": expected a MotionEvent, but didn't get one.";
return;
}
if (event->getType() != InputEventType::MOTION) {
LOG(FATAL) << mName << " expected a MotionEvent, got " << *event;
return;
}
}
sp<IBinder> getToken() { return mInfo.token; }
const std::string& getName() { return mName; }
void setOwnerInfo(Pid ownerPid, Uid ownerUid) {
mInfo.ownerPid = ownerPid;
mInfo.ownerUid = ownerUid;
}
Pid getPid() const { return mInfo.ownerPid; }
void destroyReceiver() { mInputReceiver = nullptr; }
private:
FakeWindowHandle(std::string name) : mName(name){};
const std::string mName;
std::shared_ptr<FakeInputReceiver> mInputReceiver;
static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
friend class sp<FakeWindowHandle>;
};
std::atomic<int32_t> FakeWindowHandle::sId{1};
} // namespace inputdispatcher
} // namespace android