Merge "Enable app data isolation by default"
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
index a8f7d92..b1943a4 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -14,6 +14,7 @@
"-Wall",
"-Wextra",
],
+ export_include_dirs: ["."],
}
cc_test {
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 4ee9f55..ee44cf5 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -85,6 +85,16 @@
return policyN1 - policyN2;
}
+static int bpf_obj_get_wronly(const char *pathname) {
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.pathname = ptr_to_u64((void *)pathname);
+ attr.file_flags = BPF_F_WRONLY;
+
+ return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
+}
+
static bool initGlobals() {
std::lock_guard<std::mutex> guard(gInitializedMutex);
if (gInitialized) return true;
@@ -153,17 +163,17 @@
bool startTrackingUidTimes() {
if (!initGlobals()) return false;
- unique_fd fd(bpf_obj_get(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
- if (fd < 0) return false;
+ unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
+ if (cpuPolicyFd < 0) return false;
for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) {
for (auto &cpu : gPolicyCpus[i]) {
- if (writeToMapEntry(fd, &cpu, &i, BPF_ANY)) return false;
+ if (writeToMapEntry(cpuPolicyFd, &cpu, &i, BPF_ANY)) return false;
}
}
- unique_fd fd2(bpf_obj_get(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
- if (fd2 < 0) return false;
+ unique_fd freqToIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
+ if (freqToIdxFd < 0) return false;
freq_idx_key_t key;
for (uint32_t i = 0; i < gNPolicies; ++i) {
key.policy = i;
@@ -173,14 +183,41 @@
// The uid_times map still uses 0-based indexes, and the sched_switch program handles
// conversion between them, so this does not affect our map reading code.
uint32_t idx = j + 1;
- if (writeToMapEntry(fd2, &key, &idx, BPF_ANY)) return false;
+ if (writeToMapEntry(freqToIdxFd, &key, &idx, BPF_ANY)) return false;
}
}
+ unique_fd cpuLastUpdateFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_last_update_map"));
+ if (cpuLastUpdateFd < 0) return false;
+ std::vector<uint64_t> zeros(get_nprocs_conf(), 0);
+ uint32_t zero = 0;
+ if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false;
+
+ unique_fd nrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_nr_active_map"));
+ if (nrActiveFd < 0) return false;
+ if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false;
+
+ unique_fd policyNrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_nr_active_map"));
+ if (policyNrActiveFd < 0) return false;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false;
+ }
+
+ unique_fd policyFreqIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
+ if (policyFreqIdxFd < 0) return false;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false;
+ }
+
return attachTracepointProgram("sched", "sched_switch") &&
attachTracepointProgram("power", "cpu_frequency");
}
+std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() {
+ if (!gInitialized && !initGlobals()) return {};
+ return gPolicyFreqs;
+}
+
// Retrieve the times in ns that uid spent running at each CPU frequency.
// Return contains no value on error, otherwise it contains a vector of vectors using the format:
// [[t0_0, t0_1, ...],
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index f620715..49469d8 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -26,6 +26,7 @@
std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid);
std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
getUidsCpuFreqTimes();
+std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs();
struct concurrent_time_t {
std::vector<uint64_t> active;
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index e82aad8..23d87fd 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -367,5 +367,16 @@
ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end());
}
+TEST(TimeInStateTest, GetCpuFreqs) {
+ auto freqs = getCpuFreqs();
+ ASSERT_TRUE(freqs.has_value());
+
+ auto times = getUidCpuFreqTimes(0);
+ ASSERT_TRUE(times.has_value());
+
+ ASSERT_EQ(freqs->size(), times->size());
+ for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size());
+}
+
} // namespace bpf
} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 8d36ba7..04749e6 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -112,22 +112,31 @@
if (consumed != OK) {
return nullptr;
}
- mInputConsumer->sendFinishedSignal(seqId, true);
+ status_t status = mInputConsumer->sendFinishedSignal(seqId, true);
+ EXPECT_EQ(OK, status) << "Could not send finished signal";
return ev;
}
+ void assertFocusChange(bool hasFocus) {
+ InputEvent *ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, ev->getType());
+ FocusEvent *focusEvent = static_cast<FocusEvent *>(ev);
+ EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+ }
+
void expectTap(int x, int y) {
InputEvent* ev = consumeEvent();
- EXPECT_TRUE(ev != nullptr);
- EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
MotionEvent* mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
EXPECT_EQ(x, mev->getX(0));
EXPECT_EQ(y, mev->getY(0));
ev = consumeEvent();
- EXPECT_TRUE(ev != nullptr);
- EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
}
@@ -212,7 +221,7 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
const auto display = mComposerClient->getInternalDisplayToken();
- ASSERT_FALSE(display == nullptr);
+ ASSERT_NE(display, nullptr);
DisplayInfo info;
ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info));
@@ -259,18 +268,28 @@
TEST_F(InputSurfacesTest, can_receive_input) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
+ surface->assertFocusChange(true);
injectTap(101, 101);
- EXPECT_TRUE(surface->consumeEvent() != nullptr);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
}
+/**
+ * Set up two surfaces side-by-side. Tap each surface.
+ * Next, swap the positions of the two surfaces. Inject tap into the two
+ * original locations. Ensure that the tap is received by the surfaces in the
+ * reverse order.
+ */
TEST_F(InputSurfacesTest, input_respects_positioning) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
+ surface->assertFocusChange(true);
std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
surface2->showAt(200, 200);
+ surface->assertFocusChange(false);
+ surface2->assertFocusChange(true);
injectTap(201, 201);
surface2->expectTap(1, 1);
@@ -297,11 +316,16 @@
std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
surface->showAt(10, 10);
+ surface->assertFocusChange(true);
surface2->showAt(10, 10);
+ surface->assertFocusChange(false);
+ surface2->assertFocusChange(true);
surface->doTransaction([](auto &t, auto &sc) {
t.setLayer(sc, LAYER_BASE + 1);
});
+ surface2->assertFocusChange(false);
+ surface->assertFocusChange(true);
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -309,6 +333,8 @@
surface2->doTransaction([](auto &t, auto &sc) {
t.setLayer(sc, LAYER_BASE + 1);
});
+ surface2->assertFocusChange(true);
+ surface->assertFocusChange(false);
injectTap(11, 11);
surface2->expectTap(1, 1);
@@ -316,6 +342,8 @@
surface2->doTransaction([](auto &t, auto &sc) {
t.hide(sc);
});
+ surface2->assertFocusChange(false);
+ surface->assertFocusChange(true);
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -328,9 +356,12 @@
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
+ bgSurface->assertFocusChange(true);
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
+ fgSurface->assertFocusChange(true);
+ bgSurface->assertFocusChange(false);
injectTap(106, 106);
fgSurface->expectTap(1, 1);
@@ -344,9 +375,12 @@
std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
parentSurface->showAt(100, 100);
+ parentSurface->assertFocusChange(true);
childSurface->mInputInfo.surfaceInset = 10;
childSurface->showAt(100, 100);
+ childSurface->assertFocusChange(true);
+ parentSurface->assertFocusChange(false);
childSurface->doTransaction([&](auto &t, auto &sc) {
t.setPosition(sc, -5, -5);
@@ -365,9 +399,12 @@
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
+ bgSurface->assertFocusChange(true);
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
+ bgSurface->assertFocusChange(false);
+ fgSurface->assertFocusChange(true);
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
@@ -384,6 +421,7 @@
// In case we pass the very big inset without any checking.
fgSurface->mInputInfo.surfaceInset = INT32_MAX;
fgSurface->showAt(100, 100);
+ fgSurface->assertFocusChange(true);
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -400,6 +438,7 @@
t.setTransparentRegionHint(sc, transparentRegion);
});
surface->showAt(100, 100);
+ surface->assertFocusChange(true);
injectTap(101, 101);
surface->expectTap(1, 1);
}
@@ -414,7 +453,10 @@
InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(true);
bufferSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(false);
+ bufferSurface->assertFocusChange(true);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
@@ -431,7 +473,10 @@
postBuffer(bufferSurface->mSurfaceControl);
bgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(true);
bufferSurface->showAt(10, 10);
+ bufferSurface->assertFocusChange(true);
+ bgSurface->assertFocusChange(false);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
@@ -447,7 +492,10 @@
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(true);
fgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(false);
+ fgSurface->assertFocusChange(true);
injectTap(11, 11);
fgSurface->expectTap(1, 1);
@@ -464,12 +512,17 @@
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(true);
containerSurface->showAt(10, 10);
+ bgSurface->assertFocusChange(false);
+ containerSurface->assertFocusChange(true);
injectTap(11, 11);
containerSurface->expectTap(1, 1);
containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
+ containerSurface->assertFocusChange(false);
+ bgSurface->assertFocusChange(true);
injectTap(11, 11);
bgSurface->expectTap(1, 1);
@@ -478,6 +531,7 @@
TEST_F(InputSurfacesTest, input_respects_outscreen) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(-1, -1);
+ surface->assertFocusChange(true);
injectTap(0, 0);
surface->expectTap(1, 1);
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index e925f5b..b723654 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -111,6 +111,21 @@
msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
}
+// --- FocusEntry ---
+
+// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+FocusEntry::FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp<IBinder> connectionToken,
+ bool hasFocus)
+ : EventEntry(sequenceNum, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
+ connectionToken(connectionToken),
+ hasFocus(hasFocus) {}
+
+FocusEntry::~FocusEntry() {}
+
+void FocusEntry::appendDescription(std::string& msg) const {
+ msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
+}
+
// --- KeyEntry ---
KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 9dcaadc..e8c37f0 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -33,7 +33,13 @@
constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
struct EventEntry {
- enum class Type { CONFIGURATION_CHANGED, DEVICE_RESET, KEY, MOTION };
+ enum class Type {
+ CONFIGURATION_CHANGED,
+ DEVICE_RESET,
+ FOCUS,
+ KEY,
+ MOTION,
+ };
static const char* typeToString(Type type) {
switch (type) {
@@ -41,6 +47,8 @@
return "CONFIGURATION_CHANGED";
case Type::DEVICE_RESET:
return "DEVICE_RESET";
+ case Type::FOCUS:
+ return "FOCUS";
case Type::KEY:
return "KEY";
case Type::MOTION:
@@ -102,6 +110,17 @@
virtual ~DeviceResetEntry();
};
+struct FocusEntry : EventEntry {
+ sp<IBinder> connectionToken;
+ bool hasFocus;
+
+ FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus);
+ virtual void appendDescription(std::string& msg) const;
+
+protected:
+ virtual ~FocusEntry();
+};
+
struct KeyEntry : EventEntry {
int32_t deviceId;
uint32_t source;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6157d99..77bf1e9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -530,6 +530,14 @@
break;
}
+ case EventEntry::Type::FOCUS: {
+ FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
+ dispatchFocusLocked(currentTime, typedEntry);
+ done = true;
+ dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
+ break;
+ }
+
case EventEntry::Type::KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
@@ -634,7 +642,8 @@
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET: {
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::FOCUS: {
// nothing to do
break;
}
@@ -773,6 +782,7 @@
}
break;
}
+ case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type));
@@ -933,6 +943,25 @@
return true;
}
+void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) {
+ FocusEntry* focusEntry =
+ new FocusEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, now(), window.getToken(), hasFocus);
+ enqueueInboundEventLocked(focusEntry);
+}
+
+void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
+ sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+ if (channel == nullptr) {
+ return; // Window has gone away
+ }
+ InputTarget target;
+ target.inputChannel = channel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ entry->dispatchInProgress = true;
+
+ dispatchEventLocked(currentTime, entry, {target});
+}
+
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
@@ -1315,6 +1344,7 @@
displayId = motionEntry.displayId;
break;
}
+ case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type));
@@ -2062,6 +2092,10 @@
}
void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
+ if (eventEntry.type == EventEntry::Type::FOCUS) {
+ // Focus events are passed to apps, but do not represent user activity.
+ return;
+ }
int32_t displayId = getTargetDisplayId(eventEntry);
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
@@ -2096,6 +2130,7 @@
eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
+ case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events are not user activity",
@@ -2292,6 +2327,9 @@
break;
}
+ case EventEntry::Type::FOCUS: {
+ break;
+ }
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
@@ -2427,6 +2465,14 @@
reportTouchEventForStatistics(*motionEntry);
break;
}
+ case EventEntry::Type::FOCUS: {
+ FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry);
+ status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
+ focusEntry->hasFocus,
+ mInTouchMode);
+ break;
+ }
+
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
@@ -2666,6 +2712,10 @@
*cancelationEventEntry));
break;
}
+ case EventEntry::Type::FOCUS: {
+ LOG_ALWAYS_FATAL("Canceling focus events is not supported");
+ break;
+ }
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
@@ -3456,6 +3506,7 @@
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
+ enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
}
mFocusedWindowHandlesByDisplay.erase(displayId);
}
@@ -3465,6 +3516,7 @@
newFocusedWindowHandle->getName().c_str(), displayId);
}
mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
+ enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
}
if (mFocusedDisplayId == displayId) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 50b5250..a4ba0de 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -157,6 +157,9 @@
// Cleans up input state when dropping an inbound event.
void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
+ // Enqueues a focus event.
+ void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock);
+
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
@@ -299,6 +302,7 @@
nsecs_t* nextWakeupTime) REQUIRES(mLock);
bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock);
void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index bd8d2a4..98ebf50 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -482,12 +482,31 @@
EXPECT_EQ(expectedFlags, motionEvent.getFlags());
break;
}
+ case AINPUT_EVENT_TYPE_FOCUS: {
+ FAIL() << "Use 'consumeFocusEvent' for FOCUS events";
+ }
default: {
FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
}
}
}
+ void consumeFocusEvent(bool hasFocus, bool inTouchMode) {
+ InputEvent* event = consume();
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+ << "Got " << inputEventTypeToString(event->getType())
+ << " event instead of FOCUS event";
+
+ ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+ EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+ EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
+ }
+
void assertNoEvents() {
InputEvent* event = consume();
ASSERT_EQ(nullptr, event)
@@ -508,7 +527,6 @@
public:
static const int32_t WIDTH = 600;
static const int32_t HEIGHT = 800;
- const std::string mName;
FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name,
@@ -551,7 +569,7 @@
virtual bool updateInfo() { return true; }
- void setFocus() { mInfo.hasFocus = true; }
+ void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
void setFrame(const Rect& frame) {
mInfo.frameLeft = frame.left;
@@ -586,6 +604,12 @@
expectedFlags);
}
+ void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
+ ASSERT_NE(mInputReceiver, nullptr)
+ << "Cannot consume events from a window with no receiver";
+ mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode);
+ }
+
void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
int32_t expectedFlags) {
ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
@@ -608,7 +632,10 @@
sp<IBinder> getToken() { return mInfo.token; }
+ const std::string& getName() { return mName; }
+
private:
+ const std::string mName;
std::unique_ptr<FakeInputReceiver> mInputReceiver;
};
@@ -759,10 +786,11 @@
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- // Expect one focus window exist in display.
- windowSecond->setFocus();
-
+ // Display should have only one focused window
+ windowSecond->setFocus(true);
mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+
+ windowSecond->consumeFocusEvent(true);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -782,10 +810,11 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
// Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
- windowTop->setFocus();
- windowSecond->setFocus();
+ windowTop->setFocus(true);
+ windowSecond->setFocus(true);
mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+ windowTop->consumeFocusEvent(true);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -805,11 +834,12 @@
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- windowTop->setFocus();
- windowSecond->setFocus();
+ windowTop->setFocus(true);
+ windowSecond->setFocus(true);
// Release channel for window is no longer valid.
windowTop->releaseChannel();
mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+ windowSecond->consumeFocusEvent(true);
// Test inject a key down, should dispatch to a valid window.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
@@ -849,9 +879,10 @@
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window =
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
- window->setFocus();
+ window->setFocus(true);
mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ window->consumeFocusEvent(true);
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
mDispatcher->notifyKey(&keyArgs);
@@ -890,6 +921,59 @@
0 /*expectedFlags*/);
}
+TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setFocus(true);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ window->consumeFocusEvent(true);
+
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+
+ // Window should receive key down event.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ mDispatcher->waitForIdle();
+
+ window->assertNoEvents();
+}
+
+// If a window is touchable, but does not have focus, it should receive motion events, but not keys
+TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ // Send key
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+ // Send motion
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Window should receive only the motion event
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->assertNoEvents(); // Key event or focus event will not be received
+}
+
class FakeMonitorReceiver {
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
@@ -946,9 +1030,10 @@
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- window->setFocus();
+ window->setFocus(true);
mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ window->consumeFocusEvent(true);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
@@ -1010,6 +1095,49 @@
0 /*expectedFlags*/);
}
+/**
+ * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to
+ * the device default right away. In the test scenario, we check both the default value,
+ * and the action of enabling / disabling.
+ */
+TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocus(true);
+
+ SCOPED_TRACE("Check default value of touch mode");
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+ SCOPED_TRACE("Remove the window to trigger focus loss");
+ window->setFocus(false);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/);
+
+ SCOPED_TRACE("Disable touch mode");
+ mDispatcher->setInTouchMode(false);
+ window->setFocus(true);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/);
+
+ SCOPED_TRACE("Remove the window to trigger focus loss");
+ window->setFocus(false);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/);
+
+ SCOPED_TRACE("Enable touch mode again");
+ mDispatcher->setInTouchMode(true);
+ window->setFocus(true);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+ window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+ window->assertNoEvents();
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
@@ -1023,8 +1151,9 @@
// Set focus window for primary display, but focused display would be second one.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
- windowInPrimary->setFocus();
+ windowInPrimary->setFocus(true);
mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeFocusEvent(true);
application2 = new FakeApplicationHandle();
windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
@@ -1034,8 +1163,9 @@
mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
- windowInSecondary->setFocus();
+ windowInSecondary->setFocus(true);
mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID);
+ windowInSecondary->consumeFocusEvent(true);
}
virtual void TearDown() override {
@@ -1094,6 +1224,7 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
windowInPrimary->assertNoEvents();
+ windowInSecondary->consumeFocusEvent(false);
windowInSecondary->assertNoEvents();
}
@@ -1244,10 +1375,11 @@
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mFocusedWindow->setFocus();
+ mFocusedWindow->setFocus(true);
// Expect one focus window exist in display.
mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->consumeFocusEvent(true);
}
virtual void TearDown() override {
@@ -1350,7 +1482,7 @@
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
const std::vector<PointF>& points) {
- std::string name = window->mName;
+ const std::string name = window->getName();
InputEvent* event = window->consume();
ASSERT_NE(nullptr, event) << name.c_str()