Merge "SF Generalization of Refresh Rates: Adding layer priority hint"
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index a10baa6..e7b0d5d 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -273,7 +273,7 @@
ps->startThreadPool();
ps->giveThreadPoolName();
sAppDataIsolationEnabled = android::base::GetBoolProperty(
- kAppDataIsolationEnabledProperty, false);
+ kAppDataIsolationEnabledProperty, true);
return android::OK;
}
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 01cf2f8..41718b2 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -100,6 +100,19 @@
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
AndroidBitmapInfo* info);
+#if __ANDROID_API__ >= 30
+
+/**
+ * Given a java bitmap object, return its ADataSpace.
+ *
+ * Note that ADataSpace only exposes a few values. This may return
+ * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no
+ * corresponding ADataSpace.
+ */
+int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30);
+
+#endif // __ANDROID_API__ >= 30
+
/**
* Given a java bitmap object, attempt to lock the pixel address.
* Locking will ensure that the memory for the pixels will not move
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 c0cd3e0..23d87fd 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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 <bpf_timeinstate.h>
@@ -351,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/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 6566538..1e25049 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -129,6 +129,8 @@
}
} // namespace
+namespace android {
+
int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
const size_t size = ids.size();
@@ -298,3 +300,5 @@
return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset;
}
+
+} // namespace android
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index d37b9a1..bcc99fc 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -40,6 +40,8 @@
"-Wno-unused-function",
],
+ version_script: "libnativedisplay.map.txt",
+
srcs: [
"AChoreographer.cpp",
"ADisplay.cpp",
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
index 9be401e..a7eaf87 100644
--- a/libs/nativedisplay/include/apex/display.h
+++ b/libs/nativedisplay/include/apex/display.h
@@ -20,7 +20,12 @@
#include <android/hardware_buffer.h>
#include <inttypes.h>
-__BEGIN_DECLS
+// TODO: the intention of these apis is to be stable - hence they are defined in
+// an apex directory. But because they don't yet need to be stable, hold off on
+// making them stable until a Mainline module needs them.
+// __BEGIN_DECLS
+
+namespace android {
/**
* Opaque handle for a native display
@@ -130,4 +135,5 @@
*/
int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config);
-__END_DECLS
+} // namespace android
+// __END_DECLS
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 3b6a241..650bdf1 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -7,6 +7,28 @@
AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
AChoreographer_registerRefreshRateCallback; # apex # introduced=30
AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
+ AChoreographer_create; # apex # introduced=30
+ AChoreographer_destroy; # apex # introduced=30
+ AChoreographer_getFd; # apex # introduced=30
+ AChoreographer_handlePendingEvents; # apex # introduced=30
local:
*;
};
+
+LIBNATIVEDISPLAY_PLATFORM {
+ global:
+ extern "C++" {
+ android::ADisplay_acquirePhysicalDisplays*;
+ android::ADisplay_release*;
+ android::ADisplay_getMaxSupportedFps*;
+ android::ADisplay_getDisplayType*;
+ android::ADisplay_getPreferredWideColorFormat*;
+ android::ADisplay_getCurrentConfig*;
+ android::ADisplayConfig_getDensity*;
+ android::ADisplayConfig_getWidth*;
+ android::ADisplayConfig_getHeight*;
+ android::ADisplayConfig_getFps*;
+ android::ADisplayConfig_getCompositorOffsetNanos*;
+ android::ADisplayConfig_getAppVsyncOffsetNanos*;
+ };
+} LIBNATIVEDISPLAY;
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 768d3b5..2e183b3 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -172,10 +172,6 @@
// ----------------------------------------------------------------------------
-// this mutex protects:
-// d->disp[]
-// egl_init_drivers_locked()
-//
static EGLBoolean egl_init_drivers_locked() {
if (sEarlyInitState) {
// initialized by static ctor. should be set here.
@@ -211,6 +207,8 @@
return loader.load_glesv1_driver(cnx);
}
+// this mutex protects driver load logic as a critical section since it accesses to global variable
+// like gEGLImpl
static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
EGLBoolean egl_init_drivers() {
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
new file mode 100644
index 0000000..5a5dc89
--- /dev/null
+++ b/services/automotive/display/Android.bp
@@ -0,0 +1,39 @@
+//
+// 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.
+//
+
+cc_binary {
+ name: "android.frameworks.automotive.display@1.0-service",
+ defaults: ["hidl_defaults"],
+ srcs: [
+ "main_automotivedisplay.cpp",
+ "CarWindowService.cpp",
+ ],
+ init_rc: ["android.frameworks.automotive.display@1.0-service.rc"],
+
+ shared_libs: [
+ "android.frameworks.automotive.display@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ "libgui",
+ "libhidlbase",
+ "liblog",
+ "libui",
+ "libutils",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+}
diff --git a/services/automotive/display/CarWindowService.cpp b/services/automotive/display/CarWindowService.cpp
new file mode 100644
index 0000000..e95c9e1
--- /dev/null
+++ b/services/automotive/display/CarWindowService.cpp
@@ -0,0 +1,121 @@
+//
+// 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.
+//
+#include <ui/DisplayInfo.h>
+#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+
+#include "CarWindowService.h"
+
+namespace android {
+namespace frameworks {
+namespace automotive {
+namespace display {
+namespace V1_0 {
+namespace implementation {
+
+Return<sp<IGraphicBufferProducer>>
+ CarWindowService::getIGraphicBufferProducer() {
+ if (mSurface == nullptr) {
+ status_t err;
+ mSurfaceComposerClient = new SurfaceComposerClient();
+
+ err = mSurfaceComposerClient->initCheck();
+ if (err != NO_ERROR) {
+ ALOGE("SurfaceComposerClient::initCheck error: %#x", err);
+ mSurfaceComposerClient = nullptr;
+ return nullptr;
+ }
+
+ // Get main display parameters.
+ sp<IBinder> mainDpy = SurfaceComposerClient::getInternalDisplayToken();
+ if (mainDpy == nullptr) {
+ ALOGE("Failed to get internal display ");
+ return nullptr;
+ }
+ DisplayInfo mainDpyInfo;
+ err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+ if (err != NO_ERROR) {
+ ALOGE("Failed to get display characteristics");
+ return nullptr;
+ }
+ unsigned int mWidth, mHeight;
+ if (mainDpyInfo.orientation != ui::ROTATION_0 &&
+ mainDpyInfo.orientation != ui::ROTATION_180) {
+ // rotated
+ mWidth = mainDpyInfo.h;
+ mHeight = mainDpyInfo.w;
+ } else {
+ mWidth = mainDpyInfo.w;
+ mHeight = mainDpyInfo.h;
+ }
+
+ mSurfaceControl = mSurfaceComposerClient->createSurface(
+ String8("Automotive Display"), mWidth, mHeight,
+ PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
+ if (mSurfaceControl == nullptr || !mSurfaceControl->isValid()) {
+ ALOGE("Failed to create SurfaceControl");
+ mSurfaceComposerClient = nullptr;
+ mSurfaceControl = nullptr;
+ return nullptr;
+ }
+
+ // SurfaceControl::getSurface is guaranteed to be not null.
+ mSurface = mSurfaceControl->getSurface();
+ }
+
+ return new ::android::hardware::graphics::bufferqueue::V2_0::utils::
+ B2HGraphicBufferProducer(
+ mSurface->getIGraphicBufferProducer());
+}
+
+Return<bool> CarWindowService::showWindow() {
+ status_t status = NO_ERROR;
+
+ if (mSurfaceControl != nullptr) {
+ status = SurfaceComposerClient::Transaction{}
+ .setLayer(
+ mSurfaceControl, 0x7FFFFFFF) // always on top
+ .show(mSurfaceControl)
+ .apply();
+ } else {
+ ALOGE("showWindow: Failed to get a valid SurfaceControl!");
+ return false;
+ }
+
+ return status == NO_ERROR;
+}
+
+Return<bool> CarWindowService::hideWindow() {
+ status_t status = NO_ERROR;
+
+ if (mSurfaceControl != nullptr) {
+ status = SurfaceComposerClient::Transaction{}
+ .hide(mSurfaceControl)
+ .apply();
+ } else {
+ ALOGE("hideWindow: Failed to get a valid SurfaceControl!");
+ return false;
+ }
+
+ return status == NO_ERROR;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace display
+} // namespace automotive
+} // namespace frameworks
+} // namespace android
+
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
new file mode 100644
index 0000000..ffb7b5e
--- /dev/null
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -0,0 +1,5 @@
+service automotive_display /system/bin/android.frameworks.automotive.display@1.0-service
+ class hal
+ user graphics
+ group automotive_evs
+ disabled # will not automatically start with its class; must be explicitly started.
diff --git a/services/automotive/display/include/CarWindowService.h b/services/automotive/display/include/CarWindowService.h
new file mode 100644
index 0000000..3290cc7
--- /dev/null
+++ b/services/automotive/display/include/CarWindowService.h
@@ -0,0 +1,52 @@
+//
+// 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.
+//
+#pragma once
+
+#include <android/frameworks/automotive/display/1.0/ICarWindowService.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+namespace android {
+namespace frameworks {
+namespace automotive {
+namespace display {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::Return;
+using ::android::sp;
+using ::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
+
+class CarWindowService : public ICarWindowService {
+public:
+ Return<sp<IGraphicBufferProducer>> getIGraphicBufferProducer() override;
+ Return<bool> showWindow() override;
+ Return<bool> hideWindow() override;
+
+private:
+ sp<android::Surface> mSurface;
+ sp<android::SurfaceComposerClient> mSurfaceComposerClient;
+ sp<android::SurfaceControl> mSurfaceControl;
+};
+} // namespace implementation
+} // namespace V1_0
+} // namespace display
+} // namespace automotive
+} // namespace frameworks
+} // namespace android
+
diff --git a/services/automotive/display/main_automotivedisplay.cpp b/services/automotive/display/main_automotivedisplay.cpp
new file mode 100644
index 0000000..69f0a13
--- /dev/null
+++ b/services/automotive/display/main_automotivedisplay.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AutomotiveDisplayService"
+
+#include <unistd.h>
+
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "CarWindowService.h"
+
+// libhidl:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::frameworks::automotive::display::V1_0::ICarWindowService;
+
+// The namespace in which all our implementation code lives
+using namespace android::frameworks::automotive::display::V1_0::implementation;
+using namespace android;
+
+const static char kServiceName[] = "default";
+
+int main() {
+ ALOGI("Car Window Service is starting");
+
+ android::sp<ICarWindowService> service = new CarWindowService();
+
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ // Register our service -- if somebody is already registered by our name,
+ // they will be killed (their thread pool will throw an exception).
+ status_t status = service->registerAsService(kServiceName);
+ if (status == OK) {
+ ALOGD("%s is ready.", kServiceName);
+ joinRpcThreadpool();
+ } else {
+ ALOGE("Could not register service %s (%d).", kServiceName, status);
+ }
+
+ // In normal operation, we don't expect the thread pool to exit
+ ALOGE("Car Window Service is shutting down");
+
+ return 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..ce7399e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -256,67 +256,6 @@
return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
}
-static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
- EventEntry* eventEntry,
- int32_t inputTargetFlags) {
- if (inputTarget.useDefaultPointerInfo()) {
- const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
- return std::make_unique<DispatchEntry>(eventEntry, // increments ref
- inputTargetFlags, pointerInfo.xOffset,
- pointerInfo.yOffset, inputTarget.globalScaleFactor,
- pointerInfo.windowXScale, pointerInfo.windowYScale);
- }
-
- ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
-
- PointerCoords pointerCoords[MAX_POINTERS];
-
- // Use the first pointer information to normalize all other pointers. This could be any pointer
- // as long as all other pointers are normalized to the same value and the final DispatchEntry
- // uses the offset and scale for the normalized pointer.
- const PointerInfo& firstPointerInfo =
- inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
-
- // Iterate through all pointers in the event to normalize against the first.
- for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
- const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
- uint32_t pointerId = uint32_t(pointerProperties.id);
- const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
-
- // The scale factor is the ratio of the current pointers scale to the normalized scale.
- float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
- float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
-
- pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
- // First apply the current pointers offset to set the window at 0,0
- pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
- // Next scale the coordinates.
- pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
- // Lastly, offset the coordinates so they're in the normalized pointer's frame.
- pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
- -firstPointerInfo.yOffset);
- }
-
- MotionEntry* combinedMotionEntry =
- new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId,
- motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
- motionEntry.action, motionEntry.actionButton, motionEntry.flags,
- motionEntry.metaState, motionEntry.buttonState,
- motionEntry.classification, motionEntry.edgeFlags,
- motionEntry.xPrecision, motionEntry.yPrecision,
- motionEntry.xCursorPosition, motionEntry.yCursorPosition,
- motionEntry.downTime, motionEntry.pointerCount,
- motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
- 0 /* yOffset */);
-
- return std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
- inputTargetFlags, firstPointerInfo.xOffset,
- firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
- firstPointerInfo.windowXScale,
- firstPointerInfo.windowYScale);
-}
-
// --- InputDispatcherThread ---
class InputDispatcher::InputDispatcherThread : public Thread {
@@ -530,6 +469,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 +581,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 +721,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 +882,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 +1283,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));
@@ -1846,34 +1815,23 @@
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
- std::vector<InputTarget>::iterator it =
- std::find_if(inputTargets.begin(), inputTargets.end(),
- [&windowHandle](const InputTarget& inputTarget) {
- return inputTarget.inputChannel->getConnectionToken() ==
- windowHandle->getToken();
- });
-
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
-
- if (it == inputTargets.end()) {
- InputTarget inputTarget;
- sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
- if (inputChannel == nullptr) {
- ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
- return;
- }
- inputTarget.inputChannel = inputChannel;
- inputTarget.flags = targetFlags;
- inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
- inputTargets.push_back(inputTarget);
- it = inputTargets.end() - 1;
+ sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ if (inputChannel == nullptr) {
+ ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+ return;
}
- ALOG_ASSERT(it->flags == targetFlags);
- ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
-
- it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ const InputWindowInfo* windowInfo = windowHandle->getInfo();
+ InputTarget target;
+ target.inputChannel = inputChannel;
+ target.flags = targetFlags;
+ target.xOffset = -windowInfo->frameLeft;
+ target.yOffset = -windowInfo->frameTop;
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ target.windowXScale = windowInfo->windowXScale;
+ target.windowYScale = windowInfo->windowYScale;
+ target.pointerIds = pointerIds;
+ inputTargets.push_back(target);
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1896,7 +1854,10 @@
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
+ target.xOffset = xOffset;
+ target.yOffset = yOffset;
+ target.pointerIds.clear();
+ target.globalScaleFactor = 1.0f;
inputTargets.push_back(target);
}
@@ -2062,6 +2023,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 +2061,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",
@@ -2123,10 +2089,11 @@
}
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "globalScaleFactor=%f, pointerIds=0x%x %s",
- connection->getInputChannelName().c_str(), inputTarget.flags,
- inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
- inputTarget.getPointerInfoString().c_str());
+ "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
+ "windowScaleFactor=(%f, %f), pointerIds=0x%x",
+ connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset,
+ inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale,
+ inputTarget.windowYScale, inputTarget.pointerIds.value);
#endif
// Skip this event if the connection status is not normal.
@@ -2220,15 +2187,15 @@
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
- createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
+ std::make_unique<DispatchEntry>(eventEntry, // increments ref
+ inputTargetFlags, inputTarget.xOffset,
+ inputTarget.yOffset, inputTarget.globalScaleFactor,
+ inputTarget.windowXScale, inputTarget.windowYScale);
- // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
- // different EventEntry than what was passed in.
- EventEntry* newEntry = dispatchEntry->eventEntry;
// Apply target flags and update the connection's input state.
- switch (newEntry->type) {
+ switch (eventEntry->type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2244,7 +2211,7 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
@@ -2292,17 +2259,20 @@
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",
- EventEntry::typeToString(newEntry->type));
+ EventEntry::typeToString(eventEntry->type));
break;
}
}
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatches(newEntry);
+ incrementPendingForegroundDispatches(eventEntry);
}
// Enqueue the dispatch entry.
@@ -2427,6 +2397,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 +2644,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",
@@ -2679,9 +2661,15 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
- windowInfo->windowXScale, windowInfo->windowYScale);
+ target.xOffset = -windowInfo->frameLeft;
+ target.yOffset = -windowInfo->frameTop;
target.globalScaleFactor = windowInfo->globalScaleFactor;
+ target.windowXScale = windowInfo->windowXScale;
+ target.windowYScale = windowInfo->windowYScale;
+ } else {
+ target.xOffset = 0;
+ target.yOffset = 0;
+ target.globalScaleFactor = 1.0f;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
@@ -3456,6 +3444,7 @@
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
+ enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
}
mFocusedWindowHandlesByDisplay.erase(displayId);
}
@@ -3465,6 +3454,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/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 0588374..80fa2cb 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,63 +42,4 @@
return StringPrintf("%" PRId32, dispatchMode);
}
-void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
- float windowXScale, float windowYScale) {
- // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
- // and non splittable windows since we will just use all the pointers from the input event.
- if (newPointerIds.isEmpty()) {
- setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
- return;
- }
-
- // Ensure that the new set of pointers doesn't overlap with the current set of pointers.
- ALOG_ASSERT((pointerIds & newPointerIds) == 0);
-
- pointerIds |= newPointerIds;
- while (!newPointerIds.isEmpty()) {
- int32_t pointerId = newPointerIds.clearFirstMarkedBit();
- pointerInfos[pointerId].xOffset = xOffset;
- pointerInfos[pointerId].yOffset = yOffset;
- pointerInfos[pointerId].windowXScale = windowXScale;
- pointerInfos[pointerId].windowYScale = windowYScale;
- }
-}
-
-void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
- float windowYScale) {
- pointerIds.clear();
- pointerInfos[0].xOffset = xOffset;
- pointerInfos[0].yOffset = yOffset;
- pointerInfos[0].windowXScale = windowXScale;
- pointerInfos[0].windowYScale = windowYScale;
-}
-
-bool InputTarget::useDefaultPointerInfo() const {
- return pointerIds.isEmpty();
-}
-
-const PointerInfo& InputTarget::getDefaultPointerInfo() const {
- return pointerInfos[0];
-}
-
-std::string InputTarget::getPointerInfoString() const {
- if (useDefaultPointerInfo()) {
- const PointerInfo& pointerInfo = getDefaultPointerInfo();
- return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
- pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
- pointerInfo.windowYScale);
- }
-
- std::string out;
- for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
- if (!pointerIds.hasBit(i)) {
- continue;
- }
- out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f "
- "windowScaleFactor=(%.1f, %.1f)",
- i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
- pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
- }
- return out;
-}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 499a75f..1ba5eff 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -24,22 +24,6 @@
namespace android::inputdispatcher {
/*
- * Information about each pointer for an InputTarget. This includes offset and scale so
- * all pointers can be normalized to a single offset and scale.
- *
- * These values are ignored for KeyEvents
- */
-struct PointerInfo {
- // The x and y offset to add to a MotionEvent as it is delivered.
- float xOffset = 0.0f;
- float yOffset = 0.0f;
-
- // Scaling factor to apply to MotionEvent as it is delivered.
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
-};
-
-/*
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
@@ -111,37 +95,20 @@
// Flags for the input target.
int32_t flags = 0;
+ // The x and y offset to add to a MotionEvent as it is delivered.
+ // (ignored for KeyEvents)
+ float xOffset = 0.0f;
+ float yOffset = 0.0f;
+
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
- // The data is stored by the pointerId. Use the bit position of pointerIds to look up
- // PointerInfo per pointerId.
- PointerInfo pointerInfos[MAX_POINTERS];
-
- void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
- float windowYScale);
- void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
- float windowYScale);
-
- /**
- * Returns whether the default pointer information should be used. This will be true when the
- * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors
- * and non splittable windows since we want all pointers for the EventEntry to go to this
- * target.
- */
- bool useDefaultPointerInfo() const;
-
- /**
- * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
- * true.
- */
- const PointerInfo& getDefaultPointerInfo() const;
-
- std::string getPointerInfoString() const;
};
std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index bd8d2a4..c262abb 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()
@@ -1439,107 +1571,4 @@
consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
}
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
- mWindow2->setWindowScale(0.5f, 0.5f);
-
- // Touch Window 1
- std::vector<PointF> touchedPoints = {PointF{10, 10}};
- std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
-
- // Touch Window 2
- int32_t actionPointerDown =
- AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
-
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
-}
-
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
- mWindow2->setWindowScale(0.5f, 0.5f);
-
- // Touch Window 1
- std::vector<PointF> touchedPoints = {PointF{10, 10}};
- std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
-
- // Touch Window 2
- int32_t actionPointerDown =
- AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
-
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
-
- // Move both windows
- touchedPoints = {{20, 20}, {175, 175}};
- expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
- getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
-
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
-}
-
-TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
- mWindow1->setWindowScale(0.5f, 0.5f);
-
- // Touch Window 1
- std::vector<PointF> touchedPoints = {PointF{10, 10}};
- std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
-
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
-
- // Touch Window 2
- int32_t actionPointerDown =
- AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- touchedPoints.emplace_back(PointF{150, 150});
- expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
-
- motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- // Consuming from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
-
- // Move both windows
- touchedPoints = {{20, 20}, {175, 175}};
- expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
- getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
-
- motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints);
- mDispatcher->notifyMotion(&motionArgs);
-
- consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
-}
-
} // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index d476f7b4..4c5e5da 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -51,7 +51,7 @@
"libprocessgroup",
"libprotobuf-cpp-lite",
"libsync",
- "libtimestats_proto",
+ "libtimestats",
"libui",
"libinput",
"libutils",
@@ -70,7 +70,6 @@
"libperfetto_client_experimental",
"librenderengine",
"libserviceutils",
- "libtimestats",
"libtrace_proto",
"libvr_manager",
"libvrflinger",
@@ -84,7 +83,6 @@
"libcompositionengine",
"librenderengine",
"libserviceutils",
- "libtimestats",
],
export_shared_lib_headers: [
"android.hardware.graphics.allocator@2.0",
@@ -96,6 +94,7 @@
"android.hardware.graphics.composer@2.4",
"android.hardware.power@1.3",
"libhidlbase",
+ "libtimestats",
],
// TODO (marissaw): this library is not used by surfaceflinger. This is here so
// the library compiled in a way that is accessible to system partition when running
@@ -231,7 +230,6 @@
"liblog",
"libprocessgroup",
"libsync",
- "libtimestats_proto",
"libutils",
],
static_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 78f8104..18477bb 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -22,14 +22,13 @@
"libnativewindow",
"libprotobuf-cpp-lite",
"libsync",
- "libtimestats_proto",
+ "libtimestats",
"libui",
"libutils",
],
static_libs: [
"libmath",
"librenderengine",
- "libtimestats",
"libtrace_proto",
],
header_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 28d2653..6e28167 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -788,6 +788,7 @@
outputState.usesClientComposition};
base::unique_fd readyFence;
if (!hasClientComposition) {
+ setExpensiveRenderingExpected(false);
return readyFence;
}
@@ -871,10 +872,6 @@
new Fence(dup(readyFence.get()))));
}
- if (expensiveRenderingExpected) {
- setExpensiveRenderingExpected(false);
- }
-
return readyFence;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 3a4df74..ae93969 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -700,6 +700,9 @@
// We expect no calls to queueBuffer if composition was skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+ // Expect a call to signal no expensive rendering since there is no client composition.
+ EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, false));
+
mDisplay->editState().isEnabled = true;
mDisplay->editState().usesClientComposition = false;
mDisplay->editState().viewport = Rect(0, 0, 1, 1);
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index cb6c50c..80528e3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2842,9 +2842,11 @@
OutputComposeSurfacesTest::kDefaultAvgLuminance,
OutputComposeSurfacesTest::kDefaultMinLuminance};
-TEST_F(OutputComposeSurfacesTest, doesNothingIfNoClientComposition) {
+TEST_F(OutputComposeSurfacesTest, doesNothingButSignalNoExpensiveRenderingIfNoClientComposition) {
mOutput.mState.usesClientComposition = false;
+ EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
verify().execute().expectAFenceWasReturned();
}
@@ -3163,7 +3165,6 @@
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
mOutput.composeSurfaces(kDebugRegion);
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 4b9f555..5bba7d3 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1918,7 +1918,7 @@
setTransactionFlags(eTransactionNeeded);
}
-void Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) const {
+LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) const {
LayerProto* layerProto = layersProto.add_layers();
writeToProtoDrawingState(layerProto, traceFlags);
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
@@ -1926,6 +1926,8 @@
for (const sp<Layer>& layer : mDrawingChildren) {
layer->writeToProto(layersProto, traceFlags);
}
+
+ return layerProto;
}
void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 58a360a..b8a3539 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -462,8 +462,8 @@
bool isRemovedFromCurrentState() const;
- void writeToProto(LayersProto& layersProto,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ LayerProto* writeToProto(LayersProto& layersProto,
+ uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
// Write states that are modified by the main thread. This includes drawing
// state as well as buffer data. This should be called in the main or tracing
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 4ef64b6..f2bc65d 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -5,5 +5,6 @@
lpy@google.com
marissaw@google.com
racarr@google.com
+steventhomas@google.com
stoza@google.com
vhau@google.com
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 56b3252..a6fb3a4 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -39,6 +39,14 @@
virtual ~VSyncDispatch();
/*
+ * A callback that can be registered to be awoken at a given time relative to a vsync event.
+ * \param [in] vsyncTime The timestamp of the vsync the callback is for.
+ * \param [in] targetWakeupTime The timestamp of intended wakeup time of the cb.
+ *
+ */
+ using Callback = std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime)>;
+
+ /*
* Registers a callback that will be called at designated points on the vsync timeline.
* The callback can be scheduled, rescheduled targeting vsync times, or cancelled.
* The token returned must be cleaned up via unregisterCallback.
@@ -51,7 +59,7 @@
* invocation of callbackFn.
*
*/
- virtual CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+ virtual CallbackToken registerCallback(Callback const& callbackFn,
std::string callbackName) = 0;
/*
@@ -112,7 +120,7 @@
*/
class VSyncCallbackRegistration {
public:
- VSyncCallbackRegistration(VSyncDispatch&, std::function<void(nsecs_t)> const& callbackFn,
+ VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback const& callbackFn,
std::string const& callbackName);
VSyncCallbackRegistration(VSyncCallbackRegistration&&);
VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 48f2abb..2e5b6e9 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -29,7 +29,7 @@
TimeKeeper::~TimeKeeper() = default;
VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
- std::function<void(nsecs_t)> const& cb,
+ VSyncDispatch::Callback const& cb,
nsecs_t minVsyncDistance)
: mName(name),
mCallback(cb),
@@ -97,13 +97,13 @@
return *mLastDispatchTime;
}
-void VSyncDispatchTimerQueueEntry::callback(nsecs_t t) {
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp) {
{
std::lock_guard<std::mutex> lk(mRunningMutex);
mRunning = true;
}
- mCallback(t);
+ mCallback(vsyncTimestamp, wakeupTimestamp);
std::lock_guard<std::mutex> lk(mRunningMutex);
mRunning = false;
@@ -171,7 +171,8 @@
void VSyncDispatchTimerQueue::timerCallback() {
struct Invocation {
std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
- nsecs_t timestamp;
+ nsecs_t vsyncTimestamp;
+ nsecs_t wakeupTimestamp;
};
std::vector<Invocation> invocations;
{
@@ -186,7 +187,7 @@
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
callback->executing();
invocations.emplace_back(
- Invocation{callback, *callback->lastExecutedVsyncTarget()});
+ Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
}
}
@@ -195,12 +196,12 @@
}
for (auto const& invocation : invocations) {
- invocation.callback->callback(invocation.timestamp);
+ invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp);
}
}
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
- std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) {
+ Callback const& callbackFn, std::string callbackName) {
std::lock_guard<decltype(mMutex)> lk(mMutex);
return CallbackToken{
mCallbacks
@@ -271,7 +272,7 @@
}
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
- std::function<void(nsecs_t)> const& callbackFn,
+ VSyncDispatch::Callback const& callbackFn,
std::string const& callbackName)
: mDispatch(dispatch),
mToken(dispatch.registerCallback(callbackFn, callbackName)),
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 0c9b4fe..087acc7 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -36,7 +36,7 @@
// Valid transition: disarmed -> armed ( when scheduled )
// Valid transition: armed -> running -> disarmed ( when timer is called)
// Valid transition: armed -> disarmed ( when cancelled )
- VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn,
+ VSyncDispatchTimerQueueEntry(std::string const& name, VSyncDispatch::Callback const& fn,
nsecs_t minVsyncDistance);
std::string_view name() const;
@@ -62,14 +62,14 @@
nsecs_t executing();
// End: functions that are not threadsafe.
- // Invoke the callback with the timestamp, moving the state from running->disarmed.
- void callback(nsecs_t timestamp);
+ // Invoke the callback with the two given timestamps, moving the state from running->disarmed.
+ void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp);
// Block calling thread while the callback is executing.
void ensureNotRunning();
private:
std::string const mName;
- std::function<void(nsecs_t)> const mCallback;
+ VSyncDispatch::Callback const mCallback;
nsecs_t mWorkDuration;
nsecs_t mEarliestVsync;
@@ -104,8 +104,7 @@
nsecs_t timerSlack, nsecs_t minVsyncDistance);
~VSyncDispatchTimerQueue();
- CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
- std::string callbackName) final;
+ CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
void unregisterCallback(CallbackToken token) final;
ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
CancelResult cancel(CallbackToken token) final;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 158a184..20b6238 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -48,7 +48,8 @@
nsecs_t period, nsecs_t offset, nsecs_t notBefore)
: mCallback(cb),
mRegistration(dispatch,
- std::bind(&CallbackRepeater::callback, this, std::placeholders::_1),
+ std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
+ std::placeholders::_2),
std::string(name)),
mPeriod(period),
mOffset(offset),
@@ -85,15 +86,13 @@
}
private:
- void callback(nsecs_t vsynctime) {
- nsecs_t period = 0;
+ void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
{
std::lock_guard<std::mutex> lk(mMutex);
- period = mPeriod;
mLastCallTime = vsynctime;
}
- mCallback->onDispSyncEvent(vsynctime - period);
+ mCallback->onDispSyncEvent(wakeupTime);
{
std::lock_guard<std::mutex> lk(mMutex);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 46cfb21..e90cf41 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4283,13 +4283,8 @@
rootProto->add_children(offscreenLayer->sequence);
// Add layer
- LayerProto* layerProto = layersProto.add_layers();
- offscreenLayer->writeToProtoDrawingState(layerProto, traceFlags);
- offscreenLayer->writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing,
- traceFlags);
+ LayerProto* layerProto = offscreenLayer->writeToProto(layersProto, traceFlags);
layerProto->set_parent(offscreenRootLayerId);
-
- offscreenLayer->writeToProto(layersProto, traceFlags);
}
}
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 2080a38..20c2218 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -1,12 +1,27 @@
-cc_library_static {
+cc_library_shared {
name: "libtimestats",
- defaults: ["surfaceflinger_defaults"],
srcs: [
- "TimeStats.cpp",
+ "TimeStats.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libtimestats_proto",
+ "libui",
+ "libutils",
],
export_include_dirs: ["."],
- shared_libs: [
- "libtimestats_proto",
- "libui",
+ export_shared_lib_headers: [
+ "libtimestats_proto",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
],
}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 0403237..1c8199a 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -55,7 +55,6 @@
"liblog",
"libnativewindow",
"libprotobuf-cpp-full",
- "libtimestats_proto",
"libui",
"libutils",
],
@@ -68,7 +67,7 @@
name: "ipc_defaults",
cflags: [
"-Wall",
- "-Werror",
+ "-Werror",
],
}
@@ -82,11 +81,11 @@
],
cppflags: [
"-Wall",
- "-Werror",
- "-Wformat",
- "-Wthread-safety",
- "-Wunused",
- "-Wunreachable-code",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
],
shared_libs: [
"libandroid",
@@ -98,7 +97,6 @@
"liblayers_proto",
"liblog",
"libprotobuf-cpp-full",
- "libtimestats_proto",
"libui",
"libutils",
],
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 31837a9..ff403f6 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -29,7 +29,7 @@
"liblog",
"libnativewindow",
"libsync",
- "libtimestats_proto",
+ "libtimestats",
"libui",
"libutils",
],
@@ -38,7 +38,6 @@
"libgmock",
"libperfetto_client_experimental",
"librenderengine",
- "libtimestats",
"libtrace_proto",
],
header_libs: [
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 23fa940..becf484 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -80,11 +80,12 @@
"libgui_mocks",
"libperfetto_client_experimental",
"librenderengine_mocks",
- "libtimestats",
"perfetto_trace_protos",
],
shared_libs: [
"libsurfaceflinger",
+ "libtimestats",
+ "libtimestats_proto",
],
header_libs: [
"libsurfaceflinger_headers",
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 5846c77..b51a025 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -101,7 +101,7 @@
RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl)
: mWorkload(wl),
mCallback(
- dispatch, [&](auto time) { callback_called(time); }, "repeat0") {}
+ dispatch, [&](auto time, auto) { callback_called(time); }, "repeat0") {}
void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
mCallbackTimes.reserve(iterations);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 5aff429..6fcb1af 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -97,13 +97,14 @@
CountingCallback(VSyncDispatch& dispatch)
: mDispatch(dispatch),
mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
- std::placeholders::_1),
+ std::placeholders::_1,
+ std::placeholders::_2),
"test")) {}
~CountingCallback() { mDispatch.unregisterCallback(mToken); }
operator VSyncDispatch::CallbackToken() const { return mToken; }
- void counter(nsecs_t time) { mCalls.push_back(time); }
+ void counter(nsecs_t time, nsecs_t) { mCalls.push_back(time); }
VSyncDispatch& mDispatch;
VSyncDispatch::CallbackToken mToken;
@@ -115,7 +116,8 @@
PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount)
: mDispatch(dispatch),
mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this,
- std::placeholders::_1),
+ std::placeholders::_1,
+ std::placeholders::_2),
"test")),
mRegistered(true),
mPauseAmount(pauseAmount) {}
@@ -123,7 +125,7 @@
operator VSyncDispatch::CallbackToken() const { return mToken; }
- void pause(nsecs_t) {
+ void pause(nsecs_t, nsecs_t) {
std::unique_lock<std::mutex> lk(mMutex);
mPause = true;
mCv.notify_all();
@@ -462,7 +464,8 @@
EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
VSyncDispatch::CallbackToken tmp;
- tmp = mDispatch.registerCallback([&](auto) { mDispatch.schedule(tmp, 100, 2000); }, "o.o");
+ tmp = mDispatch.registerCallback([&](auto, auto) { mDispatch.schedule(tmp, 100, 2000); },
+ "o.o");
mDispatch.schedule(tmp, 100, 1000);
advanceToNextCallback();
@@ -472,7 +475,7 @@
VSyncDispatch::CallbackToken tmp;
std::optional<nsecs_t> lastTarget;
tmp = mDispatch.registerCallback(
- [&](auto timestamp) {
+ [&](auto timestamp, auto) {
EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
ScheduleResult::Scheduled);
EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
@@ -621,7 +624,7 @@
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
VSyncCallbackRegistration cb(
- mDispatch, [](auto) {}, "");
+ mDispatch, [](auto, auto) {}, "");
VSyncCallbackRegistration cb1(std::move(cb));
cb.schedule(100, 1000);
cb.cancel();
@@ -635,9 +638,9 @@
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
VSyncCallbackRegistration cb(
- mDispatch, [](auto) {}, "");
+ mDispatch, [](auto, auto) {}, "");
VSyncCallbackRegistration cb1(
- mDispatch, [](auto) {}, "");
+ mDispatch, [](auto, auto) {}, "");
cb1 = std::move(cb);
cb.schedule(100, 1000);
cb.cancel();
@@ -656,7 +659,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
std::string name("basicname");
VSyncDispatchTimerQueueEntry entry(
- name, [](auto) {}, mVsyncMoveThreshold);
+ name, [](auto, auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.name(), Eq(name));
EXPECT_FALSE(entry.lastExecutedVsyncTarget());
EXPECT_FALSE(entry.wakeupTime());
@@ -664,7 +667,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
@@ -684,7 +687,7 @@
.Times(1)
.WillOnce(Return(10000));
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
@@ -695,12 +698,14 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
auto callCount = 0;
- auto calledTime = 0;
+ auto vsyncCalledTime = 0;
+ auto wakeupCalledTime = 0;
VSyncDispatchTimerQueueEntry entry(
"test",
- [&](auto time) {
+ [&](auto vsyncTime, auto wakeupTime) {
callCount++;
- calledTime = time;
+ vsyncCalledTime = vsyncTime;
+ wakeupCalledTime = wakeupTime;
},
mVsyncMoveThreshold);
@@ -709,10 +714,11 @@
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
- entry.callback(entry.executing());
+ entry.callback(entry.executing(), *wakeup);
EXPECT_THAT(callCount, Eq(1));
- EXPECT_THAT(calledTime, Eq(mPeriod));
+ EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
+ EXPECT_THAT(wakeupCalledTime, Eq(*wakeup));
EXPECT_FALSE(entry.wakeupTime());
auto lastCalledTarget = entry.lastExecutedVsyncTarget();
ASSERT_TRUE(lastCalledTarget);
@@ -726,7 +732,7 @@
.WillOnce(Return(1020));
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
entry.update(mStubTracker, 0);
@@ -745,7 +751,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
entry.update(mStubTracker, 0);
@@ -756,7 +762,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
@@ -773,7 +779,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest,
willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto) {}, mVsyncMoveThreshold);
Sequence seq;
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
@@ -795,7 +801,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 4df20af..188adea 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -73,7 +73,8 @@
class MockVSyncDispatch : public VSyncDispatch {
public:
- MOCK_METHOD2(registerCallback, CallbackToken(std::function<void(nsecs_t)> const&, std::string));
+ MOCK_METHOD2(registerCallback,
+ CallbackToken(std::function<void(nsecs_t, nsecs_t)> const&, std::string));
MOCK_METHOD1(unregisterCallback, void(CallbackToken));
MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
@@ -82,7 +83,7 @@
class VSyncDispatchWrapper : public VSyncDispatch {
public:
VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
- CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+ CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn,
std::string callbackName) final {
return mDispatch->registerCallback(callbackFn, callbackName);
}
@@ -159,14 +160,15 @@
static constexpr nsecs_t mPhase = 3000;
static constexpr nsecs_t mAnotherPhase = 5200;
static constexpr nsecs_t period = 10000;
- static constexpr nsecs_t mFakeCbTime = 2093;
+ static constexpr nsecs_t mFakeVSyncTime = 2093;
+ static constexpr nsecs_t mFakeWakeupTime = 1892;
static constexpr nsecs_t mFakeNow = 2214;
static constexpr const char mName[] = "callbacky";
VSyncDispatch::CallbackToken const mFakeToken{2398};
nsecs_t lastCallbackTime = 0;
StubCallback outerCb;
- std::function<void(nsecs_t)> innerCb;
+ std::function<void(nsecs_t, nsecs_t)> innerCb;
VSyncReactor mReactor;
};
@@ -439,7 +441,8 @@
.WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeCbTime))
+ EXPECT_CALL(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
.Times(2)
.InSequence(seq);
EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
@@ -447,24 +450,25 @@
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
ASSERT_TRUE(innerCb);
- innerCb(mFakeCbTime);
- innerCb(mFakeCbTime);
+ innerCb(mFakeVSyncTime, mFakeWakeupTime);
+ innerCb(mFakeVSyncTime, mFakeWakeupTime);
}
-TEST_F(VSyncReactorTest, callbackTimestampReadapted) {
+TEST_F(VSyncReactorTest, callbackTimestampDistributedIsWakeupTime) {
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
.InSequence(seq)
.WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeCbTime))
+ EXPECT_CALL(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
.InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
ASSERT_TRUE(innerCb);
- innerCb(mFakeCbTime);
- EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeCbTime - period));
+ innerCb(mFakeVSyncTime, mFakeWakeupTime);
+ EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeWakeupTime));
}
TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
@@ -540,7 +544,7 @@
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
ASSERT_TRUE(innerCb);
- innerCb(mFakeCbTime);
+ innerCb(mFakeVSyncTime, mFakeWakeupTime);
}
TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
@@ -590,7 +594,7 @@
ON_CALL(*mMockDispatch, schedule(_, _, _))
.WillByDefault(Return(ScheduleResult::CannotSchedule));
- EXPECT_DEATH(innerCb(mFakeCbTime), ".*");
+ EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*");
}
} // namespace android::scheduler