diff options
44 files changed, 873 insertions, 437 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index a10baa6b80..e7b0d5d122 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -273,7 +273,7 @@ status_t InstalldNativeService::start() { 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 01cf2f88ea..41718b2904 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -100,6 +100,19 @@ typedef struct { 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 a8f7d92b41..b1943a4afd 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -14,6 +14,7 @@ cc_library { "-Wall", "-Wextra", ], + export_include_dirs: ["."], } cc_test { diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 4ee9f55227..ee44cf524b 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -85,6 +85,16 @@ static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2 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 @@ static bool attachTracepointProgram(const std::string &eventType, const std::str 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 @@ bool startTrackingUidTimes() { // 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 f620715dab..49469d8e04 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -26,6 +26,7 @@ bool startTrackingUidTimes(); 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 c0cd3e07ff..23d87fd646 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 @@ TEST(TimeInStateTest, RemoveUid) { 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 8d36ba7b70..04749e6d13 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -112,22 +112,31 @@ public: 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 @@ public: 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 @@ void injectTap(int x, int y) { 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 @@ TEST_F(InputSurfacesTest, input_respects_layering) { 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 @@ TEST_F(InputSurfacesTest, input_respects_layering) { 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 @@ TEST_F(InputSurfacesTest, input_respects_layering) { 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 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { 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 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { 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 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { 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 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { // 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 @@ TEST_F(InputSurfacesTest, input_ignores_transparent_region) { t.setTransparentRegionHint(sc, transparentRegion); }); surface->showAt(100, 100); + surface->assertFocusChange(true); injectTap(101, 101); surface->expectTap(1, 1); } @@ -414,7 +453,10 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) { 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 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) { 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 @@ TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { 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 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { 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_container_layer_visiblity) { 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 6566538eff..1e25049eb6 100644 --- a/libs/nativedisplay/ADisplay.cpp +++ b/libs/nativedisplay/ADisplay.cpp @@ -129,6 +129,8 @@ int64_t computeSfOffset(const DisplayInfo& info) { } } // 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 @@ int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) { return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset; } + +} // namespace android diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index d37b9a1157..bcc99fc33b 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -40,6 +40,8 @@ cc_library { "-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 9be401e4a8..a7eaf87b9e 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_getCompositorOffsetNanos(ADisplayConfig* config); */ 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 3b6a24171b..650bdf197d 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -7,6 +7,28 @@ LIBNATIVEDISPLAY { 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 768d3b54bc..2e183b3ba2 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -172,10 +172,6 @@ egl_connection_t* egl_get_connection() { // ---------------------------------------------------------------------------- -// 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 @@ static EGLBoolean egl_init_glesv1_drivers_locked() { 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 0000000000..5a5dc898a2 --- /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 0000000000..e95c9e1673 --- /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 0000000000..ffb7b5ef54 --- /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 0000000000..3290cc73e6 --- /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 0000000000..69f0a13fff --- /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 e925f5b6ba..b7236547de 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -111,6 +111,21 @@ void DeviceResetEntry::appendDescription(std::string& msg) const { 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 9dcaadc6ee..e8c37f0e6e 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -33,7 +33,13 @@ namespace android::inputdispatcher { 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 @@ struct EventEntry { 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 @@ protected: 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 6157d997fd..ce7399ef6e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -256,67 +256,6 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { 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 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { 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 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { 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 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason } 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 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceReset 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 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { 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 @@ Unresponsive: 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 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO 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 @@ std::string InputDispatcher::getApplicationWindowLabel( } 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 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { 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 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } #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 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio // 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 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio } 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 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio 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 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, 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 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( *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 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( 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 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); } mFocusedWindowHandlesByDisplay.erase(displayId); } @@ -3465,6 +3454,7 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& 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 50b52502a0..a4ba0dec6a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -157,6 +157,9 @@ private: // 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 @@ private: 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 0588374292..80fa2cb995 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -42,63 +42,4 @@ std::string dispatchModeToString(int32_t dispatchMode) { 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 499a75fdac..1ba5effada 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 @@ struct InputTarget { // 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 bd8d2a4d39..c262abb229 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -482,12 +482,31 @@ public: 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 @@ class FakeWindowHandle : public InputWindowHandle { 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { // 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 @@ TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { 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 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { // 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 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { 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 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 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 @@ TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { 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 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { 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 @@ public: // 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 @@ public: 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 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) 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 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { // 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 @@ protected: 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 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { 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 d476f7b40c..4c5e5da36a 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -51,7 +51,7 @@ cc_defaults { "libprocessgroup", "libprotobuf-cpp-lite", "libsync", - "libtimestats_proto", + "libtimestats", "libui", "libinput", "libutils", @@ -70,7 +70,6 @@ cc_defaults { "libperfetto_client_experimental", "librenderengine", "libserviceutils", - "libtimestats", "libtrace_proto", "libvr_manager", "libvrflinger", @@ -84,7 +83,6 @@ cc_defaults { "libcompositionengine", "librenderengine", "libserviceutils", - "libtimestats", ], export_shared_lib_headers: [ "android.hardware.graphics.allocator@2.0", @@ -96,6 +94,7 @@ cc_defaults { "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 @@ cc_defaults { "liblog", "libprocessgroup", "libsync", - "libtimestats_proto", "libutils", ], static_libs: [ diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 78f8104915..18477bbe4c 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -22,14 +22,13 @@ cc_defaults { "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 28d26536a8..6e2816715f 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -788,6 +788,7 @@ std::optional<base::unique_fd> Output::composeSurfaces(const Region& debugRegion outputState.usesClientComposition}; base::unique_fd readyFence; if (!hasClientComposition) { + setExpensiveRenderingExpected(false); return readyFence; } @@ -871,10 +872,6 @@ std::optional<base::unique_fd> Output::composeSurfaces(const Region& debugRegion 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 3a4df7404a..ae939693fa 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -700,6 +700,9 @@ TEST_F(DisplayTest, finishFrameDoesNotSkipCompositionIfNotDirtyOnHwcDisplay) { // 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 cb6c50cb85..80528e3cb9 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -2842,9 +2842,11 @@ const HdrCapabilities OutputComposeSurfacesTest:: 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 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDatas 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 4b9f55595b..5bba7d312b 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1918,7 +1918,7 @@ void Layer::setInputInfo(const InputWindowInfo& info) { 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 @@ void Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) const { 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 58a360a763..b8a35392f8 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -462,8 +462,8 @@ public: 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 4ef64b68ae..f2bc65db35 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -5,5 +5,6 @@ chaviw@google.com 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 56b3252272..a6fb3a4df0 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -39,6 +39,14 @@ public: 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 @@ public: * 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 @@ protected: */ 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 48f2abb7aa..2e5b6e9693 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -29,7 +29,7 @@ VSyncTracker::~VSyncTracker() = default; 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 @@ nsecs_t VSyncDispatchTimerQueueEntry::executing() { 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::rearmTimerSkippingUpdateFor( 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 @@ void VSyncDispatchTimerQueue::timerCallback() { if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) { callback->executing(); invocations.emplace_back( - Invocation{callback, *callback->lastExecutedVsyncTarget()}); + Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime}); } } @@ -195,12 +196,12 @@ void VSyncDispatchTimerQueue::timerCallback() { } 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 @@ CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) { } 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 0c9b4fe53e..087acc750c 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -36,7 +36,7 @@ public: // 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 @@ public: 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 @@ public: 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 158a184ba3..20b6238c92 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -48,7 +48,8 @@ public: 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 @@ public: } 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 46cfb21998..e90cf41707 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -4283,13 +4283,8 @@ void SurfaceFlinger::dumpOffscreenLayersProto(LayersProto& layersProto, uint32_t 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 2080a3847e..20c22184d2 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", ], - export_include_dirs: ["."], shared_libs: [ - "libtimestats_proto", - "libui", + "libbase", + "libcutils", + "liblog", + "libprotobuf-cpp-lite", + "libtimestats_proto", + "libui", + "libutils", + ], + export_include_dirs: ["."], + 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 0403237371..1c8199ac6d 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -55,7 +55,6 @@ cc_test { "liblog", "libnativewindow", "libprotobuf-cpp-full", - "libtimestats_proto", "libui", "libutils", ], @@ -68,7 +67,7 @@ cc_defaults { name: "ipc_defaults", cflags: [ "-Wall", - "-Werror", + "-Werror", ], } @@ -82,11 +81,11 @@ cc_test { ], cppflags: [ "-Wall", - "-Werror", - "-Wformat", - "-Wthread-safety", - "-Wunused", - "-Wunreachable-code", + "-Werror", + "-Wformat", + "-Wthread-safety", + "-Wunused", + "-Wunreachable-code", ], shared_libs: [ "libandroid", @@ -98,7 +97,6 @@ cc_test { "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 31837a9ec3..ff403f683e 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -29,7 +29,7 @@ cc_test { "liblog", "libnativewindow", "libsync", - "libtimestats_proto", + "libtimestats", "libui", "libutils", ], @@ -38,7 +38,6 @@ cc_test { "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 23fa940a99..becf484243 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -80,11 +80,12 @@ cc_test { "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 5846c77533..b51a025e30 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -101,7 +101,7 @@ public: 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 5aff4296f9..6fcb1af426 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -97,13 +97,14 @@ public: 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 @@ public: 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 @@ public: 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 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) { 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 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) { 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 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMove) { 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 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) { 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 @@ protected: 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, stateAfterInitialization) { 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 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) .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, stateSchedulingReallyLongWakeupLatency) 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 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { 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 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { .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, updateCallback) { 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, skipsUpdateIfJustScheduled) { 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, willSnapToNextTargettableVSync) { 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, 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 4df20aff39..188adea8ad 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -73,7 +73,8 @@ private: 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 @@ public: 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 @@ protected: 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 @@ TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) { .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 @@ TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) { 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 @@ TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) { 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 @@ TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) { ON_CALL(*mMockDispatch, schedule(_, _, _)) .WillByDefault(Return(ScheduleResult::CannotSchedule)); - EXPECT_DEATH(innerCb(mFakeCbTime), ".*"); + EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*"); } } // namespace android::scheduler |