summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/atrace/atrace.rc34
-rw-r--r--cmds/dumpstate/DumpstateService.cpp1
-rw-r--r--cmds/dumpstate/DumpstateUtil.cpp9
-rw-r--r--cmds/dumpstate/DumpstateUtil.h8
-rw-r--r--cmds/dumpstate/binder/android/os/IDumpstate.aidl3
-rw-r--r--cmds/dumpstate/dumpstate.cpp45
-rw-r--r--cmds/dumpstate/dumpstate.h2
-rw-r--r--cmds/installd/CrateManager.cpp3
-rw-r--r--cmds/installd/CrateManager.h1
-rw-r--r--cmds/installd/InstalldNativeService.cpp126
-rw-r--r--cmds/installd/InstalldNativeService.h26
-rw-r--r--cmds/installd/binder/android/os/IInstalld.aidl16
-rw-r--r--cmds/installd/dexopt.h5
-rw-r--r--cmds/installd/otapreopt.cpp108
-rw-r--r--cmds/installd/otapreopt_chroot.cpp122
-rw-r--r--cmds/installd/otapreopt_script.sh59
-rw-r--r--cmds/installd/run_dex2oat.cpp37
-rw-r--r--cmds/installd/run_dex2oat_test.cpp18
-rw-r--r--cmds/installd/tests/installd_service_test.cpp101
-rw-r--r--cmds/installd/utils.h1
-rw-r--r--cmds/servicemanager/Android.bp19
-rw-r--r--cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp48
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4bin144 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42bin168 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43bin152 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44bin168 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7bin40 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8bin136 -> 0 bytes
-rw-r--r--cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9bin40 -> 0 bytes
-rw-r--r--data/etc/Android.bp16
-rw-r--r--data/etc/android.hardware.thread_network.xml (renamed from data/etc/android.hardware.threadnetwork.xml)4
-rw-r--r--data/etc/android.software.opengles.deqp.level-latest.xml21
-rw-r--r--data/etc/android.software.vulkan.deqp.level-latest.xml21
-rw-r--r--data/etc/input/motion_predictor_config.xml15
-rw-r--r--data/etc/input/motion_predictor_model.tflitebin34080 -> 179532 bytes
-rw-r--r--include/android/performance_hint.h102
-rw-r--r--include/input/Input.h12
-rw-r--r--include/input/InputEventLabels.h6
-rw-r--r--include/input/TfLiteMotionPredictor.h15
-rw-r--r--include/input/VelocityTracker.h28
-rw-r--r--libs/binder/Android.bp3
-rw-r--r--libs/binder/IActivityManager.cpp11
-rw-r--r--libs/binder/MemoryHeapBase.cpp4
-rw-r--r--libs/binder/ndk/.clang-format2
-rw-r--r--libs/binder/ndk/include_cpp/android/binder_auto_utils.h26
-rw-r--r--libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp18
-rw-r--r--libs/binder/rust/Android.bp29
-rw-r--r--libs/binder/rust/binder_tokio/lib.rs7
-rw-r--r--libs/binder/rust/libbinder_ndk_bindgen_flags.txt11
-rw-r--r--libs/binder/rust/src/binder.rs12
-rw-r--r--libs/binder/rust/src/error.rs150
-rw-r--r--libs/binder/rust/src/lib.rs2
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/Android.bp26
-rw-r--r--libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp14
-rw-r--r--libs/binder/tests/Android.bp2
-rw-r--r--libs/binder/tests/binderAllocationLimits.cpp10
-rw-r--r--libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp13
-rw-r--r--libs/binder/tests/binderRecordReplayTest.cpp127
-rw-r--r--libs/binder/tests/binderRpcTest.cpp89
-rw-r--r--libs/binder/tests/binderRpcTestFixture.h35
-rw-r--r--libs/binder/tests/binderRpcTestTrusty.cpp33
-rw-r--r--libs/binder/tests/binderRpcUniversalTests.cpp2
-rw-r--r--libs/binder/tests/parcel_fuzzer/Android.bp40
-rw-r--r--libs/binder/tests/parcel_fuzzer/binder2corpus/README.md31
-rw-r--r--libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp90
-rw-r--r--libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h47
-rw-r--r--libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp16
-rw-r--r--libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp146
-rw-r--r--libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp26
-rwxr-xr-xlibs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh2
-rw-r--r--libs/binder/tests/rpc_fuzzer/main.cpp2
-rw-r--r--libs/binder/trusty/fuzzer/Android.bp39
-rw-r--r--libs/bufferstreams/rust/Android.bp5
-rw-r--r--libs/bufferstreams/rust/src/lib.rs134
-rw-r--r--libs/fakeservicemanager/FakeServiceManager.cpp12
-rw-r--r--libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h2
-rw-r--r--libs/graphicsenv/GraphicsEnv.cpp19
-rw-r--r--libs/graphicsenv/IGpuService.cpp9
-rw-r--r--libs/graphicsenv/include/graphicsenv/GraphicsEnv.h8
-rw-r--r--libs/gui/Android.bp2
-rw-r--r--libs/gui/BLASTBufferQueue.cpp7
-rw-r--r--libs/gui/BufferItemConsumer.cpp10
-rw-r--r--libs/gui/BufferQueueConsumer.cpp15
-rw-r--r--libs/gui/BufferQueueCore.cpp51
-rw-r--r--libs/gui/BufferQueueProducer.cpp149
-rw-r--r--libs/gui/ConsumerBase.cpp18
-rw-r--r--libs/gui/CpuConsumer.cpp10
-rw-r--r--libs/gui/GLConsumer.cpp10
-rw-r--r--libs/gui/OWNERS2
-rw-r--r--libs/gui/SurfaceComposerClient.cpp84
-rw-r--r--libs/gui/aidl/android/gui/ISurfaceComposer.aidl15
-rw-r--r--libs/gui/android/gui/StalledTransactionInfo.aidl (renamed from libs/ui/include_types/ui/DataspaceUtils.h)21
-rw-r--r--libs/gui/fuzzer/Android.bp2
-rw-r--r--libs/gui/fuzzer/libgui_fuzzer_utils.h3
-rw-r--r--libs/gui/include/gui/BufferQueueCore.h14
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h4
-rw-r--r--libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h2
-rw-r--r--libs/gui/tests/BLASTBufferQueue_test.cpp63
-rw-r--r--libs/gui/tests/BufferQueue_test.cpp85
-rw-r--r--libs/gui/tests/EndToEndNativeInputTest.cpp26
-rw-r--r--libs/gui/tests/GLTest.cpp8
-rw-r--r--libs/gui/tests/OWNERS3
-rw-r--r--libs/gui/tests/SurfaceTextureFBO_test.cpp2
-rw-r--r--libs/gui/tests/SurfaceTextureGL_test.cpp7
-rw-r--r--libs/gui/tests/Surface_test.cpp11
-rw-r--r--libs/input/InputEventLabels.cpp24
-rw-r--r--libs/input/InputTransport.cpp38
-rw-r--r--libs/input/InputVerifier.cpp2
-rw-r--r--libs/input/KeyCharacterMap.cpp123
-rw-r--r--libs/input/KeyLayoutMap.cpp119
-rw-r--r--libs/input/MotionPredictor.cpp19
-rw-r--r--libs/input/PropertyMap.cpp16
-rw-r--r--libs/input/TfLiteMotionPredictor.cpp36
-rw-r--r--libs/input/VelocityTracker.cpp32
-rw-r--r--libs/input/VirtualKeyMap.cpp17
-rw-r--r--libs/input/android/os/IInputConstants.aidl84
-rw-r--r--libs/input/rust/lib.rs1
-rw-r--r--libs/input/tests/Android.bp1
-rw-r--r--libs/input/tests/InputVerifier_test.cpp (renamed from libs/renderengine/mock/Image.cpp)19
-rw-r--r--libs/input/tests/MotionPredictor_test.cpp11
-rw-r--r--libs/input/tests/VelocityTracker_test.cpp5
-rw-r--r--libs/nativedisplay/surfacetexture/EGLConsumer.cpp8
-rw-r--r--libs/nativedisplay/surfacetexture/ImageConsumer.cpp2
-rw-r--r--libs/nativedisplay/surfacetexture/SurfaceTexture.cpp8
-rw-r--r--libs/nativewindow/include/android/data_space.h14
-rw-r--r--libs/nativewindow/rust/src/lib.rs6
-rw-r--r--libs/nativewindow/tests/benchmark/Android.bp50
-rw-r--r--libs/nativewindow/tests/benchmark/README.md22
-rw-r--r--libs/nativewindow/tests/benchmark/buffer_benchmarks.cc74
-rw-r--r--libs/nativewindow/tests/benchmark/buffer_benchmarks.rs60
-rw-r--r--libs/renderengine/Android.bp26
-rw-r--r--libs/renderengine/Description.cpp61
-rw-r--r--libs/renderengine/ExternalTexture.cpp8
-rw-r--r--libs/renderengine/Mesh.cpp146
-rw-r--r--libs/renderengine/OWNERS2
-rw-r--r--libs/renderengine/RenderEngine.cpp10
-rw-r--r--libs/renderengine/Texture.cpp77
-rw-r--r--libs/renderengine/benchmark/RenderEngineBench.cpp5
-rw-r--r--libs/renderengine/gl/GLESRenderEngine.cpp1866
-rw-r--r--libs/renderengine/gl/GLESRenderEngine.h313
-rw-r--r--libs/renderengine/gl/GLFramebuffer.cpp115
-rw-r--r--libs/renderengine/gl/GLFramebuffer.h68
-rw-r--r--libs/renderengine/gl/GLImage.cpp83
-rw-r--r--libs/renderengine/gl/GLImage.h54
-rw-r--r--libs/renderengine/gl/GLShadowTexture.cpp52
-rw-r--r--libs/renderengine/gl/GLShadowTexture.h44
-rw-r--r--libs/renderengine/gl/GLShadowVertexGenerator.cpp98
-rw-r--r--libs/renderengine/gl/GLShadowVertexGenerator.h65
-rw-r--r--libs/renderengine/gl/GLSkiaShadowPort.cpp656
-rw-r--r--libs/renderengine/gl/GLSkiaShadowPort.h96
-rw-r--r--libs/renderengine/gl/GLVertexBuffer.cpp55
-rw-r--r--libs/renderengine/gl/GLVertexBuffer.h49
-rw-r--r--libs/renderengine/gl/ImageManager.cpp148
-rw-r--r--libs/renderengine/gl/ImageManager.h74
-rw-r--r--libs/renderengine/gl/Program.cpp175
-rw-r--r--libs/renderengine/gl/Program.h120
-rw-r--r--libs/renderengine/gl/ProgramCache.cpp830
-rw-r--r--libs/renderengine/gl/ProgramCache.h239
-rw-r--r--libs/renderengine/gl/filters/BlurFilter.cpp268
-rw-r--r--libs/renderengine/gl/filters/BlurFilter.h95
-rw-r--r--libs/renderengine/gl/filters/GenericProgram.cpp122
-rw-r--r--libs/renderengine/gl/filters/GenericProgram.h51
-rw-r--r--libs/renderengine/include/renderengine/LayerSettings.h13
-rw-r--r--libs/renderengine/include/renderengine/Mesh.h205
-rw-r--r--libs/renderengine/include/renderengine/RenderEngine.h25
-rw-r--r--libs/renderengine/include/renderengine/Texture.h60
-rw-r--r--libs/renderengine/include/renderengine/mock/Framebuffer.h36
-rw-r--r--libs/renderengine/include/renderengine/mock/Image.h36
-rw-r--r--libs/renderengine/include/renderengine/mock/RenderEngine.h6
-rw-r--r--libs/renderengine/include/renderengine/private/Description.h95
-rw-r--r--libs/renderengine/mock/Framebuffer.cpp30
-rw-r--r--libs/renderengine/skia/AutoBackendTexture.cpp49
-rw-r--r--libs/renderengine/skia/GLExtensions.cpp (renamed from libs/renderengine/gl/GLExtensions.cpp)18
-rw-r--r--libs/renderengine/skia/GLExtensions.h (renamed from libs/renderengine/gl/GLExtensions.h)4
-rw-r--r--libs/renderengine/skia/SkiaGLRenderEngine.cpp36
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.cpp59
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.h10
-rw-r--r--libs/renderengine/skia/SkiaVkRenderEngine.cpp2
-rwxr-xr-xlibs/renderengine/skia/debug/record.sh1
-rw-r--r--libs/renderengine/skia/filters/KawaseBlurFilter.cpp63
-rw-r--r--libs/renderengine/tests/RenderEngineTest.cpp67
-rw-r--r--libs/renderengine/tests/RenderEngineThreadedTest.cpp14
-rw-r--r--libs/renderengine/threaded/RenderEngineThreaded.cpp56
-rw-r--r--libs/renderengine/threaded/RenderEngineThreaded.h3
-rw-r--r--libs/shaders/shaders.cpp4
-rw-r--r--libs/ui/Fence.cpp2
-rw-r--r--libs/ui/Gralloc5.cpp207
-rw-r--r--libs/ui/GraphicBufferAllocator.cpp4
-rw-r--r--libs/ui/include/ui/FatVector.h11
-rw-r--r--libs/ui/include_types/ui/HdrRenderTypeUtils.h64
-rw-r--r--libs/ui/tests/Android.bp4
-rw-r--r--libs/ui/tests/DataspaceUtils_test.cpp53
-rw-r--r--libs/ui/tests/HdrRenderTypeUtils_test.cpp65
-rw-r--r--libs/ultrahdr/Android.bp9
-rw-r--r--libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp19
-rw-r--r--libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE1
-rw-r--r--libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp2
-rw-r--r--libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp134
-rw-r--r--libs/ultrahdr/gainmapmath.cpp68
-rw-r--r--libs/ultrahdr/include/ultrahdr/gainmapmath.h2
-rw-r--r--libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h21
-rw-r--r--libs/ultrahdr/include/ultrahdr/jpegr.h261
-rw-r--r--libs/ultrahdr/include/ultrahdr/ultrahdr.h2
-rw-r--r--libs/ultrahdr/jpegdecoderhelper.cpp24
-rw-r--r--libs/ultrahdr/jpegencoderhelper.cpp137
-rw-r--r--libs/ultrahdr/jpegr.cpp1194
-rw-r--r--libs/ultrahdr/tests/Android.bp6
-rw-r--r--libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010bin2782080 -> 0 bytes
-rw-r--r--libs/ultrahdr/tests/gainmapmath_test.cpp7
-rw-r--r--libs/ultrahdr/tests/jpegencoderhelper_test.cpp25
-rw-r--r--libs/ultrahdr/tests/jpegr_test.cpp2972
-rw-r--r--opengl/libs/EGL/Loader.cpp136
-rw-r--r--services/gpuservice/GpuService.cpp2
-rw-r--r--services/inputflinger/Android.bp11
-rw-r--r--services/inputflinger/InputManager.cpp70
-rw-r--r--services/inputflinger/InputManager.h5
-rw-r--r--services/inputflinger/TEST_MAPPING8
-rw-r--r--services/inputflinger/aidl/Android.bp31
-rw-r--r--services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl34
-rw-r--r--services/inputflinger/host/InputDriver.cpp6
-rw-r--r--services/inputflinger/host/InputFlinger.cpp2
-rw-r--r--services/inputflinger/include/InputReaderBase.h6
-rw-r--r--services/inputflinger/reader/EventHub.cpp26
-rw-r--r--services/inputflinger/reader/InputDevice.cpp17
-rw-r--r--services/inputflinger/reader/InputReader.cpp55
-rw-r--r--services/inputflinger/reader/include/EventHub.h20
-rw-r--r--services/inputflinger/reader/include/InputDevice.h13
-rw-r--r--services/inputflinger/reader/include/InputReader.h17
-rw-r--r--services/inputflinger/reader/include/InputReaderContext.h3
-rw-r--r--services/inputflinger/reader/mapper/KeyboardInputMapper.cpp56
-rw-r--r--services/inputflinger/reader/mapper/KeyboardInputMapper.h3
-rw-r--r--services/inputflinger/reader/mapper/TouchpadInputMapper.cpp39
-rw-r--r--services/inputflinger/reader/mapper/TouchpadInputMapper.h7
-rw-r--r--services/inputflinger/reader/mapper/gestures/GestureConverter.cpp28
-rw-r--r--services/inputflinger/reader/mapper/gestures/GestureConverter.h5
-rw-r--r--services/inputflinger/rust/Android.bp52
-rw-r--r--services/inputflinger/rust/ffi/InputFlingerBootstrap.h (renamed from libs/renderengine/include/renderengine/Image.h)16
-rw-r--r--services/inputflinger/rust/lib.rs102
-rw-r--r--services/inputflinger/tests/Android.bp2
-rw-r--r--services/inputflinger/tests/FakeInputReaderPolicy.cpp17
-rw-r--r--services/inputflinger/tests/FakeInputReaderPolicy.h9
-rw-r--r--services/inputflinger/tests/GestureConverter_test.cpp473
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp881
-rw-r--r--services/inputflinger/tests/InputReader_test.cpp185
-rw-r--r--services/inputflinger/tests/InstrumentedInputReader.h4
-rw-r--r--services/inputflinger/tests/InterfaceMocks.h3
-rw-r--r--services/inputflinger/tests/KeyboardInputMapper_test.cpp152
-rw-r--r--services/inputflinger/tests/TestInputListener.cpp12
-rw-r--r--services/inputflinger/tests/TestInputListener.h4
-rw-r--r--services/inputflinger/tests/TestInputListenerMatchers.cpp (renamed from libs/renderengine/include/renderengine/Framebuffer.h)28
-rw-r--r--services/inputflinger/tests/TestInputListenerMatchers.h156
-rw-r--r--services/inputflinger/tests/UinputDevice.cpp16
-rw-r--r--services/inputflinger/tests/UinputDevice.h4
-rw-r--r--services/inputflinger/tests/fuzzers/Android.bp9
-rw-r--r--services/inputflinger/tests/fuzzers/MapperHelpers.h7
-rw-r--r--services/powermanager/Android.bp4
-rw-r--r--services/powermanager/benchmarks/Android.bp2
-rw-r--r--services/powermanager/tests/Android.bp5
-rw-r--r--services/sensorservice/SensorService.cpp102
-rw-r--r--services/sensorservice/SensorService.h19
-rw-r--r--services/sensorservice/tests/sensorservicetest.cpp2
-rw-r--r--services/surfaceflinger/Android.bp2
-rw-r--r--services/surfaceflinger/ClientCache.h4
-rw-r--r--services/surfaceflinger/CompositionEngine/Android.bp2
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h4
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h2
-rw-r--r--services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp6
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Display.cpp7
-rw-r--r--services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp22
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Output.cpp22
-rw-r--r--services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp1
-rw-r--r--services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp26
-rw-r--r--services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp2
-rw-r--r--services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp30
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp57
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp23
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp11
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp90
-rw-r--r--services/surfaceflinger/DisplayDevice.cpp24
-rw-r--r--services/surfaceflinger/DisplayDevice.h7
-rw-r--r--services/surfaceflinger/DisplayHardware/HWC2.cpp8
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.cpp22
-rw-r--r--services/surfaceflinger/FrontEnd/LayerCreationArgs.h1
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshot.cpp21
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshot.h4
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp16
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h3
-rw-r--r--services/surfaceflinger/FrontEnd/RequestedLayerState.cpp11
-rw-r--r--services/surfaceflinger/FrontEnd/RequestedLayerState.h3
-rw-r--r--services/surfaceflinger/FrontEnd/TransactionHandler.cpp39
-rw-r--r--services/surfaceflinger/FrontEnd/TransactionHandler.h19
-rw-r--r--services/surfaceflinger/HdrLayerInfoReporter.cpp19
-rw-r--r--services/surfaceflinger/HdrLayerInfoReporter.h15
-rw-r--r--services/surfaceflinger/Layer.cpp42
-rw-r--r--services/surfaceflinger/Layer.h10
-rw-r--r--services/surfaceflinger/LayerFE.cpp4
-rw-r--r--services/surfaceflinger/OWNERS2
-rw-r--r--services/surfaceflinger/Scheduler/RefreshRateSelector.cpp58
-rw-r--r--services/surfaceflinger/Scheduler/RefreshRateSelector.h16
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.cpp12
-rw-r--r--services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h3
-rw-r--r--services/surfaceflinger/ScreenCaptureOutput.cpp22
-rw-r--r--services/surfaceflinger/ScreenCaptureOutput.h4
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp754
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h75
-rw-r--r--services/surfaceflinger/SurfaceFlingerProperties.cpp8
-rw-r--r--services/surfaceflinger/SurfaceFlingerProperties.h3
-rw-r--r--services/surfaceflinger/TEST_MAPPING5
-rw-r--r--services/surfaceflinger/Tracing/LayerTracing.cpp5
-rw-r--r--services/surfaceflinger/Tracing/LayerTracing.h4
-rw-r--r--services/surfaceflinger/Tracing/TransactionRingBuffer.h (renamed from services/surfaceflinger/Tracing/RingBuffer.h)2
-rw-r--r--services/surfaceflinger/Tracing/TransactionTracing.h4
-rw-r--r--services/surfaceflinger/Utils/RingBuffer.h68
-rw-r--r--services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp3
-rw-r--r--services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h2
-rw-r--r--services/surfaceflinger/layerproto/display.proto4
-rw-r--r--services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop5
-rw-r--r--services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp24
-rw-r--r--services/surfaceflinger/tests/LayerTransactionTest.h4
-rw-r--r--services/surfaceflinger/tests/LayerTransaction_test.cpp29
-rw-r--r--services/surfaceflinger/tests/unittests/Android.bp2
-rw-r--r--services/surfaceflinger/tests/unittests/CompositionTest.cpp18
-rw-r--r--services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp10
-rw-r--r--services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp3
-rw-r--r--services/surfaceflinger/tests/unittests/LayerHierarchyTest.h39
-rw-r--r--services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp66
-rw-r--r--services/surfaceflinger/tests/unittests/MessageQueueTest.cpp2
-rw-r--r--services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp142
-rw-r--r--services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp3
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp4
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp160
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp32
-rw-r--r--services/surfaceflinger/tests/unittests/TestableScheduler.h2
-rw-r--r--services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h14
-rw-r--r--services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp41
-rw-r--r--services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp5
-rw-r--r--services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp5
-rw-r--r--services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp1
-rw-r--r--services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h2
-rw-r--r--services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h6
-rw-r--r--services/surfaceflinger/tests/utils/ScreenshotUtils.h2
-rw-r--r--services/vibratorservice/TEST_MAPPING20
-rw-r--r--services/vibratorservice/VibratorCallbackScheduler.cpp11
-rw-r--r--services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h8
-rw-r--r--services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp52
-rw-r--r--services/vr/virtual_touchpad/VirtualTouchpadService.cpp2
-rw-r--r--vulkan/include/vulkan/vk_android_native_buffer.h43
-rw-r--r--vulkan/libvulkan/driver.cpp17
-rw-r--r--vulkan/libvulkan/driver_gen.cpp8
-rw-r--r--vulkan/libvulkan/driver_gen.h1
-rw-r--r--vulkan/libvulkan/layers_extensions.cpp4
-rw-r--r--vulkan/libvulkan/swapchain.cpp42
-rw-r--r--vulkan/nulldrv/null_driver.cpp11
-rw-r--r--vulkan/nulldrv/null_driver_gen.cpp1
-rw-r--r--vulkan/nulldrv/null_driver_gen.h1
-rw-r--r--vulkan/scripts/generator_common.py1
399 files changed, 9612 insertions, 11696 deletions
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index f1d8c72d85..fc0801cce8 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -228,10 +228,6 @@ on late-init
chmod 0666 /sys/kernel/debug/tracing/events/thermal/cdev_update/enable
chmod 0666 /sys/kernel/tracing/events/thermal/cdev_update/enable
-# Tracing disabled by default
- write /sys/kernel/debug/tracing/tracing_on 0
- write /sys/kernel/tracing/tracing_on 0
-
# Read and truncate the kernel trace.
chmod 0666 /sys/kernel/debug/tracing/trace
chmod 0666 /sys/kernel/tracing/trace
@@ -310,18 +306,9 @@ on late-init
chmod 0666 /sys/kernel/tracing/events/synthetic/suspend_resume_minimal/enable
chmod 0666 /sys/kernel/debug/tracing/events/synthetic/suspend_resume_minimal/enable
-on late-init && property:ro.boot.fastboot.boottrace=enabled
- setprop debug.atrace.tags.enableflags 802922
- setprop persist.traced.enable 0
- write /sys/kernel/tracing/events/binder/binder_transaction/enable 1
- write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 1
- write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 1
- write /sys/kernel/tracing/events/binder/binder_set_priority/enable 1
- write /sys/kernel/tracing/events/binder/binder_lock/enable 1
- write /sys/kernel/tracing/events/binder/binder_locked/enable 1
- write /sys/kernel/tracing/events/binder/binder_unlock/enable 1
- write /sys/kernel/debug/tracing/tracing_on 1
- write /sys/kernel/tracing/tracing_on 1
+on late-init && property:ro.boot.fastboot.boottrace=
+ write /sys/kernel/debug/tracing/tracing_on 0
+ write /sys/kernel/tracing/tracing_on 0
# Only create the tracing instance if persist.mm_events.enabled
# Attempting to remove the tracing instance after it has been created
@@ -534,7 +521,6 @@ on late-init && property:ro.boot.hypervisor.vm.supported=1
chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/id
chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/id
-
on property:persist.debug.atrace.boottrace=1
start boottrace
@@ -543,17 +529,3 @@ service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categ
user root
disabled
oneshot
-
-on property:sys.boot_completed=1 && property:ro.boot.fastboot.boottrace=enabled
- setprop debug.atrace.tags.enableflags 0
- setprop persist.traced.enable 1
- write /sys/kernel/tracing/events/binder/binder_transaction/enable 0
- write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 0
- write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 0
- write /sys/kernel/tracing/events/binder/binder_set_priority/enable 0
- write /sys/kernel/tracing/events/binder/binder_lock/enable 0
- write /sys/kernel/tracing/events/binder/binder_locked/enable 0
- write /sys/kernel/tracing/events/binder/binder_unlock/enable 0
- write /sys/kernel/debug/tracing/tracing_on 0
- write /sys/kernel/tracing/tracing_on 0
-
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index a7bc018ff6..c787113765 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -141,6 +141,7 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid,
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WEAR &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI &&
+ bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_ONBOARDING &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) {
MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode);
signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index aa42541a66..615701ccc1 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -207,6 +207,7 @@ std::string PropertiesHelper::build_type_ = "";
int PropertiesHelper::dry_run_ = -1;
int PropertiesHelper::unroot_ = -1;
int PropertiesHelper::parallel_run_ = -1;
+int PropertiesHelper::strict_run_ = -1;
bool PropertiesHelper::IsUserBuild() {
if (build_type_.empty()) {
@@ -237,6 +238,14 @@ bool PropertiesHelper::IsParallelRun() {
return parallel_run_ == 1;
}
+bool PropertiesHelper::IsStrictRun() {
+ if (strict_run_ == -1) {
+ // Defaults to using stricter timeouts.
+ strict_run_ = android::base::GetBoolProperty("dumpstate.strict_run", true) ? 1 : 0;
+ }
+ return strict_run_ == 1;
+}
+
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd.get() < 0) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b00c46e0db..9e955e3c04 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -193,11 +193,19 @@ class PropertiesHelper {
*/
static bool IsParallelRun();
+ /*
+ * Strict-run mode is determined by the `dumpstate.strict_run` sysprop which
+ * will default to true. This results in shortened timeouts for flaky
+ * sections.
+ */
+ static bool IsStrictRun();
+
private:
static std::string build_type_;
static int dry_run_;
static int unroot_;
static int parallel_run_;
+ static int strict_run_;
};
/*
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 0dc8f5ad64..fa9bcf351c 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -49,6 +49,9 @@ interface IDumpstate {
// Default mode.
const int BUGREPORT_MODE_DEFAULT = 6;
+ // Bugreport taken for onboarding related flows.
+ const int BUGREPORT_MODE_ONBOARDING = 7;
+
// Use pre-dumped data.
const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 0x1;
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5d5c7744e7..376d57a624 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -822,9 +822,12 @@ void Dumpstate::PrintHeader() const {
RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
CommandOptions::WithTimeout(1).Always().Build());
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
- id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
- options_->args.c_str(), options_->bugreport_mode_string.c_str());
+ printf(
+ "Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d strict_run=%d args=%s "
+ "bugreport_mode=%s\n",
+ id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
+ PropertiesHelper::IsStrictRun(), options_->args.c_str(),
+ options_->bugreport_mode_string.c_str());
printf("\n");
}
@@ -1046,7 +1049,8 @@ static void DumpIncidentReport() {
MYLOGE("Could not open %s to dump incident report.\n", path.c_str());
return;
}
- RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(20).Build());
+ RunCommandToFd(fd, "", {"incident", "-u"},
+ CommandOptions::WithTimeout(PropertiesHelper::IsStrictRun() ? 20 : 120).Build());
bool empty = 0 == lseek(fd, 0, SEEK_END);
if (!empty) {
// Use a different name from "incident.proto"
@@ -1416,12 +1420,12 @@ static void DumpHals(int out_fd = STDOUT_FILENO) {
auto ret = sm->list([&](const auto& interfaces) {
for (const std::string& interface : interfaces) {
std::string cleanName = interface;
- std::replace_if(cleanName.begin(),
- cleanName.end(),
- [](char c) {
- return !isalnum(c) &&
- std::string("@-_:.").find(c) == std::string::npos;
- }, '_');
+ std::replace_if(
+ cleanName.begin(), cleanName.end(),
+ [](char c) {
+ return !isalnum(c) && std::string("@-_.").find(c) == std::string::npos;
+ },
+ '_');
const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName;
bool empty = false;
@@ -2172,6 +2176,11 @@ static void DumpstateWifiOnly() {
printf("========================================================\n");
}
+// Collects a lightweight dumpstate to be used for debugging onboarding related flows.
+static void DumpstateOnboardingOnly() {
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+}
+
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
@@ -2304,6 +2313,7 @@ static dumpstate_hal_hidl::DumpstateMode GetDumpstateHalModeHidl(
return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
return dumpstate_hal_hidl::DumpstateMode::WIFI;
+ case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING:
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
return dumpstate_hal_hidl::DumpstateMode::DEFAULT;
}
@@ -2325,6 +2335,7 @@ static dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode GetDumpstateHalModeAi
return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI;
+ case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING:
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT;
}
@@ -2808,6 +2819,8 @@ static inline const char* ModeToString(Dumpstate::BugreportMode mode) {
return "BUGREPORT_TELEPHONY";
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
return "BUGREPORT_WIFI";
+ case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING:
+ return "BUGREPORT_ONBOARDING";
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
return "BUGREPORT_DEFAULT";
}
@@ -2853,6 +2866,10 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt
options->wifi_only = true;
options->do_screenshot = false;
break;
+ case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING:
+ options->onboarding_only = true;
+ options->do_screenshot = false;
+ break;
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
break;
}
@@ -3141,6 +3158,12 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
+ if (PropertiesHelper::IsStrictRun()) {
+ MYLOGI(
+ "Running on strict-run mode, which has shorter timeouts "
+ "(to disable, call 'setprop dumpstate.strict_run false')\n");
+ }
+
MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
id_, options_->args.c_str(), options_->bugreport_mode_string.c_str(), version_.c_str());
@@ -3266,6 +3289,8 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
DumpstateWifiOnly();
} else if (options_->limited_only) {
DumpstateLimitedOnly();
+ } else if (options_->onboarding_only) {
+ DumpstateOnboardingOnly();
} else {
// Dump state for the default case. This also drops root.
RunStatus s = DumpstateDefaultAfterCritical();
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 8a31c314d9..0a032ecfc3 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -201,6 +201,7 @@ class Dumpstate {
BUGREPORT_WEAR = android::os::IDumpstate::BUGREPORT_MODE_WEAR,
BUGREPORT_TELEPHONY = android::os::IDumpstate::BUGREPORT_MODE_TELEPHONY,
BUGREPORT_WIFI = android::os::IDumpstate::BUGREPORT_MODE_WIFI,
+ BUGREPORT_ONBOARDING = android::os::IDumpstate::BUGREPORT_MODE_ONBOARDING,
BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT
};
@@ -412,6 +413,7 @@ class Dumpstate {
bool show_header_only = false;
bool telephony_only = false;
bool wifi_only = false;
+ bool onboarding_only = false;
// Trimmed-down version of dumpstate to only include whitelisted logs.
bool limited_only = false;
// Whether progress updates should be published.
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
index b17cba15d5..fd1df35aee 100644
--- a/cmds/installd/CrateManager.cpp
+++ b/cmds/installd/CrateManager.cpp
@@ -29,9 +29,10 @@
#include <sys/xattr.h>
#include <unistd.h>
+#include <utils.h>
#include <fstream>
+#include <functional>
#include <string>
-#include <utils.h>
#include "utils.h"
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
index 1f30b5dc79..d9b590f784 100644
--- a/cmds/installd/CrateManager.h
+++ b/cmds/installd/CrateManager.h
@@ -25,6 +25,7 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <functional>
#include <optional>
#include <string>
#include <vector>
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b302f52f3e..e2a2927f2b 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -19,6 +19,7 @@
#include <errno.h>
#include <fts.h>
#include <inttypes.h>
+#include <linux/fsverity.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -51,6 +52,7 @@
#include <android-base/unique_fd.h>
#include <cutils/ashmem.h>
#include <cutils/fs.h>
+#include <cutils/misc.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
#include <linux/quota.h>
@@ -84,6 +86,8 @@
using android::base::ParseUint;
using android::base::Split;
using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::os::ParcelFileDescriptor;
using std::endl;
namespace android {
@@ -229,6 +233,14 @@ binder::Status checkArgumentFileName(const std::string& path) {
return ok();
}
+binder::Status checkUidInAppRange(int32_t appUid) {
+ if (FIRST_APPLICATION_UID <= appUid && appUid <= LAST_APPLICATION_UID) {
+ return ok();
+ }
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ StringPrintf("UID %d is outside of the range", appUid));
+}
+
#define ENFORCE_UID(uid) { \
binder::Status status = checkUid((uid)); \
if (!status.isOk()) { \
@@ -283,6 +295,14 @@ binder::Status checkArgumentFileName(const std::string& path) {
} \
}
+#define CHECK_ARGUMENT_UID_IN_APP_RANGE(uid) \
+ { \
+ binder::Status status = checkUidInAppRange((uid)); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+ }
+
#ifdef GRANULAR_LOCKS
/**
@@ -383,6 +403,33 @@ using PackageLockGuard = std::lock_guard<PackageLock>;
} // namespace
+binder::Status InstalldNativeService::FsveritySetupAuthToken::authenticate(
+ const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId) {
+ int open_flags = fcntl(authFd.get(), F_GETFL);
+ if (open_flags < 0) {
+ return exception(binder::Status::EX_SERVICE_SPECIFIC, "fcntl failed");
+ }
+ if ((open_flags & O_ACCMODE) != O_WRONLY && (open_flags & O_ACCMODE) != O_RDWR) {
+ return exception(binder::Status::EX_SECURITY, "Received FD with unexpected open flag");
+ }
+ if (fstat(authFd.get(), &this->mStatFromAuthFd) < 0) {
+ return exception(binder::Status::EX_SERVICE_SPECIFIC, "fstat failed");
+ }
+ if (!S_ISREG(this->mStatFromAuthFd.st_mode)) {
+ return exception(binder::Status::EX_SECURITY, "Not a regular file");
+ }
+ // Don't accept a file owned by a different app.
+ uid_t uid = multiuser_get_uid(userId, appUid);
+ if (this->mStatFromAuthFd.st_uid != uid) {
+ return exception(binder::Status::EX_SERVICE_SPECIFIC, "File not owned by appUid");
+ }
+ return ok();
+}
+
+bool InstalldNativeService::FsveritySetupAuthToken::isSameStat(const struct stat& st) const {
+ return memcmp(&st, &mStatFromAuthFd, sizeof(st)) == 0;
+}
+
status_t InstalldNativeService::start() {
IPCThreadState::self()->disableBackgroundScheduling(true);
status_t ret = BinderService<InstalldNativeService>::publish();
@@ -3857,5 +3904,84 @@ binder::Status InstalldNativeService::getOdexVisibility(
return *_aidl_return == -1 ? error() : ok();
}
+// Creates an auth token to be used in enableFsverity. This token is really to store a proof that
+// the caller can write to a file, represented by the authFd. Effectively, system_server as the
+// attacker-in-the-middle cannot enable fs-verity on arbitrary app files. If the FD is not writable,
+// return null.
+//
+// appUid and userId are passed for additional ownership check, such that one app can not be
+// authenticated for another app's file. These parameters are assumed trusted for this purpose of
+// consistency check.
+//
+// Notably, creating the token allows us to manage the writable FD easily during enableFsverity.
+// Since enabling fs-verity to a file requires no outstanding writable FD, passing the authFd to the
+// server allows the server to hold the only reference (as long as the client app doesn't).
+binder::Status InstalldNativeService::createFsveritySetupAuthToken(
+ const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId,
+ sp<IFsveritySetupAuthToken>* _aidl_return) {
+ CHECK_ARGUMENT_UID_IN_APP_RANGE(appUid);
+ ENFORCE_VALID_USER(userId);
+
+ auto token = sp<FsveritySetupAuthToken>::make();
+ binder::Status status = token->authenticate(authFd, appUid, userId);
+ if (!status.isOk()) {
+ return status;
+ }
+ *_aidl_return = token;
+ return ok();
+}
+
+// Enables fs-verity for filePath, which must be an absolute path and the same inode as in the auth
+// token previously returned from createFsveritySetupAuthToken, and owned by the app uid. As
+// installd is more privileged than its client / system server, we attempt to limit what a
+// (compromised) client can do.
+//
+// The reason for this app request to go through installd is to avoid exposing a risky area (PKCS#7
+// signature verification) in the kernel to the app as an attack surface (it can't be system server
+// because it can't override DAC and manipulate app files). Note that we should be able to drop
+// these hops and simply the app calls the ioctl, once all upgrading devices run with a kernel
+// without fs-verity built-in signature (https://r.android.com/2650402).
+binder::Status InstalldNativeService::enableFsverity(const sp<IFsveritySetupAuthToken>& authToken,
+ const std::string& filePath,
+ const std::string& packageName,
+ int32_t* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PATH(filePath);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ LOCK_PACKAGE();
+ if (authToken == nullptr) {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Received a null auth token");
+ }
+
+ // Authenticate to check the targeting file is the same inode as the authFd.
+ sp<IBinder> authTokenBinder = IInterface::asBinder(authToken)->localBinder();
+ if (authTokenBinder == nullptr) {
+ return exception(binder::Status::EX_SECURITY, "Received a non-local auth token");
+ }
+ auto authTokenInstance = sp<FsveritySetupAuthToken>::cast(authTokenBinder);
+ unique_fd rfd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ struct stat stFromPath;
+ if (fstat(rfd.get(), &stFromPath) < 0) {
+ *_aidl_return = errno;
+ return ok();
+ }
+ if (!authTokenInstance->isSameStat(stFromPath)) {
+ LOG(DEBUG) << "FD authentication failed";
+ *_aidl_return = EPERM;
+ return ok();
+ }
+
+ fsverity_enable_arg arg = {};
+ arg.version = 1;
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
+ if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
+ *_aidl_return = errno;
+ } else {
+ *_aidl_return = 0;
+ }
+ return ok();
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 521afc3f97..0f28234422 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -19,6 +19,7 @@
#define COMMANDS_H_
#include <inttypes.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <shared_mutex>
@@ -35,8 +36,26 @@
namespace android {
namespace installd {
+using IFsveritySetupAuthToken = android::os::IInstalld::IFsveritySetupAuthToken;
+
class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {
public:
+ class FsveritySetupAuthToken : public os::IInstalld::BnFsveritySetupAuthToken {
+ public:
+ FsveritySetupAuthToken() : mStatFromAuthFd() {}
+
+ binder::Status authenticate(const android::os::ParcelFileDescriptor& authFd, int32_t appUid,
+ int32_t userId);
+ bool isSameStat(const struct stat& st) const;
+
+ private:
+ // Not copyable or movable
+ FsveritySetupAuthToken(const FsveritySetupAuthToken&) = delete;
+ FsveritySetupAuthToken& operator=(const FsveritySetupAuthToken&) = delete;
+
+ struct stat mStatFromAuthFd;
+ };
+
static status_t start();
static char const* getServiceName() { return "installd"; }
virtual status_t dump(int fd, const Vector<String16> &args) override;
@@ -192,6 +211,13 @@ public:
const std::optional<std::string>& outputPath,
int32_t* _aidl_return);
+ binder::Status createFsveritySetupAuthToken(const android::os::ParcelFileDescriptor& authFd,
+ int32_t appUid, int32_t userId,
+ android::sp<IFsveritySetupAuthToken>* _aidl_return);
+ binder::Status enableFsverity(const android::sp<IFsveritySetupAuthToken>& authToken,
+ const std::string& filePath, const std::string& packageName,
+ int32_t* _aidl_return);
+
private:
std::recursive_mutex mLock;
std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock;
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 9ad853b1df..8893e38c03 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -134,6 +134,22 @@ interface IInstalld {
int getOdexVisibility(@utf8InCpp String packageName, @utf8InCpp String apkPath,
@utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath);
+ interface IFsveritySetupAuthToken {
+ // Using an interface here is an easy way to create and maintain an IBinder object across
+ // the processes. When installd creates this binder object, it stores the file stat
+ // privately for later authentication, and only returns the reference to the caller process.
+ // Once the binder object has no reference count, it gets destructed automatically
+ // (alternatively, installd can maintain an internal mapping, but it is more error prone
+ // because the app may crash and not finish the fs-verity setup, keeping the memory unused
+ // forever).
+ //
+ // We don't necessarily need a method here, so it's left blank intentionally.
+ }
+ IFsveritySetupAuthToken createFsveritySetupAuthToken(in ParcelFileDescriptor authFd, int appUid,
+ int userId);
+ int enableFsverity(in IFsveritySetupAuthToken authToken, @utf8InCpp String filePath,
+ @utf8InCpp String packageName);
+
const int FLAG_STORAGE_DE = 0x1;
const int FLAG_STORAGE_CE = 0x2;
const int FLAG_STORAGE_EXTERNAL = 0x4;
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 5cf402c54c..df02588a22 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -18,6 +18,7 @@
#define DEXOPT_H_
#include "installd_constants.h"
+#include "unique_file.h"
#include <sys/types.h>
@@ -156,6 +157,10 @@ const char* select_execution_binary(
// artifacts.
int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir);
+UniqueFile maybe_open_reference_profile(const std::string& pkgname, const std::string& dex_path,
+ const char* profile_name, bool profile_guided,
+ bool is_public, int uid, bool is_secondary_dex);
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 7cabdb09e1..27ae8f6e6f 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -14,20 +14,21 @@
** limitations under the License.
*/
-#include <algorithm>
#include <inttypes.h>
-#include <limits>
-#include <random>
-#include <regex>
#include <selinux/android.h>
#include <selinux/avc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/capability.h>
+#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/stat.h>
-#include <sys/mman.h>
#include <sys/wait.h>
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <random>
+#include <regex>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -47,6 +48,7 @@
#include "otapreopt_parameters.h"
#include "otapreopt_utils.h"
#include "system_properties.h"
+#include "unique_file.h"
#include "utils.h"
#ifndef LOG_TAG
@@ -87,6 +89,9 @@ static_assert(DEXOPT_GENERATE_APP_IMAGE == 1 << 12, "DEXOPT_GENERATE_APP_IMAGE u
static_assert(DEXOPT_MASK == (0x3dfe | DEXOPT_IDLE_BACKGROUND_JOB),
"DEXOPT_MASK unexpected.");
+constexpr const char* kAotCompilerFilters[]{
+ "space-profile", "space", "speed-profile", "speed", "everything-profile", "everything",
+};
template<typename T>
static constexpr bool IsPowerOfTwo(T x) {
@@ -415,6 +420,32 @@ private:
return (strcmp(arg, "!") == 0) ? nullptr : arg;
}
+ bool IsAotCompilation() const {
+ if (std::find(std::begin(kAotCompilerFilters), std::end(kAotCompilerFilters),
+ parameters_.compiler_filter) == std::end(kAotCompilerFilters)) {
+ return false;
+ }
+
+ int dexopt_flags = parameters_.dexopt_flags;
+ bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+ bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0;
+ bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0;
+
+ if (profile_guided) {
+ UniqueFile reference_profile =
+ maybe_open_reference_profile(parameters_.pkgName, parameters_.apk_path,
+ parameters_.profile_name, profile_guided,
+ is_public, parameters_.uid, is_secondary_dex);
+ struct stat sbuf;
+ if (reference_profile.fd() == -1 ||
+ (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
bool ShouldSkipPreopt() const {
// There's one thing we have to be careful about: we may/will be asked to compile an app
// living in the system image. This may be a valid request - if the app wasn't compiled,
@@ -439,9 +470,12 @@ private:
// (This is ugly as it's the only thing where we need to understand the contents
// of parameters_, but it beats postponing the decision or using the call-
// backs to do weird things.)
+
+ // In addition, no need to preopt for "verify". The existing vdex files in the OTA package
+ // and the /data partition will still be usable after the OTA update is applied.
const char* apk_path = parameters_.apk_path;
CHECK(apk_path != nullptr);
- if (StartsWith(apk_path, android_root_)) {
+ if (StartsWith(apk_path, android_root_) || !IsAotCompilation()) {
const char* last_slash = strrchr(apk_path, '/');
if (last_slash != nullptr) {
std::string path(apk_path, last_slash - apk_path + 1);
@@ -471,13 +505,18 @@ private:
// TODO(calin): embed the profile name in the parameters.
int Dexopt() {
std::string error;
+
+ int dexopt_flags = parameters_.dexopt_flags;
+ // Make sure dex2oat is run with background priority.
+ dexopt_flags |= DEXOPT_BOOTCOMPLETE | DEXOPT_IDLE_BACKGROUND_JOB;
+
int res = dexopt(parameters_.apk_path,
parameters_.uid,
parameters_.pkgName,
parameters_.instruction_set,
parameters_.dexopt_needed,
parameters_.oat_dir,
- parameters_.dexopt_flags,
+ dexopt_flags,
parameters_.compiler_filter,
parameters_.volume_uuid,
parameters_.shared_libraries,
@@ -521,61 +560,6 @@ private:
return Dexopt();
}
- ////////////////////////////////////
- // Helpers, mostly taken from ART //
- ////////////////////////////////////
-
- // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
- static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
- constexpr size_t kPageSize = PAGE_SIZE;
- static_assert(IsPowerOfTwo(kPageSize), "page size must be power of two");
- CHECK_EQ(min_delta % kPageSize, 0u);
- CHECK_EQ(max_delta % kPageSize, 0u);
- CHECK_LT(min_delta, max_delta);
-
- std::default_random_engine generator;
- generator.seed(GetSeed());
- std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
- int32_t r = distribution(generator);
- if (r % 2 == 0) {
- r = RoundUp(r, kPageSize);
- } else {
- r = RoundDown(r, kPageSize);
- }
- CHECK_LE(min_delta, r);
- CHECK_GE(max_delta, r);
- CHECK_EQ(r % kPageSize, 0u);
- return r;
- }
-
- static uint64_t GetSeed() {
-#ifdef __BIONIC__
- // Bionic exposes arc4random, use it.
- uint64_t random_data;
- arc4random_buf(&random_data, sizeof(random_data));
- return random_data;
-#else
-#error "This is only supposed to run with bionic. Otherwise, implement..."
-#endif
- }
-
- void AddCompilerOptionFromSystemProperty(const char* system_property,
- const char* prefix,
- bool runtime,
- std::vector<std::string>& out) const {
- const std::string* value = system_properties_.GetProperty(system_property);
- if (value != nullptr) {
- if (runtime) {
- out.push_back("--runtime-arg");
- }
- if (prefix != nullptr) {
- out.push_back(StringPrintf("%s%s", prefix, value->c_str()));
- } else {
- out.push_back(*value);
- }
- }
- }
-
static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH";
static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT";
static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA";
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c86993cb06..c40caf56d8 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -19,9 +19,12 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
+#include <unistd.h>
+#include <algorithm>
#include <array>
#include <fstream>
+#include <iostream>
#include <sstream>
#include <android-base/file.h>
@@ -29,6 +32,7 @@
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <libdm/dm.h>
#include <selinux/android.h>
@@ -37,7 +41,7 @@
#include "otapreopt_utils.h"
#ifndef LOG_TAG
-#define LOG_TAG "otapreopt"
+#define LOG_TAG "otapreopt_chroot"
#endif
using android::base::StringPrintf;
@@ -49,20 +53,22 @@ namespace installd {
// so just try the possibilities one by one.
static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
-static void CloseDescriptor(int fd) {
- if (fd >= 0) {
- int result = close(fd);
- UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor
- // that we do *not* want.
- }
-}
-
static void CloseDescriptor(const char* descriptor_string) {
int fd = -1;
std::istringstream stream(descriptor_string);
stream >> fd;
if (!stream.fail()) {
- CloseDescriptor(fd);
+ if (fd >= 0) {
+ if (close(fd) < 0) {
+ PLOG(ERROR) << "Failed to close " << fd;
+ }
+ }
+ }
+}
+
+static void SetCloseOnExec(int fd) {
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
+ PLOG(ERROR) << "Failed to set FD_CLOEXEC on " << fd;
}
}
@@ -129,24 +135,39 @@ static void TryExtraMount(const char* name, const char* slot, const char* target
}
// Entry for otapreopt_chroot. Expected parameters are:
-// [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params]
-// The file descriptor denoted by status-fd will be closed. The rest of the parameters will
-// be passed on to otapreopt in the chroot.
+//
+// [cmd] [status-fd] [target-slot-suffix]
+//
+// The file descriptor denoted by status-fd will be closed. Dexopt commands on
+// the form
+//
+// "dexopt" [dexopt-params]
+//
+// are then read from stdin until EOF and passed on to /system/bin/otapreopt one
+// by one. After each call a line with the current command count is written to
+// stdout and flushed.
static int otapreopt_chroot(const int argc, char **arg) {
// Validate arguments
- // We need the command, status channel and target slot, at a minimum.
- if(argc < 3) {
- PLOG(ERROR) << "Not enough arguments.";
+ if (argc == 2 && std::string_view(arg[1]) == "--version") {
+ // Accept a single --version flag, to allow the script to tell this binary
+ // from the earlier one.
+ std::cout << "2" << std::endl;
+ return 0;
+ }
+ if (argc != 3) {
+ LOG(ERROR) << "Wrong number of arguments: " << argc;
exit(208);
}
- // Close all file descriptors. They are coming from the caller, we do not want to pass them
- // on across our fork/exec into a different domain.
- // 1) Default descriptors.
- CloseDescriptor(STDIN_FILENO);
- CloseDescriptor(STDOUT_FILENO);
- CloseDescriptor(STDERR_FILENO);
- // 2) The status channel.
- CloseDescriptor(arg[1]);
+ const char* status_fd = arg[1];
+ const char* slot_suffix = arg[2];
+
+ // Set O_CLOEXEC on standard fds. They are coming from the caller, we do not
+ // want to pass them on across our fork/exec into a different domain.
+ SetCloseOnExec(STDIN_FILENO);
+ SetCloseOnExec(STDOUT_FILENO);
+ SetCloseOnExec(STDERR_FILENO);
+ // Close the status channel.
+ CloseDescriptor(status_fd);
// We need to run the otapreopt tool from the postinstall partition. As such, set up a
// mount namespace and change root.
@@ -185,20 +206,20 @@ static int otapreopt_chroot(const int argc, char **arg) {
// 2) We're in a mount namespace here, so when we die, this will be cleaned up.
// 3) Ignore errors. Printing anything at this stage will open a file descriptor
// for logging.
- if (!ValidateTargetSlotSuffix(arg[2])) {
- LOG(ERROR) << "Target slot suffix not legal: " << arg[2];
+ if (!ValidateTargetSlotSuffix(slot_suffix)) {
+ LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
exit(207);
}
- TryExtraMount("vendor", arg[2], "/postinstall/vendor");
+ TryExtraMount("vendor", slot_suffix, "/postinstall/vendor");
// Try to mount the product partition. update_engine doesn't do this for us, but we
// want it for product APKs. Same notes as vendor above.
- TryExtraMount("product", arg[2], "/postinstall/product");
+ TryExtraMount("product", slot_suffix, "/postinstall/product");
// Try to mount the system_ext partition. update_engine doesn't do this for
// us, but we want it for system_ext APKs. Same notes as vendor and product
// above.
- TryExtraMount("system_ext", arg[2], "/postinstall/system_ext");
+ TryExtraMount("system_ext", slot_suffix, "/postinstall/system_ext");
constexpr const char* kPostInstallLinkerconfig = "/postinstall/linkerconfig";
// Try to mount /postinstall/linkerconfig. we will set it up after performing the chroot
@@ -329,30 +350,37 @@ static int otapreopt_chroot(const int argc, char **arg) {
exit(218);
}
- // Now go on and run otapreopt.
+ // Now go on and read dexopt lines from stdin and pass them on to otapreopt.
- // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc
- // Outgoing: cmd + target-slot + cmd... | Outgoing | = argc - 1
- std::vector<std::string> cmd;
- cmd.reserve(argc);
- cmd.push_back("/system/bin/otapreopt");
+ int count = 1;
+ for (std::array<char, 1000> linebuf;
+ std::cin.clear(), std::cin.getline(&linebuf[0], linebuf.size()); ++count) {
+ // Subtract one from gcount() since getline() counts the newline.
+ std::string line(&linebuf[0], std::cin.gcount() - 1);
- // The first parameter is the status file descriptor, skip.
- for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
- cmd.push_back(arg[i]);
- }
+ if (std::cin.fail()) {
+ LOG(ERROR) << "Command exceeds max length " << linebuf.size() << " - skipped: " << line;
+ continue;
+ }
- // Fork and execute otapreopt in its own process.
- std::string error_msg;
- bool exec_result = Exec(cmd, &error_msg);
- if (!exec_result) {
- LOG(ERROR) << "Running otapreopt failed: " << error_msg;
- }
+ std::vector<std::string> tokenized_line = android::base::Tokenize(line, " ");
+ std::vector<std::string> cmd{"/system/bin/otapreopt", slot_suffix};
+ std::move(tokenized_line.begin(), tokenized_line.end(), std::back_inserter(cmd));
- if (!exec_result) {
- exit(213);
+ LOG(INFO) << "Command " << count << ": " << android::base::Join(cmd, " ");
+
+ // Fork and execute otapreopt in its own process.
+ std::string error_msg;
+ bool exec_result = Exec(cmd, &error_msg);
+ if (!exec_result) {
+ LOG(ERROR) << "Running otapreopt failed: " << error_msg;
+ }
+
+ // Print the count to stdout and flush to indicate progress.
+ std::cout << count << std::endl;
}
+ LOG(INFO) << "No more dexopt commands";
return 0;
}
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index db5c34edc2..28bd7932a2 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -16,7 +16,9 @@
# limitations under the License.
#
-# This script will run as a postinstall step to drive otapreopt.
+# This script runs as a postinstall step to drive otapreopt. It comes with the
+# OTA package, but runs /system/bin/otapreopt_chroot in the (old) active system
+# image. See system/extras/postinst/postinst.sh for some docs.
TARGET_SLOT="$1"
STATUS_FD="$2"
@@ -31,12 +33,11 @@ BOOT_PROPERTY_NAME="dev.bootcomplete"
BOOT_COMPLETE=$(getprop $BOOT_PROPERTY_NAME)
if [ "$BOOT_COMPLETE" != "1" ] ; then
- echo "Error: boot-complete not detected."
+ echo "$0: Error: boot-complete not detected."
# We must return 0 to not block sideload.
exit 0
fi
-
# Compute target slot suffix.
# TODO: Once bootctl is not restricted, we should query from there. Or get this from
# update_engine as a parameter.
@@ -45,45 +46,63 @@ if [ "$TARGET_SLOT" = "0" ] ; then
elif [ "$TARGET_SLOT" = "1" ] ; then
TARGET_SLOT_SUFFIX="_b"
else
- echo "Unknown target slot $TARGET_SLOT"
+ echo "$0: Unknown target slot $TARGET_SLOT"
exit 1
fi
+if [ "$(/system/bin/otapreopt_chroot --version)" != 2 ]; then
+ # We require an updated chroot wrapper that reads dexopt commands from stdin.
+ # Even if we kept compat with the old binary, the OTA preopt wouldn't work due
+ # to missing sepolicy rules, so there's no use spending time trying to dexopt
+ # (b/291974157).
+ echo "$0: Current system image is too old to work with OTA preopt - skipping."
+ exit 0
+fi
PREPARE=$(cmd otadexopt prepare)
# Note: Ignore preparation failures. Step and done will fail and exit this.
# This is necessary to support suspends - the OTA service will keep
# the state around for us.
-PROGRESS=$(cmd otadexopt progress)
-print -u${STATUS_FD} "global_progress $PROGRESS"
-
-i=0
-while ((i<MAXIMUM_PACKAGES)) ; do
+# Create an array with all dexopt commands in advance, to know how many there are.
+otadexopt_cmds=()
+while (( ${#otadexopt_cmds[@]} < MAXIMUM_PACKAGES )) ; do
DONE=$(cmd otadexopt done)
if [ "$DONE" = "OTA complete." ] ; then
break
fi
+ otadexopt_cmds+=("$(cmd otadexopt next)")
+done
- DEXOPT_PARAMS=$(cmd otadexopt next)
+DONE=$(cmd otadexopt done)
+cmd otadexopt cleanup
- /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
+echo "$0: Using streaming otapreopt_chroot on ${#otadexopt_cmds[@]} packages"
- PROGRESS=$(cmd otadexopt progress)
- print -u${STATUS_FD} "global_progress $PROGRESS"
+function print_otadexopt_cmds {
+ for cmd in "${otadexopt_cmds[@]}" ; do
+ print "$cmd"
+ done
+}
- sleep 1
- i=$((i+1))
-done
+function report_progress {
+ while read count ; do
+ # mksh can't do floating point arithmetic, so emulate a fixed point calculation.
+ (( permilles = 1000 * count / ${#otadexopt_cmds[@]} ))
+ printf 'global_progress %d.%03d\n' $((permilles / 1000)) $((permilles % 1000)) >&${STATUS_FD}
+ done
+}
+
+print_otadexopt_cmds | \
+ /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX | \
+ report_progress
-DONE=$(cmd otadexopt done)
if [ "$DONE" = "OTA incomplete." ] ; then
- echo "Incomplete."
+ echo "$0: Incomplete."
else
- echo "Complete or error."
+ echo "$0: Complete or error."
fi
print -u${STATUS_FD} "global_progress 1.0"
-cmd otadexopt cleanup
exit 0
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 4221a3a593..7648265f0f 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -208,36 +208,13 @@ void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
}
// Compute compiler filter.
- {
- std::string dex2oat_compiler_filter_arg;
- {
- // If we are booting without the real /data, don't spend time compiling.
- std::string vold_decrypt = GetProperty("vold.decrypt", "");
- bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
- vold_decrypt == "1";
-
- bool have_dex2oat_relocation_skip_flag = false;
- if (skip_compilation) {
- dex2oat_compiler_filter_arg = "--compiler-filter=extract";
- have_dex2oat_relocation_skip_flag = true;
- } else if (compiler_filter != nullptr) {
- dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s",
- compiler_filter);
- }
- if (have_dex2oat_relocation_skip_flag) {
- AddRuntimeArg("-Xnorelocate");
- }
- }
-
- if (dex2oat_compiler_filter_arg.empty()) {
- dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
- "--compiler-filter=%s");
- }
- AddArg(dex2oat_compiler_filter_arg);
-
- if (compilation_reason != nullptr) {
- AddArg(std::string("--compilation-reason=") + compilation_reason);
- }
+ if (compiler_filter != nullptr) {
+ AddArg(StringPrintf("--compiler-filter=%s", compiler_filter));
+ } else {
+ AddArg(MapPropertyToArg("dalvik.vm.dex2oat-filter", "--compiler-filter=%s"));
+ }
+ if (compilation_reason != nullptr) {
+ AddArg(std::string("--compilation-reason=") + compilation_reason);
}
AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 304ba7b04f..56f84a5e12 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -441,24 +441,6 @@ TEST_F(RunDex2OatTest, Runtime) {
VerifyExpectedFlags();
}
-TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) {
- setSystemProperty("vold.decrypt", "trigger_restart_min_framework");
- CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
- SetExpectedFlagUsed("--compiler-filter", "=extract");
- SetExpectedFlagUsed("-Xnorelocate", "");
- VerifyExpectedFlags();
-}
-
-TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) {
- setSystemProperty("vold.decrypt", "1");
- CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
- SetExpectedFlagUsed("--compiler-filter", "=extract");
- SetExpectedFlagUsed("-Xnorelocate", "");
- VerifyExpectedFlags();
-}
-
TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) {
setSystemProperty("dalvik.vm.dex2oat-filter", "speed");
auto args = RunDex2OatArgs::MakeDefaultTestArgs();
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 858a92cc65..4bc92afa18 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -42,9 +42,12 @@
#include "binder_test_utils.h"
#include "dexopt.h"
#include "globals.h"
+#include "unique_file.h"
#include "utils.h"
using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::os::ParcelFileDescriptor;
using std::filesystem::is_empty;
namespace android {
@@ -136,6 +139,16 @@ static int create(const std::string& path, uid_t owner, gid_t group, mode_t mode
return fd;
}
+static void create_with_content(const std::string& path, uid_t owner, gid_t group, mode_t mode,
+ const std::string& content) {
+ int fd = ::open(path.c_str(), O_RDWR | O_CREAT, mode);
+ EXPECT_NE(fd, -1);
+ EXPECT_TRUE(android::base::WriteStringToFd(content, fd));
+ EXPECT_EQ(::fchown(fd, owner, group), 0);
+ EXPECT_EQ(::fchmod(fd, mode), 0);
+ close(fd);
+}
+
static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0);
}
@@ -527,6 +540,94 @@ TEST_F(ServiceTest, GetAppSizeWrongSizes) {
externalStorageAppId, ceDataInodes, codePaths,
&externalStorageSize));
}
+
+class FsverityTest : public ServiceTest {
+protected:
+ binder::Status createFsveritySetupAuthToken(const std::string& path, int open_mode,
+ sp<IFsveritySetupAuthToken>* _aidl_return) {
+ unique_fd ufd(open(path.c_str(), open_mode));
+ EXPECT_GE(ufd.get(), 0) << "open failed: " << strerror(errno);
+ ParcelFileDescriptor rfd(std::move(ufd));
+ return service->createFsveritySetupAuthToken(std::move(rfd), kTestAppId, kTestUserId,
+ _aidl_return);
+ }
+};
+
+TEST_F(FsverityTest, enableFsverity) {
+ const std::string path = kTestPath + "/foo";
+ create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Expect to fs-verity setup to succeed
+ sp<IFsveritySetupAuthToken> authToken;
+ binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_TRUE(authToken != nullptr);
+
+ // Verity auth token works to enable fs-verity
+ int32_t errno_local;
+ status = service->enableFsverity(authToken, path, "fake.package.name", &errno_local);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(errno_local, 0);
+}
+
+TEST_F(FsverityTest, enableFsverity_nullAuthToken) {
+ const std::string path = kTestPath + "/foo";
+ create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Verity null auth token fails
+ sp<IFsveritySetupAuthToken> authToken;
+ int32_t errno_local;
+ binder::Status status =
+ service->enableFsverity(authToken, path, "fake.package.name", &errno_local);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(FsverityTest, enableFsverity_differentFile) {
+ const std::string path = kTestPath + "/foo";
+ create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Expect to fs-verity setup to succeed
+ sp<IFsveritySetupAuthToken> authToken;
+ binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_TRUE(authToken != nullptr);
+
+ // Verity auth token does not work for a different file
+ const std::string anotherPath = kTestPath + "/bar";
+ ASSERT_TRUE(android::base::WriteStringToFile("content", anotherPath));
+ UniqueFile raii2(/*fd=*/-1, anotherPath, [](const std::string& path) { unlink(path.c_str()); });
+ int32_t errno_local;
+ status = service->enableFsverity(authToken, anotherPath, "fake.package.name", &errno_local);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_NE(errno_local, 0);
+}
+
+TEST_F(FsverityTest, createFsveritySetupAuthToken_ReadonlyFdDoesNotAuthenticate) {
+ const std::string path = kTestPath + "/foo";
+ create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Expect the fs-verity setup to fail
+ sp<IFsveritySetupAuthToken> authToken;
+ binder::Status status = createFsveritySetupAuthToken(path, O_RDONLY, &authToken);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(FsverityTest, createFsveritySetupAuthToken_UnownedFile) {
+ const std::string path = kTestPath + "/foo";
+ // Simulate world-writable file owned by another app
+ create_with_content(path, kTestAppUid + 1, kTestAppUid + 1, 0666, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Expect the fs-verity setup to fail
+ sp<IFsveritySetupAuthToken> authToken;
+ binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken);
+ EXPECT_FALSE(status.isOk());
+}
+
static bool mkdirs(const std::string& path, mode_t mode) {
struct stat sb;
if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index ecea1d2b1c..c43fdbd547 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -18,6 +18,7 @@
#ifndef UTILS_H_
#define UTILS_H_
+#include <functional>
#include <string>
#include <vector>
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index fb69513d24..d73a30bf9b 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -93,22 +93,9 @@ cc_fuzz {
libfuzzer_options: [
"max_len=50000",
],
- },
-}
-
-// Adding this new fuzzer to test the corpus generated by record_binder
-cc_fuzz {
- name: "servicemanager_test_fuzzer",
- defaults: [
- "servicemanager_defaults",
- "service_fuzzer_defaults",
- ],
- host_supported: true,
- srcs: ["fuzzers/ServiceManagerTestFuzzer.cpp"],
- fuzz_config: {
- libfuzzer_options: [
- "max_len=50000",
+ cc: [
+ "smoreland@google.com",
+ "waghpawan@google.com",
],
},
- corpus: ["fuzzers/servicemamanager_fuzzer_corpus/*"],
}
diff --git a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
deleted file mode 100644
index e19b6eb279..0000000000
--- a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fuzzbinder/libbinder_driver.h>
-#include <utils/StrongPointer.h>
-
-#include "Access.h"
-#include "ServiceManager.h"
-
-using ::android::Access;
-using ::android::Parcel;
-using ::android::ServiceManager;
-using ::android::sp;
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider provider(data, size);
- auto accessPtr = std::make_unique<Access>();
- auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr));
-
- // Reserved bytes
- provider.ConsumeBytes<uint8_t>(8);
- uint32_t code = provider.ConsumeIntegral<uint32_t>();
- uint32_t flag = provider.ConsumeIntegral<uint32_t>();
- std::vector<uint8_t> parcelData = provider.ConsumeRemainingBytes<uint8_t>();
-
- Parcel inputParcel;
- inputParcel.setData(parcelData.data(), parcelData.size());
-
- Parcel reply;
- serviceManager->transact(code, inputParcel, &reply, flag);
-
- serviceManager->clear();
-
- return 0;
-}
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
deleted file mode 100644
index 07319f864e..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
deleted file mode 100644
index 07319f864e..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
deleted file mode 100644
index 07319f864e..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
deleted file mode 100644
index 07319f864e..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
deleted file mode 100644
index 88ad474f09..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
deleted file mode 100644
index fae15a2fea..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
deleted file mode 100644
index e69ab49d5d..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
deleted file mode 100644
index 88ad474f09..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
deleted file mode 100644
index fae15a2fea..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
deleted file mode 100644
index 88ad474f09..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
deleted file mode 100644
index fae15a2fea..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
deleted file mode 100644
index 88ad474f09..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
deleted file mode 100644
index fae15a2fea..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
deleted file mode 100644
index 88ad474f09..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
deleted file mode 100644
index fae15a2fea..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
deleted file mode 100644
index 88ad474f09..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
deleted file mode 100644
index fae15a2fea..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
deleted file mode 100644
index 88ad474f09..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
deleted file mode 100644
index fae15a2fea..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
deleted file mode 100644
index b326907a58..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
deleted file mode 100644
index 05b27bf413..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
deleted file mode 100644
index b326907a58..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
deleted file mode 100644
index cdaa1f01b1..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
deleted file mode 100644
index ff0941b7a6..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
deleted file mode 100644
index cdaa1f01b1..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
deleted file mode 100644
index 7e5f948682..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
deleted file mode 100644
index 07319f864e..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
deleted file mode 100644
index 07319f864e..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
+++ /dev/null
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
deleted file mode 100644
index 39e5104927..0000000000
--- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
+++ /dev/null
Binary files differ
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 2143d93632..c962c15a24 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -263,8 +263,8 @@ prebuilt_etc {
}
prebuilt_etc {
- name: "android.hardware.threadnetwork.prebuilt.xml",
- src: "android.hardware.threadnetwork.xml",
+ name: "android.hardware.thread_network.prebuilt.xml",
+ src: "android.hardware.thread_network.xml",
defaults: ["frameworks_native_data_etc_defaults"],
}
@@ -341,6 +341,12 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "android.software.opengles.deqp.level-latest.prebuilt.xml",
+ src: "android.software.opengles.deqp.level-latest.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.software.sip.voip.prebuilt.xml",
src: "android.software.sip.voip.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -371,6 +377,12 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "android.software.vulkan.deqp.level-latest.prebuilt.xml",
+ src: "android.software.vulkan.deqp.level-latest.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "aosp_excluded_hardware.prebuilt.xml",
src: "aosp_excluded_hardware.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.threadnetwork.xml b/data/etc/android.hardware.thread_network.xml
index 9cbdc905fb..b116ed6bf8 100644
--- a/data/etc/android.hardware.threadnetwork.xml
+++ b/data/etc/android.hardware.thread_network.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<!-- Adds the feature indicating support for the ThreadNetwork API -->
+<!-- Adds the feature indicating support for the Thread networking protocol -->
<permissions>
- <feature name="android.hardware.threadnetwork" />
+ <feature name="android.hardware.thread_network" />
</permissions>
diff --git a/data/etc/android.software.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml
new file mode 100644
index 0000000000..bd15eb6eb2
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-latest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+ dEQP tests associated with the most recent level for this Android version. -->
+<permissions>
+ <feature name="android.software.opengles.deqp.level" version="132580097" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml
new file mode 100644
index 0000000000..87be0709d6
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-latest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan
+ dEQP tests associated with the most recent level for this Android version. -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132580097" />
+</permissions>
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
index 03dfd63cbd..39772aece2 100644
--- a/data/etc/input/motion_predictor_config.xml
+++ b/data/etc/input/motion_predictor_config.xml
@@ -16,5 +16,20 @@
<motion-predictor>
<!-- The time interval (ns) between the model's predictions. -->
<prediction-interval>4166666</prediction-interval> <!-- 4.167 ms = ~240 Hz -->
+ <!-- The noise floor (px) for predicted distances.
+
+ As the model is trained stochastically, there is some expected minimum
+ variability in its output. This can be a UX issue when the input device
+ is moving slowly and the variability is large relative to the magnitude
+ of the motion. In these cases, it is better to inhibit the prediction,
+ rather than show noisy predictions (and there is little benefit to
+ prediction anyway).
+
+ The value for this parameter should at least be close to the maximum
+ predicted distance when the input device is held stationary (i.e. the
+ expected minimum variability), and perhaps a little larger to capture
+ the UX issue mentioned above.
+ -->
+ <distance-noise-floor>0.2</distance-noise-floor>
</motion-predictor>
diff --git a/data/etc/input/motion_predictor_model.tflite b/data/etc/input/motion_predictor_model.tflite
index 10b3c8b114..45fc162cd1 100644
--- a/data/etc/input/motion_predictor_model.tflite
+++ b/data/etc/input/motion_predictor_model.tflite
Binary files differ
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index b494f897d5..ba8b02d597 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -14,6 +14,23 @@
* limitations under the License.
*/
+ /**
+ * @defgroup APerformanceHint Performance Hint Manager
+ *
+ * APerformanceHint allows apps to create performance hint sessions for groups
+ * of threads, and provide hints to the system about the workload of those threads,
+ * to help the system more accurately allocate power for them. It is the NDK
+ * counterpart to the Java PerformanceHintManager SDK API.
+ *
+ * @{
+ */
+
+/**
+ * @file performance_hint.h
+ * @brief API for creating and managing a hint session.
+ */
+
+
#ifndef ANDROID_NATIVE_PERFORMANCE_HINT_H
#define ANDROID_NATIVE_PERFORMANCE_HINT_H
@@ -48,7 +65,7 @@ struct APerformanceHintSession;
* An opaque type representing a handle to a performance hint manager.
* It must be released after use.
*
- * <p>To use:<ul>
+ * To use:<ul>
* <li>Obtain the performance hint manager instance by calling
* {@link APerformanceHint_getManager} function.</li>
* <li>Create an {@link APerformanceHintSession} with
@@ -61,50 +78,43 @@ typedef struct APerformanceHintManager APerformanceHintManager;
/**
* An opaque type representing a handle to a performance hint session.
* A session can only be acquired from a {@link APerformanceHintManager}
- * with {@link APerformanceHint_getPreferredUpdateRateNanos}. It must be
+ * with {@link APerformanceHint_createSession}. It must be
* freed with {@link APerformanceHint_closeSession} after use.
*
* A Session represents a group of threads with an inter-related workload such that hints for
* their performance should be considered as a unit. The threads in a given session should be
- * long-life and not created or destroyed dynamically.
- *
- * <p>Each session is expected to have a periodic workload with a target duration for each
- * cycle. The cycle duration is likely greater than the target work duration to allow other
- * parts of the pipeline to run within the available budget. For example, a renderer thread may
- * work at 60hz in order to produce frames at the display's frame but have a target work
- * duration of only 6ms.</p>
- *
- * <p>After each cycle of work, the client is expected to use
- * {@link APerformanceHint_reportActualWorkDuration} to report the actual time taken to
- * complete.</p>
- *
- * <p>To use:<ul>
- * <li>Update a sessions target duration for each cycle of work
- * with {@link APerformanceHint_updateTargetWorkDuration}.</li>
- * <li>Report the actual duration for the last cycle of work with
- * {@link APerformanceHint_reportActualWorkDuration}.</li>
- * <li>Release the session instance with
- * {@link APerformanceHint_closeSession}.</li></ul></p>
+ * long-lived and not created or destroyed dynamically.
+ *
+ * The work duration API can be used with periodic workloads to dynamically adjust thread
+ * performance and keep the work on schedule while optimizing the available power budget.
+ * When using the work duration API, the starting target duration should be specified
+ * while creating the session, and can later be adjusted with
+ * {@link APerformanceHint_updateTargetWorkDuration}. While using the work duration
+ * API, the client is expected to call {@link APerformanceHint_reportActualWorkDuration} each
+ * cycle to report the actual time taken to complete to the system.
+ *
+ * All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
*/
typedef struct APerformanceHintSession APerformanceHintSession;
/**
* Acquire an instance of the performance hint manager.
*
- * @return manager instance on success, nullptr on failure.
+ * @return APerformanceHintManager instance on success, nullptr on failure.
*/
APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__);
/**
* Creates a session for the given set of threads and sets their initial target work
* duration.
+ *
* @param manager The performance hint manager instance.
* @param threadIds The list of threads to be associated with this session. They must be part of
- * this app's thread group.
- * @param size the size of threadIds.
- * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new session.
- * This must be positive.
- * @return manager instance on success, nullptr on failure.
+ * this process' thread group.
+ * @param size The size of the list of threadIds.
+ * @param initialTargetWorkDurationNanos The target duration in nanoseconds for the new session.
+ * This must be positive if using the work duration API, or 0 otherwise.
+ * @return APerformanceHintManager instance on success, nullptr on failure.
*/
APerformanceHintSession* APerformanceHint_createSession(
APerformanceHintManager* manager,
@@ -124,8 +134,8 @@ int64_t APerformanceHint_getPreferredUpdateRateNanos(
* Updates this session's target duration for each cycle of work.
*
* @param session The performance hint session instance to update.
- * @param targetDurationNanos the new desired duration in nanoseconds. This must be positive.
- * @return 0 on success
+ * @param targetDurationNanos The new desired duration in nanoseconds. This must be positive.
+ * @return 0 on success.
* EINVAL if targetDurationNanos is not positive.
* EPIPE if communication with the system service has failed.
*/
@@ -136,14 +146,13 @@ int APerformanceHint_updateTargetWorkDuration(
/**
* Reports the actual duration for the last cycle of work.
*
- * <p>The system will attempt to adjust the core placement of the threads within the thread
- * group and/or the frequency of the core on which they are run to bring the actual duration
- * close to the target duration.</p>
+ * The system will attempt to adjust the scheduling and performance of the
+ * threads within the thread group to bring the actual duration close to the target duration.
*
* @param session The performance hint session instance to update.
- * @param actualDurationNanos how long the thread group took to complete its last task in
- * nanoseconds. This must be positive.
- * @return 0 on success
+ * @param actualDurationNanos The duration of time the thread group took to complete its last
+ * task in nanoseconds. This must be positive.
+ * @return 0 on success.
* EINVAL if actualDurationNanos is not positive.
* EPIPE if communication with the system service has failed.
*/
@@ -164,19 +173,36 @@ void APerformanceHint_closeSession(
* Set a list of threads to the performance hint session. This operation will replace
* the current list of threads with the given list of threads.
*
- * @param session The performance hint session instance for the threads.
+ * @param session The performance hint session instance to update.
* @param threadIds The list of threads to be associated with this session. They must be part of
* this app's thread group.
- * @param size the size of the list of threadIds.
+ * @param size The size of the list of threadIds.
* @return 0 on success.
- * EINVAL if the list of thread ids is empty or if any of the thread ids is not part of the thread group.
+ * EINVAL if the list of thread ids is empty or if any of the thread ids are not part of
+ the thread group.
* EPIPE if communication with the system service has failed.
+ * EPERM if any thread id doesn't belong to the application.
*/
int APerformanceHint_setThreads(
APerformanceHintSession* session,
const pid_t* threadIds,
size_t size) __INTRODUCED_IN(__ANDROID_API_U__);
+/**
+ * This tells the session that these threads can be
+ * safely scheduled to prefer power efficiency over performance.
+ *
+ * @param session The performance hint session instance to update.
+ * @param enabled The flag which sets whether this session will use power-efficient scheduling.
+ * @return 0 on success.
+ * EPIPE if communication with the system service has failed.
+ */
+int APerformanceHint_setPreferPowerEfficiency(
+ APerformanceHintSession* session,
+ bool enabled) __INTRODUCED_IN(__ANDROID_API_V__);
+
__END_DECLS
#endif // ANDROID_NATIVE_PERFORMANCE_HINT_H
+
+/** @} */ \ No newline at end of file
diff --git a/include/input/Input.h b/include/input/Input.h
index a271f0c1f9..64ee47342d 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -551,7 +551,7 @@ class KeyEvent : public InputEvent {
public:
virtual ~KeyEvent() { }
- virtual InputEventType getType() const { return InputEventType::KEY; }
+ InputEventType getType() const override { return InputEventType::KEY; }
inline int32_t getAction() const { return mAction; }
@@ -602,7 +602,7 @@ class MotionEvent : public InputEvent {
public:
virtual ~MotionEvent() { }
- virtual InputEventType getType() const { return InputEventType::MOTION; }
+ InputEventType getType() const override { return InputEventType::MOTION; }
inline int32_t getAction() const { return mAction; }
@@ -930,7 +930,7 @@ class FocusEvent : public InputEvent {
public:
virtual ~FocusEvent() {}
- virtual InputEventType getType() const override { return InputEventType::FOCUS; }
+ InputEventType getType() const override { return InputEventType::FOCUS; }
inline bool getHasFocus() const { return mHasFocus; }
@@ -949,7 +949,7 @@ class CaptureEvent : public InputEvent {
public:
virtual ~CaptureEvent() {}
- virtual InputEventType getType() const override { return InputEventType::CAPTURE; }
+ InputEventType getType() const override { return InputEventType::CAPTURE; }
inline bool getPointerCaptureEnabled() const { return mPointerCaptureEnabled; }
@@ -968,7 +968,7 @@ class DragEvent : public InputEvent {
public:
virtual ~DragEvent() {}
- virtual InputEventType getType() const override { return InputEventType::DRAG; }
+ InputEventType getType() const override { return InputEventType::DRAG; }
inline bool isExiting() const { return mIsExiting; }
@@ -992,7 +992,7 @@ class TouchModeEvent : public InputEvent {
public:
virtual ~TouchModeEvent() {}
- virtual InputEventType getType() const override { return InputEventType::TOUCH_MODE; }
+ InputEventType getType() const override { return InputEventType::TOUCH_MODE; }
inline bool isInTouchMode() const { return mIsInTouchMode; }
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 909bf087dd..44247c1249 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -69,6 +69,12 @@ public:
static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value);
+ static std::optional<int> getLinuxEvdevEventTypeByLabel(const char* label);
+
+ static std::optional<int> getLinuxEvdevEventCodeByLabel(int32_t type, const char* label);
+
+ static std::optional<int> getLinuxEvdevInputPropByLabel(const char* label);
+
private:
InputEventLookup();
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index fbd60261b2..2edc138f67 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -99,6 +99,14 @@ private:
// A TFLite model for generating motion predictions.
class TfLiteMotionPredictorModel {
public:
+ struct Config {
+ // The time between predictions.
+ nsecs_t predictionInterval = 0;
+ // The noise floor for predictions.
+ // Distances (r) less than this should be discarded as noise.
+ float distanceNoiseFloor = 0;
+ };
+
// Creates a model from an encoded Flatbuffer model.
static std::unique_ptr<TfLiteMotionPredictorModel> create();
@@ -110,8 +118,7 @@ public:
// Returns the length of the model's output buffers.
size_t outputLength() const;
- // Returns the time interval between predictions.
- nsecs_t predictionInterval() const { return mPredictionInterval; }
+ const Config& config() const { return mConfig; }
// Executes the model.
// Returns true if the model successfully executed and the output tensors can be read.
@@ -132,7 +139,7 @@ public:
private:
explicit TfLiteMotionPredictorModel(std::unique_ptr<android::base::MappedFile> model,
- nsecs_t predictionInterval);
+ Config config);
void allocateTensors();
void attachInputTensors();
@@ -154,7 +161,7 @@ private:
std::unique_ptr<tflite::Interpreter> mInterpreter;
tflite::SignatureRunner* mRunner = nullptr;
- const nsecs_t mPredictionInterval = 0;
+ const Config mConfig = {};
};
} // namespace android
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index b58feac444..2e9949578d 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/os/IInputConstants.h>
#include <input/Input.h>
#include <input/RingBuffer.h>
#include <utils/BitSet.h>
@@ -35,19 +36,20 @@ public:
static const size_t MAX_DEGREE = 4;
enum class Strategy : int32_t {
- DEFAULT = -1,
- MIN = 0,
- IMPULSE = 0,
- LSQ1 = 1,
- LSQ2 = 2,
- LSQ3 = 3,
- WLSQ2_DELTA = 4,
- WLSQ2_CENTRAL = 5,
- WLSQ2_RECENT = 6,
- INT1 = 7,
- INT2 = 8,
- LEGACY = 9,
+ DEFAULT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_DEFAULT,
+ IMPULSE = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_IMPULSE,
+ LSQ1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ1,
+ LSQ2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ2,
+ LSQ3 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ3,
+ WLSQ2_DELTA = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA,
+ WLSQ2_CENTRAL = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL,
+ WLSQ2_RECENT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT,
+ INT1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT1,
+ INT2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT2,
+ LEGACY = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LEGACY,
+ MIN = IMPULSE,
MAX = LEGACY,
+ ftl_last = LEGACY,
};
/*
@@ -81,8 +83,6 @@ public:
// TODO(b/32830165): support axis-specific strategies.
VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
- ~VelocityTracker();
-
/** Return true if the axis is supported for velocity tracking, false otherwise. */
static bool isAxisSupported(int32_t axis);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 3f1fc33c90..6c2b313f8a 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -190,6 +190,9 @@ cc_defaults {
"-performance-move-const-arg", // b/273486801
"portability*",
],
+ lto: {
+ thin: true,
+ },
}
cc_library_headers {
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index f2b4a6ef37..7d6ae00ed2 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -193,8 +193,7 @@ public:
status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply,
IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
- ALOGD("FGS Logger Transaction failed");
- ALOGD("%d", err);
+ ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err);
return err;
}
return NO_ERROR;
@@ -209,8 +208,7 @@ public:
status_t err =
remote()->transact(LOG_FGS_API_END_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
- ALOGD("FGS Logger Transaction failed");
- ALOGD("%d", err);
+ ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err);
return err;
}
return NO_ERROR;
@@ -224,11 +222,10 @@ public:
data.writeInt32(state);
data.writeInt32(appUid);
data.writeInt32(appPid);
- status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply,
+ status_t err = remote()->transact(LOG_FGS_API_STATE_CHANGED_TRANSACTION, data, &reply,
IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
- ALOGD("FGS Logger Transaction failed");
- ALOGD("%d", err);
+ ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err);
return err;
}
return NO_ERROR;
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 3da06ba4db..fc273e0367 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -73,8 +73,8 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
ALOGV("MemoryHeapBase: Attempting to force MemFD");
fd = memfd_create_region(name ? name : "MemoryHeapBase", size);
if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
- const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) |
- ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
+ const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) | F_SEAL_GROW |
+ F_SEAL_SHRINK | ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) {
ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error %s", name,
SEAL_FLAGS, strerror(errno));
diff --git a/libs/binder/ndk/.clang-format b/libs/binder/ndk/.clang-format
index 9a9d936f15..60774143ee 100644
--- a/libs/binder/ndk/.clang-format
+++ b/libs/binder/ndk/.clang-format
@@ -2,9 +2,7 @@ BasedOnStyle: Google
ColumnLimit: 100
IndentWidth: 4
ContinuationIndentWidth: 8
-PointerAlignment: Left
TabWidth: 4
AllowShortFunctionsOnASingleLine: Inline
PointerAlignment: Left
-TabWidth: 4
UseTab: Never
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index d6937c2c52..ed53891e3d 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -115,17 +115,29 @@ class SpAIBinder {
*/
AIBinder** getR() { return &mBinder; }
- bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); }
- bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); }
- bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); }
- bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); }
- bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); }
- bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); }
-
private:
AIBinder* mBinder = nullptr;
};
+#define SP_AIBINDER_COMPARE(_op_) \
+ static inline bool operator _op_(const SpAIBinder& lhs, const SpAIBinder& rhs) { \
+ return lhs.get() _op_ rhs.get(); \
+ } \
+ static inline bool operator _op_(const SpAIBinder& lhs, const AIBinder* rhs) { \
+ return lhs.get() _op_ rhs; \
+ } \
+ static inline bool operator _op_(const AIBinder* lhs, const SpAIBinder& rhs) { \
+ return lhs _op_ rhs.get(); \
+ }
+
+SP_AIBINDER_COMPARE(!=)
+SP_AIBINDER_COMPARE(<)
+SP_AIBINDER_COMPARE(<=)
+SP_AIBINDER_COMPARE(==)
+SP_AIBINDER_COMPARE(>)
+SP_AIBINDER_COMPARE(>=)
+#undef SP_AIBINDER_COMPARE
+
namespace impl {
/**
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 27ce615565..25b8e975b3 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -377,18 +377,24 @@ TEST(NdkBinder, CantHaveTwoLocalBinderClassesWithSameDescriptor) {
}
TEST(NdkBinder, GetTestServiceStressTest) {
- // libbinder has some complicated logic to make sure only one instance of
- // ABpBinder is associated with each binder.
-
constexpr size_t kNumThreads = 10;
constexpr size_t kNumCalls = 1000;
std::vector<std::thread> threads;
+ // this is not a lazy service, but we must make sure that it's started before calling
+ // checkService on it, since the other process serving it might not be started yet.
+ {
+ // getService, not waitForService, to take advantage of timeout
+ auto binder = ndk::SpAIBinder(AServiceManager_getService(IFoo::kSomeInstanceName));
+ ASSERT_NE(nullptr, binder.get());
+ }
+
for (size_t i = 0; i < kNumThreads; i++) {
threads.push_back(std::thread([&]() {
for (size_t j = 0; j < kNumCalls; j++) {
auto binder =
ndk::SpAIBinder(AServiceManager_checkService(IFoo::kSomeInstanceName));
+ ASSERT_NE(nullptr, binder.get());
EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
}
}));
@@ -755,9 +761,9 @@ TEST(NdkBinder, ConvertToPlatformBinder) {
// local
ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
// convert to platform binder
- EXPECT_NE(binder.get(), nullptr);
+ EXPECT_NE(binder, nullptr);
sp<IBinder> platformBinder = AIBinder_toPlatformBinder(binder.get());
- EXPECT_NE(platformBinder.get(), nullptr);
+ EXPECT_NE(platformBinder, nullptr);
auto proxy = interface_cast<IBinderNdkUnitTest>(platformBinder);
EXPECT_NE(proxy, nullptr);
@@ -768,7 +774,7 @@ TEST(NdkBinder, ConvertToPlatformBinder) {
// convert back
ndk::SpAIBinder backBinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(platformBinder));
- EXPECT_EQ(backBinder.get(), binder.get());
+ EXPECT_EQ(backBinder, binder);
}
}
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index d36ebac109..57a38dc480 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -11,9 +11,6 @@ rust_library {
name: "libbinder_rs",
crate_name: "binder",
srcs: ["src/lib.rs"],
- shared_libs: [
- "libutils",
- ],
rustlibs: [
"libbinder_ndk_sys",
"libdowncast_rs",
@@ -97,34 +94,12 @@ rust_bindgen {
crate_name: "binder_ndk_bindgen",
wrapper_src: "sys/BinderBindings.hpp",
source_stem: "bindings",
- bindgen_flags: [
+ bindgen_flag_files: [
// Unfortunately the only way to specify the rust_non_exhaustive enum
// style for a type is to make it the default
- "--default-enum-style",
- "rust_non_exhaustive",
// and then specify constified enums for the enums we don't want
// rustified
- "--constified-enum",
- "android::c_interface::consts::.*",
-
- "--allowlist-type",
- "android::c_interface::.*",
- "--allowlist-type",
- "AStatus",
- "--allowlist-type",
- "AIBinder_Class",
- "--allowlist-type",
- "AIBinder",
- "--allowlist-type",
- "AIBinder_Weak",
- "--allowlist-type",
- "AIBinder_DeathRecipient",
- "--allowlist-type",
- "AParcel",
- "--allowlist-type",
- "binder_status_t",
- "--allowlist-function",
- ".*",
+ "libbinder_ndk_bindgen_flags.txt",
],
shared_libs: [
"libbinder_ndk",
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 2d2bf7c582..1dc0b2471d 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -103,7 +103,12 @@ impl BinderAsyncPool for Tokio {
//
// This shouldn't cause issues with blocking the thread as only one task will run in a
// call to `block_on`, so there aren't other tasks to block.
- let result = spawn_me();
+ //
+ // If the `block_in_place` call fails, then you are driving a current-thread runtime on
+ // the binder threadpool. Instead, it is recommended to use `TokioRuntime<Handle>` when
+ // the runtime is a current-thread runtime, as the current-thread runtime can be driven
+ // only by `Runtime::block_on` calls and not by `Handle::block_on`.
+ let result = tokio::task::block_in_place(spawn_me);
Box::pin(after_spawn(result))
} else {
let handle = tokio::task::spawn_blocking(spawn_me);
diff --git a/libs/binder/rust/libbinder_ndk_bindgen_flags.txt b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
new file mode 100644
index 0000000000..551c59f671
--- /dev/null
+++ b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
@@ -0,0 +1,11 @@
+--default-enum-style=rust_non_exhaustive
+--constified-enum=android::c_interface::consts::.*
+--allowlist-type=android::c_interface::.*
+--allowlist-type=AStatus
+--allowlist-type=AIBinder_Class
+--allowlist-type=AIBinder
+--allowlist-type=AIBinder_Weak
+--allowlist-type=AIBinder_DeathRecipient
+--allowlist-type=AParcel
+--allowlist-type=binder_status_t
+--allowlist-function=.*
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 0bd0781cf4..463c210316 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1023,17 +1023,7 @@ macro_rules! declare_binder_interface {
}
if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) {
- let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> =
- std::convert::TryFrom::try_from(ibinder.clone());
- if let Ok(service) = service {
- // We were able to associate with our expected class and
- // the service is local.
- todo!()
- //return Ok($crate::Strong::new(Box::new(service)));
- } else {
- // Service is remote
- return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
- }
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?)));
}
Err($crate::StatusCode::BAD_TYPE.into())
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index 8d9ce0e731..eb04cc3153 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -370,6 +370,94 @@ unsafe impl AsNative<sys::AStatus> for Status {
}
}
+/// A conversion from `std::result::Result<T, E>` to `binder::Result<T>`. If this type is `Ok(T)`,
+/// it's returned as is. If this type is `Err(E)`, `E` is converted into `Status` which can be
+/// either a general binder exception, or a service-specific exception.
+///
+/// # Examples
+///
+/// ```
+/// // std::io::Error is formatted as the exception's message
+/// fn file_exists(name: &str) -> binder::Result<bool> {
+/// std::fs::metadata(name)
+/// .or_service_specific_exception(NOT_FOUND)?
+/// }
+///
+/// // A custom function is used to create the exception's message
+/// fn file_exists(name: &str) -> binder::Result<bool> {
+/// std::fs::metadata(name)
+/// .or_service_specific_exception_with(NOT_FOUND,
+/// |e| format!("file {} not found: {:?}", name, e))?
+/// }
+///
+/// // anyhow::Error is formatted as the exception's message
+/// use anyhow::{Context, Result};
+/// fn file_exists(name: &str) -> binder::Result<bool> {
+/// std::fs::metadata(name)
+/// .context("file {} not found")
+/// .or_service_specific_exception(NOT_FOUND)?
+/// }
+///
+/// // General binder exceptions can be created similarly
+/// fn file_exists(name: &str) -> binder::Result<bool> {
+/// std::fs::metadata(name)
+/// .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
+/// }
+/// ```
+pub trait IntoBinderResult<T, E> {
+ /// Converts the embedded error into a general binder exception of code `exception`. The
+ /// message of the exception is set by formatting the error for debugging.
+ fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status>;
+
+ /// Converts the embedded error into a general binder exception of code `exception`. The
+ /// message of the exception is set by lazily evaluating the `op` function.
+ fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
+ self,
+ exception: ExceptionCode,
+ op: O,
+ ) -> result::Result<T, Status>;
+
+ /// Converts the embedded error into a service-specific binder exception. `error_code` is used
+ /// to distinguish different service-specific binder exceptions. The message of the exception
+ /// is set by formatting the error for debugging.
+ fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status>;
+
+ /// Converts the embedded error into a service-specific binder exception. `error_code` is used
+ /// to distinguish different service-specific binder exceptions. The message of the exception
+ /// is set by lazily evaluating the `op` function.
+ fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
+ self,
+ error_code: i32,
+ op: O,
+ ) -> result::Result<T, Status>;
+}
+
+impl<T, E: std::fmt::Debug> IntoBinderResult<T, E> for result::Result<T, E> {
+ fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status> {
+ self.or_binder_exception_with(exception, |e| format!("{:?}", e))
+ }
+
+ fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
+ self,
+ exception: ExceptionCode,
+ op: O,
+ ) -> result::Result<T, Status> {
+ self.map_err(|e| Status::new_exception_str(exception, Some(op(e))))
+ }
+
+ fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status> {
+ self.or_service_specific_exception_with(error_code, |e| format!("{:?}", e))
+ }
+
+ fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
+ self,
+ error_code: i32,
+ op: O,
+ ) -> result::Result<T, Status> {
+ self.map_err(|e| Status::new_service_specific_error_str(error_code, Some(op(e))))
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -406,4 +494,66 @@ mod tests {
assert_eq!(status.service_specific_error(), 0);
assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string());
}
+
+ #[test]
+ fn convert_to_service_specific_exception() {
+ let res: std::result::Result<(), Status> =
+ Err("message").or_service_specific_exception(-42);
+
+ assert!(res.is_err());
+ let status = res.unwrap_err();
+ assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
+ assert_eq!(status.service_specific_error(), -42);
+ assert_eq!(
+ status.get_description(),
+ "Status(-8, EX_SERVICE_SPECIFIC): '-42: \"message\"'".to_string()
+ );
+ }
+
+ #[test]
+ fn convert_to_service_specific_exception_with() {
+ let res: std::result::Result<(), Status> = Err("message")
+ .or_service_specific_exception_with(-42, |e| format!("outer message: {:?}", e));
+
+ assert!(res.is_err());
+ let status = res.unwrap_err();
+ assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
+ assert_eq!(status.service_specific_error(), -42);
+ assert_eq!(
+ status.get_description(),
+ "Status(-8, EX_SERVICE_SPECIFIC): '-42: outer message: \"message\"'".to_string()
+ );
+ }
+
+ #[test]
+ fn convert_to_binder_exception() {
+ let res: std::result::Result<(), Status> =
+ Err("message").or_binder_exception(ExceptionCode::ILLEGAL_STATE);
+
+ assert!(res.is_err());
+ let status = res.unwrap_err();
+ assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
+ assert_eq!(status.service_specific_error(), 0);
+ assert_eq!(
+ status.get_description(),
+ "Status(-5, EX_ILLEGAL_STATE): '\"message\"'".to_string()
+ );
+ }
+
+ #[test]
+ fn convert_to_binder_exception_with() {
+ let res: std::result::Result<(), Status> = Err("message")
+ .or_binder_exception_with(ExceptionCode::ILLEGAL_STATE, |e| {
+ format!("outer message: {:?}", e)
+ });
+
+ assert!(res.is_err());
+ let status = res.unwrap_err();
+ assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
+ assert_eq!(status.service_specific_error(), 0);
+ assert_eq!(
+ status.get_description(),
+ "Status(-5, EX_ILLEGAL_STATE): 'outer message: \"message\"'".to_string()
+ );
+ }
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 0c8b48f1f5..8841fe640b 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -106,7 +106,7 @@ use binder_ndk_sys as sys;
pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
-pub use error::{ExceptionCode, Status, StatusCode};
+pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
pub use native::{
add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
LazyServiceGuard,
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
index ac968237a0..6eb707bcf7 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -3,19 +3,12 @@ package {
default_applicable_licenses: ["frameworks_native_license"],
}
-rust_fuzz {
- name: "parcel_fuzzer_rs",
- srcs: [
- "parcel_fuzzer.rs",
- ],
+rust_defaults {
+ name: "service_fuzzer_defaults_rs",
rustlibs: [
- "libarbitrary",
- "libnum_traits",
"libbinder_rs",
"libbinder_random_parcel_rs",
- "binderReadParcelIface-rust",
],
-
fuzz_config: {
cc: [
"waghpawan@google.com",
@@ -26,3 +19,18 @@ rust_fuzz {
hotlists: ["4637097"],
},
}
+
+rust_fuzz {
+ name: "parcel_fuzzer_rs",
+ srcs: [
+ "parcel_fuzzer.rs",
+ ],
+ defaults: [
+ "service_fuzzer_defaults_rs",
+ ],
+ rustlibs: [
+ "libarbitrary",
+ "libnum_traits",
+ "binderReadParcelIface-rust",
+ ],
+}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
index 2537ce0bcb..84130c17e1 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -19,18 +19,10 @@ rust_fuzz {
srcs: [
"service_fuzzer.rs",
],
+ defaults: [
+ "service_fuzzer_defaults_rs",
+ ],
rustlibs: [
- "libbinder_rs",
- "libbinder_random_parcel_rs",
"testServiceInterface-rust",
],
- fuzz_config: {
- cc: [
- "waghpawan@google.com",
- "smoreland@google.com",
- ],
- triage_assignee: "waghpawan@google.com",
- // hotlist "AIDL fuzzers bugs" on buganizer
- hotlists: ["4637097"],
- },
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 41856f9b79..cd3e7c0fef 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -77,6 +77,8 @@ cc_test {
static_libs: [
"binderRecordReplayTestIface-cpp",
"binderReadParcelIface-cpp",
+ "libbinder_random_parcel_seeds",
+ "libbinder_random_parcel",
],
test_suites: ["general-tests"],
require_root: true,
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index bc40864020..6712c9cece 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -216,16 +216,16 @@ TEST(RpcBinderAllocation, SetupRpcServer) {
auto server = RpcServer::make();
server->setRootObject(sp<BBinder>::make());
- CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
+ ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
std::thread([server]() { server->join(); }).detach();
- status_t status;
auto session = RpcSession::make();
- status = session->setupUnixDomainClient(addr.c_str());
- CHECK_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str();
+ status_t status = session->setupUnixDomainClient(addr.c_str());
+ ASSERT_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str();
auto remoteBinder = session->getRootObject();
+ ASSERT_NE(remoteBinder, nullptr);
size_t mallocs = 0, totalBytes = 0;
{
@@ -233,7 +233,7 @@ TEST(RpcBinderAllocation, SetupRpcServer) {
mallocs++;
totalBytes += bytes;
});
- CHECK_EQ(OK, remoteBinder->pingBinder());
+ ASSERT_EQ(OK, remoteBinder->pingBinder());
}
EXPECT_EQ(mallocs, 1);
EXPECT_EQ(totalBytes, 40);
diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
index 278dd2bf81..140270f5a1 100644
--- a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
+++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
@@ -37,7 +37,8 @@ TEST(MemoryHeapBase, MemfdSealed) {
ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
- EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL);
+ EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL);
+ EXPECT_EQ(ftruncate(fd, 4096), -1);
}
TEST(MemoryHeapBase, MemfdUnsealed) {
@@ -48,7 +49,8 @@ TEST(MemoryHeapBase, MemfdUnsealed) {
ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
- EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0);
+ EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK);
+ EXPECT_EQ(ftruncate(fd, 4096), -1);
}
TEST(MemoryHeapBase, MemfdSealedProtected) {
@@ -59,7 +61,9 @@ TEST(MemoryHeapBase, MemfdSealedProtected) {
ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
- EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
+ EXPECT_EQ(fcntl(fd, F_GET_SEALS),
+ F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
+ EXPECT_EQ(ftruncate(fd, 4096), -1);
}
TEST(MemoryHeapBase, MemfdUnsealedProtected) {
@@ -71,7 +75,8 @@ TEST(MemoryHeapBase, MemfdUnsealedProtected) {
ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
- EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE);
+ EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_FUTURE_WRITE);
+ EXPECT_EQ(ftruncate(fd, 4096), -1);
}
#else
diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
index 17d5c8a219..6773c95ed6 100644
--- a/libs/binder/tests/binderRecordReplayTest.cpp
+++ b/libs/binder/tests/binderRecordReplayTest.cpp
@@ -15,6 +15,7 @@
*/
#include <BnBinderRecordReplayTest.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/Binder.h>
@@ -23,6 +24,11 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/RecordedTransaction.h>
+
+#include <fuzzbinder/libbinder_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <fuzzseeds/random_parcel_seeds.h>
+
#include <gtest/gtest.h>
#include <sys/prctl.h>
@@ -30,6 +36,7 @@
#include "parcelables/SingleDataParcelable.h"
using namespace android;
+using android::generateSeedsFromRecording;
using android::binder::Status;
using android::binder::debug::RecordedTransaction;
using parcelables::SingleDataParcelable;
@@ -84,6 +91,44 @@ public:
GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>);
};
+std::vector<uint8_t> retrieveData(base::borrowed_fd fd) {
+ struct stat fdStat;
+ EXPECT_TRUE(fstat(fd.get(), &fdStat) != -1);
+ EXPECT_TRUE(fdStat.st_size != 0);
+
+ std::vector<uint8_t> buffer(fdStat.st_size);
+ auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size);
+ EXPECT_TRUE(readResult != 0);
+ return std::move(buffer);
+}
+
+void replayFuzzService(const sp<BpBinder>& binder, const RecordedTransaction& transaction) {
+ base::unique_fd seedFd(open("/data/local/tmp/replayFuzzService",
+ O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC, 0666));
+ ASSERT_TRUE(seedFd.ok());
+
+ // generate corpus from this transaction.
+ generateSeedsFromRecording(seedFd, transaction);
+
+ // Read the data which has been written to seed corpus
+ ASSERT_EQ(0, lseek(seedFd.get(), 0, SEEK_SET));
+ std::vector<uint8_t> seedData = retrieveData(seedFd);
+
+ // use fuzzService to replay the corpus
+ FuzzedDataProvider provider(seedData.data(), seedData.size());
+ fuzzService(binder, std::move(provider));
+}
+
+void replayBinder(const sp<BpBinder>& binder, const RecordedTransaction& transaction) {
+ // TODO: move logic to replay RecordedTransaction into RecordedTransaction
+ Parcel data;
+ data.setData(transaction.getDataParcel().data(), transaction.getDataParcel().dataSize());
+ auto result = binder->transact(transaction.getCode(), data, nullptr, transaction.getFlags());
+
+ // make sure recording does the thing we expect it to do
+ EXPECT_EQ(OK, result);
+}
+
class BinderRecordReplayTest : public ::testing::Test {
public:
void SetUp() override {
@@ -98,48 +143,46 @@ public:
template <typename T, typename U>
void recordReplay(Status (IBinderRecordReplayTest::*set)(T), U recordedValue,
Status (IBinderRecordReplayTest::*get)(U*), U changedValue) {
- base::unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec",
- O_RDWR | O_CREAT | O_CLOEXEC, 0666));
- ASSERT_TRUE(fd.ok());
-
- // record a transaction
- mBpBinder->startRecordingBinder(fd);
- auto status = (*mInterface.*set)(recordedValue);
- EXPECT_TRUE(status.isOk());
- mBpBinder->stopRecordingBinder();
-
- // test transaction does the thing we expect it to do
- U output;
- status = (*mInterface.*get)(&output);
- EXPECT_TRUE(status.isOk());
- EXPECT_EQ(output, recordedValue);
-
- // write over the existing state
- status = (*mInterface.*set)(changedValue);
- EXPECT_TRUE(status.isOk());
-
- status = (*mInterface.*get)(&output);
- EXPECT_TRUE(status.isOk());
-
- EXPECT_EQ(output, changedValue);
-
- // replay transaction
- ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET));
- std::optional<RecordedTransaction> transaction = RecordedTransaction::fromFile(fd);
- ASSERT_NE(transaction, std::nullopt);
-
- // TODO: move logic to replay RecordedTransaction into RecordedTransaction
- Parcel data;
- data.setData(transaction->getDataParcel().data(), transaction->getDataParcel().dataSize());
- auto result =
- mBpBinder->transact(transaction->getCode(), data, nullptr, transaction->getFlags());
-
- // make sure recording does the thing we expect it to do
- EXPECT_EQ(OK, result);
-
- status = (*mInterface.*get)(&output);
- EXPECT_TRUE(status.isOk());
- EXPECT_EQ(output, recordedValue);
+ auto replayFunctions = {&replayBinder, &replayFuzzService};
+ for (auto replayFunc : replayFunctions) {
+ base::unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec",
+ O_RDWR | O_CREAT | O_CLOEXEC, 0666));
+ ASSERT_TRUE(fd.ok());
+
+ // record a transaction
+ mBpBinder->startRecordingBinder(fd);
+ auto status = (*mInterface.*set)(recordedValue);
+ EXPECT_TRUE(status.isOk());
+ mBpBinder->stopRecordingBinder();
+
+ // test transaction does the thing we expect it to do
+ U output;
+ status = (*mInterface.*get)(&output);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(output, recordedValue);
+
+ // write over the existing state
+ status = (*mInterface.*set)(changedValue);
+ EXPECT_TRUE(status.isOk());
+
+ status = (*mInterface.*get)(&output);
+ EXPECT_TRUE(status.isOk());
+
+ EXPECT_EQ(output, changedValue);
+
+ // replay transaction
+ ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET));
+ std::optional<RecordedTransaction> transaction = RecordedTransaction::fromFile(fd);
+ ASSERT_NE(transaction, std::nullopt);
+
+ const RecordedTransaction& recordedTransaction = *transaction;
+ // call replay function with recorded transaction
+ (*replayFunc)(mBpBinder, recordedTransaction);
+
+ status = (*mInterface.*get)(&output);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(output, recordedValue);
+ }
}
private:
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index d352ce5bca..4c3c68e2e7 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -249,12 +249,12 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions);
}
- SocketType socketType = std::get<0>(GetParam());
- RpcSecurity rpcSecurity = std::get<1>(GetParam());
- uint32_t clientVersion = std::get<2>(GetParam());
- uint32_t serverVersion = std::get<3>(GetParam());
- bool singleThreaded = std::get<4>(GetParam());
- bool noKernel = std::get<5>(GetParam());
+ SocketType socketType = GetParam().type;
+ RpcSecurity rpcSecurity = GetParam().security;
+ uint32_t clientVersion = GetParam().clientVersion;
+ uint32_t serverVersion = GetParam().serverVersion;
+ bool singleThreaded = GetParam().singleThreaded;
+ bool noKernel = GetParam().noKernel;
std::string path = android::base::GetExecutableDirectory();
auto servicePath = android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(),
@@ -674,7 +674,7 @@ TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) {
// session 0 - will check for leaks in destrutor of proc
// session 1 - we want to make sure it gets deleted when we drop all references to it
auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1, .numIncomingConnectionsBySession = {0, 1}, .numSessions = 2});
+ {.numThreads = 1, .numSessions = 2, .numIncomingConnectionsBySession = {0, 1}});
wp<RpcSession> session = proc.proc->sessions.at(1).session;
@@ -1121,12 +1121,27 @@ TEST_P(BinderRpc, Fds) {
}
#ifdef BINDER_RPC_TO_TRUSTY_TEST
-INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
- ::testing::Combine(::testing::Values(SocketType::TIPC),
- ::testing::Values(RpcSecurity::RAW),
- ::testing::ValuesIn(testVersions()),
- ::testing::ValuesIn(testVersions()),
- ::testing::Values(true), ::testing::Values(true)),
+
+static std::vector<BinderRpc::ParamType> getTrustyBinderRpcParams() {
+ std::vector<BinderRpc::ParamType> ret;
+
+ for (const auto& clientVersion : testVersions()) {
+ for (const auto& serverVersion : testVersions()) {
+ ret.push_back(BinderRpc::ParamType{
+ .type = SocketType::TIPC,
+ .security = RpcSecurity::RAW,
+ .clientVersion = clientVersion,
+ .serverVersion = serverVersion,
+ .singleThreaded = true,
+ .noKernel = true,
+ });
+ }
+ }
+
+ return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, ::testing::ValuesIn(getTrustyBinderRpcParams()),
BinderRpc::PrintParamInfo);
#else // BINDER_RPC_TO_TRUSTY_TEST
bool testSupportVsockLoopback() {
@@ -1246,13 +1261,47 @@ static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
return ret;
}
-INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
- ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
- ::testing::ValuesIn(RpcSecurityValues()),
- ::testing::ValuesIn(testVersions()),
- ::testing::ValuesIn(testVersions()),
- ::testing::Values(false, true),
- ::testing::Values(false, true)),
+static std::vector<BinderRpc::ParamType> getBinderRpcParams() {
+ std::vector<BinderRpc::ParamType> ret;
+
+ constexpr bool full = false;
+
+ for (const auto& type : testSocketTypes()) {
+ if (full || type == SocketType::UNIX) {
+ for (const auto& security : RpcSecurityValues()) {
+ for (const auto& clientVersion : testVersions()) {
+ for (const auto& serverVersion : testVersions()) {
+ for (bool singleThreaded : {false, true}) {
+ for (bool noKernel : {false, true}) {
+ ret.push_back(BinderRpc::ParamType{
+ .type = type,
+ .security = security,
+ .clientVersion = clientVersion,
+ .serverVersion = serverVersion,
+ .singleThreaded = singleThreaded,
+ .noKernel = noKernel,
+ });
+ }
+ }
+ }
+ }
+ }
+ } else {
+ ret.push_back(BinderRpc::ParamType{
+ .type = type,
+ .security = RpcSecurity::RAW,
+ .clientVersion = RPC_WIRE_PROTOCOL_VERSION,
+ .serverVersion = RPC_WIRE_PROTOCOL_VERSION,
+ .singleThreaded = false,
+ .noKernel = false,
+ });
+ }
+ }
+
+ return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::ValuesIn(getBinderRpcParams()),
BinderRpc::PrintParamInfo);
class BinderRpcServerRootObject
diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
index 0b8920b5a6..2c9646b30e 100644
--- a/libs/binder/tests/binderRpcTestFixture.h
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -106,15 +106,23 @@ struct BinderRpcTestProcessSession {
}
};
-class BinderRpc : public ::testing::TestWithParam<
- std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> {
+struct BinderRpcParam {
+ SocketType type;
+ RpcSecurity security;
+ uint32_t clientVersion;
+ uint32_t serverVersion;
+ bool singleThreaded;
+ bool noKernel;
+};
+class BinderRpc : public ::testing::TestWithParam<BinderRpcParam> {
public:
- SocketType socketType() const { return std::get<0>(GetParam()); }
- RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
- uint32_t clientVersion() const { return std::get<2>(GetParam()); }
- uint32_t serverVersion() const { return std::get<3>(GetParam()); }
- bool serverSingleThreaded() const { return std::get<4>(GetParam()); }
- bool noKernel() const { return std::get<5>(GetParam()); }
+ // TODO: avoid unnecessary layer of indirection
+ SocketType socketType() const { return GetParam().type; }
+ RpcSecurity rpcSecurity() const { return GetParam().security; }
+ uint32_t clientVersion() const { return GetParam().clientVersion; }
+ uint32_t serverVersion() const { return GetParam().serverVersion; }
+ bool serverSingleThreaded() const { return GetParam().singleThreaded; }
+ bool noKernel() const { return GetParam().noKernel; }
bool clientOrServerSingleThreaded() const {
return !kEnableRpcThreads || serverSingleThreaded();
@@ -148,15 +156,16 @@ public:
}
static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
- auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
- std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
- if (singleThreaded) {
+ auto ret = PrintToString(info.param.type) + "_" +
+ newFactory(info.param.security)->toCString() + "_clientV" +
+ std::to_string(info.param.clientVersion) + "_serverV" +
+ std::to_string(info.param.serverVersion);
+ if (info.param.singleThreaded) {
ret += "_single_threaded";
} else {
ret += "_multi_threaded";
}
- if (noKernel) {
+ if (info.param.noKernel) {
ret += "_no_kernel";
} else {
ret += "_with_kernel";
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index 28be10db76..fcb83bdabd 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -57,9 +57,9 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
[](size_t n) { return n != 0; }),
"Non-zero incoming connections on Trusty");
- RpcSecurity rpcSecurity = std::get<1>(GetParam());
- uint32_t clientVersion = std::get<2>(GetParam());
- uint32_t serverVersion = std::get<3>(GetParam());
+ RpcSecurity rpcSecurity = GetParam().security;
+ uint32_t clientVersion = GetParam().clientVersion;
+ uint32_t serverVersion = GetParam().serverVersion;
auto ret = std::make_unique<TrustyProcessSession>();
@@ -89,12 +89,27 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
return ret;
}
-INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
- ::testing::Combine(::testing::Values(SocketType::TIPC),
- ::testing::Values(RpcSecurity::RAW),
- ::testing::ValuesIn(testVersions()),
- ::testing::ValuesIn(testVersions()),
- ::testing::Values(false), ::testing::Values(true)),
+static std::vector<BinderRpc::ParamType> getTrustyBinderRpcParams() {
+ std::vector<BinderRpc::ParamType> ret;
+
+ for (const auto& clientVersion : testVersions()) {
+ for (const auto& serverVersion : testVersions()) {
+ ret.push_back(BinderRpc::ParamType{
+ .type = SocketType::TIPC,
+ .security = RpcSecurity::RAW,
+ .clientVersion = clientVersion,
+ .serverVersion = serverVersion,
+ // TODO: should we test both versions here?
+ .singleThreaded = false,
+ .noKernel = true,
+ });
+ }
+ }
+
+ return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, ::testing::ValuesIn(getTrustyBinderRpcParams()),
BinderRpc::PrintParamInfo);
} // namespace android
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 1f4601010c..e43508ee79 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -84,7 +84,7 @@ TEST_P(BinderRpc, SeparateRootObject) {
GTEST_SKIP() << "This test requires a multi-threaded service";
}
- SocketType type = std::get<0>(GetParam());
+ SocketType type = GetParam().type;
if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) {
// we can't get port numbers for unix sockets
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 35866adf20..383795eff5 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -104,3 +104,43 @@ cc_library_static {
local_include_dirs: ["include_random_parcel"],
export_include_dirs: ["include_random_parcel"],
}
+
+cc_library {
+ name: "libbinder_random_parcel_seeds",
+ host_supported: true,
+ vendor_available: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ srcs: [
+ "random_parcel_seeds.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libbinder_ndk",
+ "libcutils",
+ "libutils",
+ ],
+ local_include_dirs: [
+ "include_random_parcel_seeds",
+ ],
+ export_include_dirs: ["include_random_parcel_seeds"],
+}
+
+cc_binary_host {
+ name: "binder2corpus",
+ static_libs: [
+ "libbinder_random_parcel_seeds",
+ ],
+ srcs: [
+ "binder2corpus/binder2corpus.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libutils",
+ ],
+}
diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md
new file mode 100644
index 0000000000..59bf9f31c0
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md
@@ -0,0 +1,31 @@
+# binder2corpus
+
+This tool converts recordings generated by record_binder tool to fuzzer seeds for fuzzService.
+
+# Steps to add corpus:
+
+## Start recording the service binder
+ex. record_binder start manager
+
+## Run test on device or keep device idle
+ex. atest servicemanager_test
+
+## Stop the recording
+record_binder stop manager
+
+## Pull the recording on host
+Recordings are present on device at /data/local/recordings/<service_name>. Use adb pull.
+Use inspect command of record_binder to check if there are some transactions captured.
+ex. record_binder inspect manager
+
+## run corpus generator tool
+binder2corpus <recording_path> <dir_to_write_corpus>
+
+## Build fuzzer and sync data directory
+ex. m servicemanager_fuzzer && adb sync data
+
+## Push corpus on device
+ex. adb push servicemanager_fuzzer_corpus/ /data/fuzz/x86_64/servicemanager_fuzzer/
+
+## Run fuzzer with corpus directory as argument
+ex. adb shell /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer_corpus \ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp
new file mode 100644
index 0000000000..c0fdaea0a8
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/RecordedTransaction.h>
+
+#include <fuzzseeds/random_parcel_seeds.h>
+
+#include <sys/prctl.h>
+
+using android::generateSeedsFromRecording;
+using android::status_t;
+using android::base::unique_fd;
+using android::binder::debug::RecordedTransaction;
+
+status_t generateCorpus(const char* recordingPath, const char* corpusDir) {
+ unique_fd fd(open(recordingPath, O_RDONLY));
+ if (!fd.ok()) {
+ std::cerr << "Failed to open recording file at path " << recordingPath
+ << " with error: " << strerror(errno) << '\n';
+ return android::BAD_VALUE;
+ }
+
+ if (auto res = mkdir(corpusDir, 0766); res != 0) {
+ std::cerr
+ << "Failed to create corpus directory at path. Delete directory if already exists: "
+ << corpusDir << std::endl;
+ return android::BAD_VALUE;
+ }
+
+ int transactionNumber = 0;
+ while (auto transaction = RecordedTransaction::fromFile(fd)) {
+ ++transactionNumber;
+ std::string filePath = std::string(corpusDir) + std::string("transaction_") +
+ std::to_string(transactionNumber);
+ constexpr int openFlags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC;
+ android::base::unique_fd corpusFd(open(filePath.c_str(), openFlags, 0666));
+ if (!corpusFd.ok()) {
+ std::cerr << "Failed to open fd. Path " << filePath
+ << " with error: " << strerror(errno) << std::endl;
+ return android::UNKNOWN_ERROR;
+ }
+ generateSeedsFromRecording(corpusFd, transaction.value());
+ }
+
+ if (transactionNumber == 0) {
+ std::cerr << "No valid transaction has been found in recording file: " << recordingPath
+ << std::endl;
+ return android::BAD_VALUE;
+ }
+
+ return android::NO_ERROR;
+}
+
+void printHelp(const char* toolName) {
+ std::cout << "Usage: \n\n"
+ << toolName
+ << " <recording_path> <destination_directory> \n\n*Use "
+ "record_binder tool for recording binder transactions."
+ << std::endl;
+}
+
+int main(int argc, char** argv) {
+ if (argc != 3) {
+ printHelp(argv[0]);
+ return 1;
+ }
+ const char* sourcePath = argv[1];
+ const char* corpusDir = argv[2];
+ if (android::NO_ERROR != generateCorpus(sourcePath, corpusDir)) {
+ std::cerr << "Failed to generate fuzzer corpus." << std::endl;
+ return 1;
+ }
+ return 0;
+}
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h
new file mode 100644
index 0000000000..5755239c8b
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/hex.h>
+#include <android-base/logging.h>
+
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+#include <binder/RecordedTransaction.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <vector>
+
+using android::Parcel;
+using android::base::HexString;
+using std::vector;
+
+namespace android {
+namespace impl {
+// computes the bytes so that if they are passed to FuzzedDataProvider and
+// provider.ConsumeIntegralInRange<T>(min, max) is called, it will return val
+template <typename T>
+void writeReversedBuffer(std::vector<std::byte>& integralBuffer, T min, T max, T val);
+
+// Calls writeInBuffer method with min and max numeric limits of type T. This method
+// is reversal of ConsumeIntegral<T>() in FuzzedDataProvider
+template <typename T>
+void writeReversedBuffer(std::vector<std::byte>& integralBuffer, T val);
+} // namespace impl
+void generateSeedsFromRecording(base::borrowed_fd fd,
+ const binder::debug::RecordedTransaction& transaction);
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index b268c5dcd4..93ac1162ed 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -35,6 +35,11 @@ void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& p
.extraFds = {},
};
+ // Reserved bytes so that we don't have to change fuzzers and seed corpus if
+ // we introduce anything new in fuzzService.
+ std::vector<uint8_t> reservedBytes = provider.ConsumeBytes<uint8_t>(8);
+ (void)reservedBytes;
+
// always refresh the calling identity, because we sometimes set it below, but also,
// the code we're fuzzing might reset it
IPCThreadState::self()->clearCallingIdentity();
@@ -55,8 +60,15 @@ void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& p
while (provider.remaining_bytes() > 0) {
// Most of the AIDL services will have small set of transaction codes.
- uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
- : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+ // TODO(b/295942369) : Add remaining transact codes from IBinder.h
+ uint32_t code = provider.ConsumeBool()
+ ? provider.ConsumeIntegral<uint32_t>()
+ : provider.PickValueInArray<int64_t>(
+ {provider.ConsumeIntegralInRange<uint32_t>(0, 100),
+ IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
+ IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
+ IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
+ IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
// for increased fuzz coverage
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
new file mode 100644
index 0000000000..9e3e2aba77
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include <binder/RecordedTransaction.h>
+
+#include <fuzzseeds/random_parcel_seeds.h>
+
+using android::base::WriteFully;
+
+namespace android {
+namespace impl {
+template <typename T>
+std::vector<uint8_t> reverseBytes(T min, T max, T val) {
+ uint64_t range = static_cast<uint64_t>(max) - min;
+ uint64_t result = val - min;
+ size_t offset = 0;
+
+ std::vector<uint8_t> reverseData;
+ uint8_t reversed = 0;
+ reversed |= result;
+
+ while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0) {
+ reverseData.push_back(reversed);
+ reversed = 0;
+ reversed |= (result >> CHAR_BIT);
+ result = result >> CHAR_BIT;
+ offset += CHAR_BIT;
+ }
+
+ return std::move(reverseData);
+}
+
+template <typename T>
+void writeReversedBuffer(std::vector<uint8_t>& integralBuffer, T min, T max, T val) {
+ std::vector<uint8_t> reversedData = reverseBytes(min, max, val);
+ // ConsumeIntegral Calls read buffer from the end. Keep inserting at the front of the buffer
+ // so that we can align fuzzService operations with seed generation for readability.
+ integralBuffer.insert(integralBuffer.begin(), reversedData.begin(), reversedData.end());
+}
+
+template <typename T>
+void writeReversedBuffer(std::vector<uint8_t>& integralBuffer, T val) {
+ // For ConsumeIntegral<T>() calls, FuzzedDataProvider uses numeric limits min and max
+ // as range
+ writeReversedBuffer(integralBuffer, std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max(), val);
+}
+
+} // namespace impl
+
+void generateSeedsFromRecording(base::borrowed_fd fd,
+ const binder::debug::RecordedTransaction& transaction) {
+ // Write Reserved bytes for future use
+ std::vector<uint8_t> reservedBytes(8);
+ CHECK(WriteFully(fd, reservedBytes.data(), reservedBytes.size())) << fd.get();
+
+ std::vector<uint8_t> integralBuffer;
+
+ // Write UID array : Array elements are initialized in the order that they are declared
+ // UID array index 2 element
+ // int64_t aidRoot = 0;
+ impl::writeReversedBuffer(integralBuffer, static_cast<int64_t>(AID_ROOT) << 32,
+ static_cast<int64_t>(AID_USER) << 32,
+ static_cast<int64_t>(AID_ROOT) << 32);
+
+ // UID array index 3 element
+ impl::writeReversedBuffer(integralBuffer, static_cast<int64_t>(AID_ROOT) << 32);
+
+ // always pick AID_ROOT -> index 0
+ size_t uidIndex = 0;
+ impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), static_cast<size_t>(3),
+ uidIndex);
+
+ // Never set uid in seed corpus
+ uint8_t writeUid = 0;
+ impl::writeReversedBuffer(integralBuffer, writeUid);
+
+ // Read random code. this will be from recorded transaction
+ uint8_t selectCode = 1;
+ impl::writeReversedBuffer(integralBuffer, selectCode);
+
+ // Get from recorded transaction
+ uint32_t code = transaction.getCode();
+ impl::writeReversedBuffer(integralBuffer, code);
+
+ // Get from recorded transaction
+ uint32_t flags = transaction.getFlags();
+ impl::writeReversedBuffer(integralBuffer, flags);
+
+ // always fuzz primary binder
+ size_t extraBindersIndex = 0;
+ impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), static_cast<size_t>(0),
+ extraBindersIndex);
+
+ const Parcel& dataParcel = transaction.getDataParcel();
+
+ // This buffer holds the bytes which will be used for fillRandomParcel API
+ std::vector<uint8_t> fillParcelBuffer;
+
+ // Don't take rpc path
+ uint8_t rpcBranch = 0;
+ impl::writeReversedBuffer(fillParcelBuffer, rpcBranch);
+
+ // Implicit branch on this path -> options->writeHeader(p, provider)
+ uint8_t writeHeaderInternal = 0;
+ impl::writeReversedBuffer(fillParcelBuffer, writeHeaderInternal);
+
+ // Choose to write data in parcel
+ size_t fillFuncIndex = 0;
+ impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2),
+ fillFuncIndex);
+
+ // Write parcel data size from recorded transaction
+ size_t toWrite = transaction.getDataParcel().dataBufferSize();
+ impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), toWrite, toWrite);
+
+ // Write parcel data with size towrite from recorded transaction
+ CHECK(WriteFully(fd, dataParcel.data(), toWrite)) << fd.get();
+
+ // Write Fill Parcel buffer size in integralBuffer so that fuzzService knows size of data
+ size_t subDataSize = toWrite + fillParcelBuffer.size();
+ impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), subDataSize, subDataSize);
+
+ // Write fill parcel buffer
+ CHECK(WriteFully(fd, fillParcelBuffer.data(), fillParcelBuffer.size())) << fd.get();
+
+ // Write the integralBuffer to data
+ CHECK(WriteFully(fd, integralBuffer.data(), integralBuffer.size())) << fd.get();
+}
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
index 46205d7689..d2fa581822 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
@@ -33,6 +33,9 @@ enum class CrashType {
ON_KNOWN_UID,
ON_SYSTEM_AID,
ON_ROOT_AID,
+ ON_DUMP_TRANSACT,
+ ON_SHELL_CMD_TRANSACT,
+ CRASH_ALWAYS,
};
// This service is to verify that fuzzService is functioning properly
@@ -92,6 +95,16 @@ public:
return Status::ok();
}
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override {
+ if (mCrash == CrashType::ON_DUMP_TRANSACT && code == DUMP_TRANSACTION) {
+ LOG_ALWAYS_FATAL("Expected crash, DUMP.");
+ } else if (mCrash == CrashType::ON_SHELL_CMD_TRANSACT &&
+ code == SHELL_COMMAND_TRANSACTION) {
+ LOG_ALWAYS_FATAL("Expected crash, SHELL_CMD.");
+ }
+ return BnTestService::onTransact(code, data, reply, flags);
+ }
+
private:
CrashType mCrash;
};
@@ -100,8 +113,10 @@ CrashType gCrashType = CrashType::NONE;
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
if (*argc < 2) {
- printf("You must specify at least one argument\n");
- exit(0); // success because this is a crash test
+ // This fuzzer is also used as test fuzzer to check infra pipeline.
+ // It should always run and find a crash in TestService.
+ gCrashType = CrashType::CRASH_ALWAYS;
+ return 0;
}
std::string arg = std::string((*argv)[1]);
@@ -121,6 +136,10 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
gCrashType = CrashType::ON_ROOT_AID;
} else if (arg == "BINDER") {
gCrashType = CrashType::ON_BINDER;
+ } else if (arg == "DUMP") {
+ gCrashType = CrashType::ON_DUMP_TRANSACT;
+ } else if (arg == "SHELL_CMD") {
+ gCrashType = CrashType::ON_SHELL_CMD_TRANSACT;
} else {
printf("INVALID ARG\n");
exit(0); // success because this is a crash test
@@ -130,6 +149,9 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (gCrashType == CrashType::CRASH_ALWAYS) {
+ LOG_ALWAYS_FATAL("Expected crash, This fuzzer will always crash.");
+ }
auto service = sp<TestService>::make(gCrashType);
fuzzService(service, FuzzedDataProvider(data, size));
return 0;
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
index 25906d8aeb..c447bffbfd 100755
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -27,7 +27,7 @@ then
exit 1
fi
-for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER; do
+for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER DUMP SHELL_CMD; do
echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE"
./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=30 &>"$FUZZER_OUT"
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index b8ae84d5c0..dcc8b8ebf7 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -135,7 +135,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// b/260736889 - limit arbitrarily, due to thread resource exhaustion, which currently
// aborts. Servers should consider RpcServer::setConnectionFilter instead.
- constexpr size_t kMaxConnections = 1000;
+ constexpr size_t kMaxConnections = 10;
while (provider.remaining_bytes() > 0) {
if (connections.empty() ||
diff --git a/libs/binder/trusty/fuzzer/Android.bp b/libs/binder/trusty/fuzzer/Android.bp
new file mode 100644
index 0000000000..2f1f54b0a6
--- /dev/null
+++ b/libs/binder/trusty/fuzzer/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_fuzz {
+ name: "trusty_binder_fuzzer",
+ defaults: ["trusty_fuzzer_defaults"],
+ srcs: [":trusty_tipc_fuzzer"],
+ cflags: [
+ "-DTRUSTY_APP_PORT=\"com.android.trusty.binder.test.service\"",
+ "-DTRUSTY_APP_UUID=\"d42f06c5-9dc5-455b-9914-cf094116cfa8\"",
+ "-DTRUSTY_APP_FILENAME=\"binder-test-service.syms.elf\"",
+ ],
+}
+
+cc_fuzz {
+ name: "trusty_binder_rpc_fuzzer",
+ defaults: ["trusty_fuzzer_defaults"],
+ srcs: [":trusty_tipc_fuzzer"],
+ cflags: [
+ "-DTRUSTY_APP_PORT=\"com.android.trusty.binderRpcTestService.V0\"",
+ "-DTRUSTY_APP_UUID=\"87e424e5-69d7-4bbd-8b7c-7e24812cbc94\"",
+ "-DTRUSTY_APP_FILENAME=\"binderRpcTestService.syms.elf\"",
+ ],
+}
diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp
index 95a85b550f..ff951487bc 100644
--- a/libs/bufferstreams/rust/Android.bp
+++ b/libs/bufferstreams/rust/Android.bp
@@ -17,7 +17,8 @@ rust_library {
crate_name: "bufferstreams",
srcs: ["src/lib.rs"],
edition: "2021",
- vendor_available: true,
- host_supported: true,
+ rlibs: [
+ "libnativewindow_rs",
+ ],
min_sdk_version: "30",
}
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
index 51f1c73c49..1d321c833d 100644
--- a/libs/bufferstreams/rust/src/lib.rs
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -14,9 +14,143 @@
//! libbufferstreams: Reactive Streams for Graphics Buffers
+use nativewindow::*;
+use std::sync::{Arc, Weak};
+use std::time::Instant;
+
/// This function will print Hello World.
#[no_mangle]
pub extern "C" fn hello() -> bool {
println!("Hello world.");
true
}
+
+/// BufferPublishers provide buffers to BufferSusbscribers. Depending on the
+/// particular object in question, these could be allocated locally or provided
+/// over IPC.
+///
+/// BufferPublishers are required to adhere to the following, based on the
+/// reactive streams specification:
+/// * The total number of on_next´s signalled by a Publisher to a Subscriber
+/// MUST be less than or equal to the total number of elements requested by that
+/// Subscriber´s Subscription at all times.
+/// * A Publisher MAY signal fewer on_next than requested and terminate the
+/// Subscription by calling on_complete or on_error.
+/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber
+/// MUST be signaled serially.
+/// * If a Publisher fails it MUST signal an on_error.
+/// * If a Publisher terminates successfully (finite stream) it MUST signal an
+/// on_complete.
+/// * If a Publisher signals either on_error or on_complete on a Subscriber,
+/// that Subscriber’s Subscription MUST be considered cancelled.
+/// * Once a terminal state has been signaled (on_error, on_complete) it is
+/// REQUIRED that no further signals occur.
+/// * If a Subscription is cancelled its Subscriber MUST eventually stop being
+/// signaled.
+/// * A Publisher MAY support multiple Subscribers and decides whether each
+/// Subscription is unicast or multicast.
+pub trait BufferPublisher {
+ /// This function will create the subscription between the publisher and
+ /// the subscriber.
+ fn subscribe(&self, subscriber: Weak<dyn BufferSubscriber>);
+}
+
+/// BufferSubscribers can subscribe to BufferPublishers. They can request Frames
+/// via the BufferSubscription they get from the publisher, then receive Frames
+/// via on_next.
+///
+/// BufferSubcribers are required to adhere to the following, based on the
+/// reactive streams specification:
+/// * The total number of on_next´s signalled by a Publisher to a Subscriber
+/// MUST be less than or equal to the total number of elements requested by that
+/// Subscriber´s Subscription at all times.
+/// * A Publisher MAY signal fewer on_next than requested and terminate the
+/// Subscription by calling on_complete or on_error.
+/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber
+/// MUST be signaled serially.
+/// * If a Publisher fails it MUST signal an on_error.
+/// * If a Publisher terminates successfully (finite stream) it MUST signal an
+/// on_complete.
+/// * If a Publisher signals either on_error or on_complete on a Subscriber,
+/// that Subscriber’s Subscription MUST be considered cancelled.
+/// * Once a terminal state has been signaled (on_error, on_complete) it is
+/// REQUIRED that no further signals occur.
+/// * If a Subscription is cancelled its Subscriber MUST eventually stop being
+/// signaled.
+/// * Publisher.subscribe MAY be called as many times as wanted but MUST be
+/// with a different Subscriber each time.
+/// * A Publisher MAY support multiple Subscribers and decides whether each
+/// Subscription is unicast or multicast.
+pub trait BufferSubscriber {
+ /// This function will be called at the beginning of the subscription.
+ fn on_subscribe(&self, subscription: Arc<dyn BufferSubscription>);
+ /// This function will be called for buffer that comes in.
+ fn on_next(&self, frame: Frame);
+ /// This function will be called in case of an error.
+ fn on_error(&self, error: BufferError);
+ /// This function will be called on finite streams when done.
+ fn on_complete(&self);
+}
+
+/// BufferSubscriptions serve as the bridge between BufferPublishers and
+/// BufferSubscribers. BufferSubscribers receive a BufferSubscription when they
+/// subscribe to a BufferPublisher via on_subscribe.
+/// This object is to be used by the BufferSubscriber to cancel its subscription
+/// or request more buffers.
+///
+/// BufferSubcriptions are required to adhere to the following, based on the
+/// reactive streams specification:
+/// * Subscription.request and Subscription.cancel MUST only be called inside
+/// of its Subscriber context.
+/// * The Subscription MUST allow the Subscriber to call Subscription.request
+/// synchronously from within on_next or on_subscribe.
+/// * Subscription.request MUST place an upper bound on possible synchronous
+/// recursion between Publisher and Subscriber.
+/// * Subscription.request SHOULD respect the responsivity of its caller by
+/// returning in a timely manner.
+/// * Subscription.cancel MUST respect the responsivity of its caller by
+/// returning in a timely manner, MUST be idempotent and MUST be thread-safe.
+/// * After the Subscription is cancelled, additional
+/// Subscription.request(n: u64) MUST be NOPs.
+/// * After the Subscription is cancelled, additional Subscription.cancel()
+/// MUST be NOPs.
+/// * While the Subscription is not cancelled, Subscription.request(n: u64)
+/// MUST register the given number of additional elements to be produced to the
+/// respective subscriber.
+/// * While the Subscription is not cancelled, Subscription.request(n: u64)
+/// MUST signal on_error if the argument is <= 0. The cause message SHOULD
+/// explain that non-positive request signals are illegal.
+/// * While the Subscription is not cancelled, Subscription.request(n: u64)
+/// MAY synchronously call on_next on this (or other) subscriber(s).
+/// * While the Subscription is not cancelled, Subscription.request(n: u64)
+/// MAY synchronously call on_complete or on_error on this (or other)
+/// subscriber(s).
+/// * While the Subscription is not cancelled, Subscription.cancel() MUST
+/// request the Publisher to eventually stop signaling its Subscriber. The
+/// operation is NOT REQUIRED to affect the Subscription immediately.
+/// * While the Subscription is not cancelled, Subscription.cancel() MUST
+/// request the Publisher to eventually drop any references to the corresponding
+/// subscriber.
+/// * While the Subscription is not cancelled, calling Subscription.cancel MAY
+/// cause the Publisher, if stateful, to transition into the shut-down state if
+/// no other Subscription exists at this point.
+/// * Calling Subscription.cancel MUST return normally.
+/// * Calling Subscription.request MUST return normally.
+pub trait BufferSubscription {
+ /// request
+ fn request(&self, n: u64);
+ /// cancel
+ fn cancel(&self);
+}
+/// Type used to describe errors produced by subscriptions.
+type BufferError = Box<dyn std::error::Error + Send + Sync + 'static>;
+
+/// Struct used to contain the buffer.
+pub struct Frame {
+ /// A handle to the C buffer interface.
+ pub buffer: AHardwareBuffer,
+ /// The time at which the buffer was dispatched.
+ pub present_time: Instant,
+ /// A fence used for reading/writing safely.
+ pub fence: i32,
+}
diff --git a/libs/fakeservicemanager/FakeServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
index 3272bbc1aa..80661c1cbd 100644
--- a/libs/fakeservicemanager/FakeServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -26,6 +26,8 @@ sp<IBinder> FakeServiceManager::getService( const String16& name) const {
}
sp<IBinder> FakeServiceManager::checkService( const String16& name) const {
+ std::lock_guard<std::mutex> l(mMutex);
+
auto it = mNameToService.find(name);
if (it == mNameToService.end()) {
return nullptr;
@@ -36,6 +38,8 @@ sp<IBinder> FakeServiceManager::checkService( const String16& name) const {
status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service,
bool /*allowIsolated*/,
int /*dumpsysFlags*/) {
+ std::lock_guard<std::mutex> l(mMutex);
+
if (service == nullptr) {
return UNEXPECTED_NULL;
}
@@ -44,6 +48,8 @@ status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>&
}
Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) {
+ std::lock_guard<std::mutex> l(mMutex);
+
Vector<String16> services;
for (auto const& [name, service] : mNameToService) {
(void) service;
@@ -61,10 +67,14 @@ sp<IBinder> FakeServiceManager::waitForService(const String16& name) {
}
bool FakeServiceManager::isDeclared(const String16& name) {
+ std::lock_guard<std::mutex> l(mMutex);
+
return mNameToService.find(name) != mNameToService.end();
}
Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) {
+ std::lock_guard<std::mutex> l(mMutex);
+
Vector<String16> out;
const String16 prefix = name + String16("/");
for (const auto& [registeredName, service] : mNameToService) {
@@ -108,6 +118,8 @@ std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDeb
}
void FakeServiceManager::clear() {
+ std::lock_guard<std::mutex> l(mMutex);
+
mNameToService.clear();
}
} // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index 97add24ac8..f62241d9c2 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -19,6 +19,7 @@
#include <binder/IServiceManager.h>
#include <map>
+#include <mutex>
#include <optional>
#include <vector>
@@ -68,6 +69,7 @@ public:
void clear();
private:
+ mutable std::mutex mMutex;
std::map<String16, sp<IBinder>> mNameToService;
};
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 715822b4e4..732ca36b44 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -512,7 +512,11 @@ bool GraphicsEnv::shouldUseAngle() {
return mShouldUseAngle;
}
-void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseSystemAngle,
+// Set ANGLE information.
+// If path is "system", it means system ANGLE must be used for the process.
+// If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
+// If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
+void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
const std::string& packageName,
const std::vector<std::string> eglFeatures) {
if (mShouldUseAngle) {
@@ -529,8 +533,13 @@ void GraphicsEnv::setAngleInfo(const std::string& path, const bool shouldUseSyst
mAnglePath = std::move(path);
ALOGV("setting app package name to '%s'", packageName.c_str());
mPackageName = std::move(packageName);
- mShouldUseAngle = true;
- mShouldUseSystemAngle = shouldUseSystemAngle;
+ if (mAnglePath == "system") {
+ mShouldUseSystemAngle = true;
+ }
+ if (!mAnglePath.empty()) {
+ mShouldUseAngle = true;
+ }
+ mShouldUseNativeDriver = shouldUseNativeDriver;
}
std::string& GraphicsEnv::getPackageName() {
@@ -607,6 +616,10 @@ bool GraphicsEnv::shouldUseSystemAngle() {
return mShouldUseSystemAngle;
}
+bool GraphicsEnv::shouldUseNativeDriver() {
+ return mShouldUseNativeDriver;
+}
+
/**
* APIs for debuggable layers
*/
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 4c070aec01..1c0439ec1e 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -180,9 +180,9 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep
return reply->writeUtf8AsUtf16(driverPath);
}
case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
+ int in = dup(data.readFileDescriptor());
+ int out = dup(data.readFileDescriptor());
+ int err = dup(data.readFileDescriptor());
std::vector<String16> args;
data.readString16Vector(&args);
@@ -195,6 +195,9 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep
status = shellCommand(in, out, err, args);
if (resultReceiver != nullptr) resultReceiver->send(status);
+ ::close(in);
+ ::close(out);
+ ::close(err);
return OK;
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index fbf2902869..6cce3f6998 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -108,7 +108,10 @@ public:
// (libraries must be stored uncompressed and page aligned); such elements
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
- void setAngleInfo(const std::string& path, const bool useSystemAngle,
+ // If the search patch is "system", then it means the system ANGLE should be used.
+ // If shouldUseNativeDriver is true, it means native GLES drivers must be used for the process.
+ // If path is set to nonempty and shouldUseNativeDriver is true, ANGLE will be used regardless.
+ void setAngleInfo(const std::string& path, const bool shouldUseNativeDriver,
const std::string& packageName, const std::vector<std::string> eglFeatures);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
@@ -118,6 +121,7 @@ public:
// Set the persist.graphics.egl system property value.
void nativeToggleAngleAsSystemDriver(bool enabled);
bool shouldUseSystemAngle();
+ bool shouldUseNativeDriver();
/*
* Apis for debug layer
@@ -175,6 +179,8 @@ private:
bool mShouldUseAngle = false;
// Whether loader should load system ANGLE.
bool mShouldUseSystemAngle = false;
+ // Whether loader should load native GLES driver.
+ bool mShouldUseNativeDriver = false;
// ANGLE namespace.
android_namespace_t* mAngleNamespace = nullptr;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d7e7eb8ea1..298838d816 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -62,6 +62,7 @@ filegroup {
name: "guiconstants_aidl",
srcs: [
"android/gui/DropInputMode.aidl",
+ "android/gui/StalledTransactionInfo.aidl",
"android/**/TouchOcclusionMode.aidl",
],
}
@@ -140,6 +141,7 @@ aidl_library {
"android/gui/IWindowInfosListener.aidl",
"android/gui/IWindowInfosPublisher.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
+ "android/gui/StalledTransactionInfo.aidl",
"android/gui/WindowInfo.aidl",
"android/gui/WindowInfosUpdate.aidl",
],
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 5c324b29cd..207fa4fd31 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -303,13 +303,8 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/,
// frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers
// set and then check if it's empty. If there are no more pending syncs, we can
// proceed with flushing the shadow queue.
- // We also want to check if mSyncTransaction is null because it's possible another
- // sync request came in while waiting, but it hasn't started processing yet. In that
- // case, we don't actually want to flush the frames in between since they will get
- // processed and merged with the sync transaction and released earlier than if they
- // were sent to SF
mSyncedFrameNumbers.erase(currFrameNumber);
- if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) {
+ if (mSyncedFrameNumbers.empty()) {
flushShadowQueue();
}
} else {
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index f50bc203e8..e6331e7282 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -24,11 +24,11 @@
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
-#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
namespace android {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 52172090af..5b34ba12c8 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -47,23 +47,23 @@ namespace android {
// Macros for include BufferQueueCore information in log messages
#define BQ_LOGV(x, ...) \
- ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGD(x, ...) \
- ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGI(x, ...) \
- ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGW(x, ...) \
- ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGE(x, ...) \
- ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
@@ -298,8 +298,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
// decrease.
mCore->mDequeueCondition.notify_all();
- ATRACE_INT(mCore->mConsumerName.string(),
- static_cast<int32_t>(mCore->mQueue.size()));
+ ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
#endif
@@ -718,7 +717,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
status_t BufferQueueConsumer::setConsumerName(const String8& name) {
ATRACE_CALL();
- BQ_LOGV("setConsumerName: '%s'", name.string());
+ BQ_LOGV("setConsumerName: '%s'", name.c_str());
std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mConsumerName = name;
mConsumerName = name;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 2930154ad4..648db67fc1 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -41,20 +41,20 @@
namespace android {
// Macros for include BufferQueueCore information in log messages
-#define BQ_LOGV(x, ...) \
- ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
-#define BQ_LOGD(x, ...) \
- ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
-#define BQ_LOGI(x, ...) \
- ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
-#define BQ_LOGW(x, ...) \
- ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
-#define BQ_LOGE(x, ...) \
- ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
static String8 getUniqueName() {
@@ -146,23 +146,23 @@ BufferQueueCore::~BufferQueueCore() {}
void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
std::lock_guard<std::mutex> lock(mMutex);
- outResult->appendFormat("%s- BufferQueue ", prefix.string());
+ outResult->appendFormat("%s- BufferQueue ", prefix.c_str());
outResult->appendFormat("mMaxAcquiredBufferCount=%d mMaxDequeuedBufferCount=%d\n",
mMaxAcquiredBufferCount, mMaxDequeuedBufferCount);
- outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.string(),
+ outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.c_str(),
mDequeueBufferCannotBlock, mAsyncMode);
- outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.string(),
+ outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.c_str(),
mQueueBufferCanDrop, mLegacyBufferDrop);
- outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(),
+ outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.c_str(),
mDefaultWidth, mDefaultHeight, mDefaultBufferFormat);
- outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.string(),
+ outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.c_str(),
mTransformHint, mFrameCounter);
- outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.string(),
+ outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.c_str(),
mTransformHintInUse, mAutoPrerotation);
- outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+ outResult->appendFormat("%sFIFO(%zu):\n", prefix.c_str(), mQueue.size());
- outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string());
+ outResult->appendFormat("%s(mConsumerName=%s, ", prefix.c_str(), mConsumerName.c_str());
outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi,
mConsumerUsageBits);
@@ -173,12 +173,11 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const
getProcessName(mConnectedPid, producerProcName);
getProcessName(pid, consumerProcName);
outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId,
- mConnectedPid, producerProcName.string(), pid,
- consumerProcName.string());
+ mConnectedPid, producerProcName.c_str(), pid, consumerProcName.c_str());
Fifo::const_iterator current(mQueue.begin());
while (current != mQueue.end()) {
double timestamp = current->mTimestamp / 1e9;
- outResult->appendFormat("%s %02d:%p ", prefix.string(), current->mSlot,
+ outResult->appendFormat("%s %02d:%p ", prefix.c_str(), current->mSlot,
current->mGraphicBuffer.get());
outResult->appendFormat("crop=[%d,%d,%d,%d] ", current->mCrop.left, current->mCrop.top,
current->mCrop.right, current->mCrop.bottom);
@@ -187,12 +186,12 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const
++current;
}
- outResult->appendFormat("%sSlots:\n", prefix.string());
+ outResult->appendFormat("%sSlots:\n", prefix.c_str());
for (int s : mActiveBuffers) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
// A dequeued buffer might be null if it's still being allocated
if (buffer.get()) {
- outResult->appendFormat("%s %s[%02d:%p] ", prefix.string(),
+ outResult->appendFormat("%s %s[%02d:%p] ", prefix.c_str(),
(mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
buffer.get());
outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(),
@@ -200,14 +199,14 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const
outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height,
buffer->stride, buffer->format);
} else {
- outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get());
+ outResult->appendFormat("%s [%02d:%p] ", prefix.c_str(), s, buffer.get());
outResult->appendFormat("state=%-8s frame=%" PRIu64 "\n",
mSlots[s].mBufferState.string(), mSlots[s].mFrameNumber);
}
}
for (int s : mFreeBuffers) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
- outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get());
+ outResult->appendFormat("%s [%02d:%p] ", prefix.c_str(), s, buffer.get());
outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(),
buffer->handle, mSlots[s].mFrameNumber);
outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height,
@@ -216,7 +215,7 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const
for (int s : mFreeSlots) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
- outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s, buffer.get(),
+ outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.c_str(), s, buffer.get(),
mSlots[s].mBufferState.string());
}
}
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 808388fca3..10f5899b68 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -47,23 +47,23 @@ namespace android {
// Macros for include BufferQueueCore information in log messages
#define BQ_LOGV(x, ...) \
- ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGD(x, ...) \
- ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGI(x, ...) \
- ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGW(x, ...) \
- ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGE(x, ...) \
- ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
@@ -418,6 +418,9 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false;
+ sp<IConsumerListener> listener;
+ bool callOnFrameDequeued = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameDequeued == true
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
@@ -561,17 +564,18 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou
}
if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
- if (mCore->mConsumerListener != nullptr) {
- mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
- }
+ callOnFrameDequeued = true;
+ bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
}
+
+ listener = mCore->mConsumerListener;
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
- width, height, format, BQ_LAYER_COUNT, usage,
- {mConsumerName.string(), mConsumerName.size()});
+ sp<GraphicBuffer> graphicBuffer =
+ new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,
+ {mConsumerName.c_str(), mConsumerName.size()});
status_t error = graphicBuffer->initCheck();
@@ -581,10 +585,8 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
- if (mCore->mConsumerListener != nullptr) {
- mCore->mConsumerListener->onFrameDequeued(
- mSlots[*outSlot].mGraphicBuffer->getId());
- }
+ callOnFrameDequeued = true;
+ bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
}
mCore->mIsAllocating = false;
@@ -608,6 +610,10 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou
} // Autolock scope
}
+ if (listener != nullptr && callOnFrameDequeued) {
+ listener->onFrameDequeued(bufferId);
+ }
+
if (attachedByConsumer) {
returnFlags |= BUFFER_NEEDS_REALLOCATION;
}
@@ -647,6 +653,8 @@ status_t BufferQueueProducer::detachBuffer(int slot) {
BQ_LOGV("detachBuffer: slot %d", slot);
sp<IConsumerListener> listener;
+ bool callOnFrameDetached = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameDetached is true
{
std::lock_guard<std::mutex> lock(mCore->mMutex);
@@ -684,8 +692,9 @@ status_t BufferQueueProducer::detachBuffer(int slot) {
listener = mCore->mConsumerListener;
auto gb = mSlots[slot].mGraphicBuffer;
- if (listener != nullptr && gb != nullptr) {
- listener->onFrameDetached(gb->getId());
+ if (gb != nullptr) {
+ callOnFrameDetached = true;
+ bufferId = gb->getId();
}
mSlots[slot].mBufferState.detachProducer();
mCore->mActiveBuffers.erase(slot);
@@ -695,6 +704,10 @@ status_t BufferQueueProducer::detachBuffer(int slot) {
VALIDATE_CONSISTENCY();
}
+ if (listener != nullptr && callOnFrameDetached) {
+ listener->onFrameDetached(bufferId);
+ }
+
if (listener != nullptr) {
listener->onBuffersReleased();
}
@@ -1033,8 +1046,7 @@ status_t BufferQueueProducer::queueBuffer(int slot,
output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
output->nextFrameNumber = mCore->mFrameCounter + 1;
- ATRACE_INT(mCore->mConsumerName.string(),
- static_cast<int32_t>(mCore->mQueue.size()));
+ ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
#endif
@@ -1105,57 +1117,70 @@ status_t BufferQueueProducer::queueBuffer(int slot,
status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
ATRACE_CALL();
BQ_LOGV("cancelBuffer: slot %d", slot);
- std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
- return NO_INIT;
- }
+ sp<IConsumerListener> listener;
+ bool callOnFrameCancelled = false;
+ uint64_t bufferId = 0; // Only used if callOnFrameCancelled == true
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("cancelBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
- if (mCore->mSharedBufferMode) {
- BQ_LOGE("cancelBuffer: cannot cancel a buffer in shared buffer mode");
- return BAD_VALUE;
- }
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("cancelBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
- } else if (!mSlots[slot].mBufferState.isDequeued()) {
- BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (fence == nullptr) {
- BQ_LOGE("cancelBuffer: fence is NULL");
- return BAD_VALUE;
- }
+ if (mCore->mSharedBufferMode) {
+ BQ_LOGE("cancelBuffer: cannot cancel a buffer in shared buffer mode");
+ return BAD_VALUE;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
+ BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
+ "(state = %s)",
+ slot, mSlots[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if (fence == nullptr) {
+ BQ_LOGE("cancelBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
- mSlots[slot].mBufferState.cancel();
+ mSlots[slot].mBufferState.cancel();
- // After leaving shared buffer mode, the shared buffer will still be around.
- // Mark it as no longer shared if this operation causes it to be free.
- if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
- mSlots[slot].mBufferState.mShared = false;
- }
+ // After leaving shared buffer mode, the shared buffer will still be around.
+ // Mark it as no longer shared if this operation causes it to be free.
+ if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
+ mSlots[slot].mBufferState.mShared = false;
+ }
- // Don't put the shared buffer on the free list.
- if (!mSlots[slot].mBufferState.isShared()) {
- mCore->mActiveBuffers.erase(slot);
- mCore->mFreeBuffers.push_back(slot);
+ // Don't put the shared buffer on the free list.
+ if (!mSlots[slot].mBufferState.isShared()) {
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeBuffers.push_back(slot);
+ }
+
+ auto gb = mSlots[slot].mGraphicBuffer;
+ if (gb != nullptr) {
+ callOnFrameCancelled = true;
+ bufferId = gb->getId();
+ }
+ mSlots[slot].mFence = fence;
+ mCore->mDequeueCondition.notify_all();
+ listener = mCore->mConsumerListener;
+ VALIDATE_CONSISTENCY();
}
- auto gb = mSlots[slot].mGraphicBuffer;
- if (mCore->mConsumerListener != nullptr && gb != nullptr) {
- mCore->mConsumerListener->onFrameCancelled(gb->getId());
+ if (listener != nullptr && callOnFrameCancelled) {
+ listener->onFrameCancelled(bufferId);
}
- mSlots[slot].mFence = fence;
- mCore->mDequeueCondition.notify_all();
- VALIDATE_CONSISTENCY();
return NO_ERROR;
}
@@ -1461,7 +1486,7 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
allocUsage = usage | mCore->mConsumerUsageBits;
- allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size());
+ allocName.assign(mCore->mConsumerName.c_str(), mCore->mConsumerName.size());
mCore->mIsAllocating = true;
} // Autolock scope
@@ -1563,7 +1588,7 @@ status_t BufferQueueProducer::setGenerationNumber(uint32_t generationNumber) {
String8 BufferQueueProducer::getConsumerName() const {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mCore->mMutex);
- BQ_LOGV("getConsumerName: %s", mConsumerName.string());
+ BQ_LOGV("getConsumerName: %s", mConsumerName.c_str());
return mConsumerName;
}
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 9f91d9d3aa..b625c3f75e 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -41,11 +41,11 @@
#include <utils/Trace.h>
// Macros for including the ConsumerBase name in log messages
-#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
namespace android {
@@ -86,8 +86,10 @@ ConsumerBase::~ConsumerBase() {
// be done by ConsumerBase::onLastStrongRef(), but it's possible for a
// derived class to override that method and not call
// ConsumerBase::onLastStrongRef().
- LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~ConsumerBase was called, but the "
- "consumer is not abandoned!", mName.string());
+ LOG_ALWAYS_FATAL_IF(!mAbandoned,
+ "[%s] ~ConsumerBase was called, but the "
+ "consumer is not abandoned!",
+ mName.c_str());
}
void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) {
@@ -451,7 +453,7 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot,
// them to get an accurate timestamp.
if (currentStatus == incomingStatus) {
char fenceName[32] = {};
- snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
+ snprintf(fenceName, 32, "%.28s:%d", mName.c_str(), slot);
sp<Fence> mergedFence = Fence::merge(
fenceName, mSlots[slot].mFence, fence);
if (!mergedFence.get()) {
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index a62697064b..3031fa11fc 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -23,11 +23,11 @@
#include <gui/BufferItem.h>
#include <utils/Log.h>
-#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
namespace android {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index b3647d6126..d49489c5a8 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -52,11 +52,11 @@
namespace android {
// Macros for including the GLConsumer name in log messages
-#define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
static const struct {
uint32_t width, height;
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 826a418aab..070f6bf532 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1075131
+
chrisforbes@google.com
jreck@google.com
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5bc05ef0d8..dd1fad0f6b 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -59,7 +59,7 @@
#include <private/gui/ComposerServiceAIDL.h>
// This server size should always be smaller than the server cache size
-#define BUFFER_CACHE_MAX_SIZE 64
+#define BUFFER_CACHE_MAX_SIZE 4096
namespace android {
@@ -377,7 +377,6 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
}
auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
if (!callbackFunction) {
- ALOGE("cannot call null callback function, skipping");
continue;
}
std::vector<SurfaceControlStats> surfaceControlStats;
@@ -394,6 +393,11 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
+
+ // More than one transaction may contain the same callback id. Erase the callback from
+ // the map to ensure that it is only called once. This can happen if transactions are
+ // parcelled out of process and applied in both processes.
+ callbacksMap.erase(callbackId);
}
// handle on complete callbacks
@@ -446,7 +450,9 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
surfaceControlStats);
}
+ }
+ for (const auto& transactionStats : listenerStats.transactionStats) {
for (const auto& surfaceStats : transactionStats.surfaceStats) {
// The callbackMap contains the SurfaceControl object, which we need to look up the
// layerId. Since we don't know which callback contains the SurfaceControl, iterate
@@ -1269,7 +1275,7 @@ sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, boo
sp<IBinder> display = nullptr;
binder::Status status =
ComposerServiceAIDL::getComposerService()->createDisplay(std::string(
- displayName.string()),
+ displayName.c_str()),
secure, requestedRefereshRate,
&display);
return status.isOk() ? display : nullptr;
@@ -1302,6 +1308,13 @@ sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId dis
return status.isOk() ? display : nullptr;
}
+std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTransactionInfo(
+ pid_t pid) {
+ std::optional<gui::StalledTransactionInfo> result;
+ ComposerServiceAIDL::getComposerService()->getStalledTransactionInfo(pid, &result);
+ return result;
+}
+
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
mAnimation = true;
}
@@ -2509,38 +2522,41 @@ status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId,
outInfo->secure = ginfo.secure;
outInfo->installOrientation = static_cast<ui::Rotation>(ginfo.installOrientation);
- DeviceProductInfo info;
- std::optional<gui::DeviceProductInfo> dpi = ginfo.deviceProductInfo;
- gui::DeviceProductInfo::ManufactureOrModelDate& date = dpi->manufactureOrModelDate;
- info.name = dpi->name;
- if (dpi->manufacturerPnpId.size() > 0) {
- // copid from PnpId = std::array<char, 4> in ui/DeviceProductInfo.h
- constexpr int kMaxPnpIdSize = 4;
- size_t count = std::max<size_t>(kMaxPnpIdSize, dpi->manufacturerPnpId.size());
- std::copy_n(dpi->manufacturerPnpId.begin(), count, info.manufacturerPnpId.begin());
- }
- if (dpi->relativeAddress.size() > 0) {
- std::copy(dpi->relativeAddress.begin(), dpi->relativeAddress.end(),
- std::back_inserter(info.relativeAddress));
- }
- info.productId = dpi->productId;
- if (date.getTag() == Tag::modelYear) {
- DeviceProductInfo::ModelYear modelYear;
- modelYear.year = static_cast<uint32_t>(date.get<Tag::modelYear>().year);
- info.manufactureOrModelDate = modelYear;
- } else if (date.getTag() == Tag::manufactureYear) {
- DeviceProductInfo::ManufactureYear manufactureYear;
- manufactureYear.year = date.get<Tag::manufactureYear>().modelYear.year;
- info.manufactureOrModelDate = manufactureYear;
- } else if (date.getTag() == Tag::manufactureWeekAndYear) {
- DeviceProductInfo::ManufactureWeekAndYear weekAndYear;
- weekAndYear.year =
- date.get<Tag::manufactureWeekAndYear>().manufactureYear.modelYear.year;
- weekAndYear.week = date.get<Tag::manufactureWeekAndYear>().week;
- info.manufactureOrModelDate = weekAndYear;
- }
+ if (const std::optional<gui::DeviceProductInfo> dpi = ginfo.deviceProductInfo) {
+ DeviceProductInfo info;
+ info.name = dpi->name;
+ if (dpi->manufacturerPnpId.size() > 0) {
+ // copid from PnpId = std::array<char, 4> in ui/DeviceProductInfo.h
+ constexpr int kMaxPnpIdSize = 4;
+ size_t count = std::max<size_t>(kMaxPnpIdSize, dpi->manufacturerPnpId.size());
+ std::copy_n(dpi->manufacturerPnpId.begin(), count, info.manufacturerPnpId.begin());
+ }
+ if (dpi->relativeAddress.size() > 0) {
+ std::copy(dpi->relativeAddress.begin(), dpi->relativeAddress.end(),
+ std::back_inserter(info.relativeAddress));
+ }
+ info.productId = dpi->productId;
+
+ const gui::DeviceProductInfo::ManufactureOrModelDate& date =
+ dpi->manufactureOrModelDate;
+ if (date.getTag() == Tag::modelYear) {
+ DeviceProductInfo::ModelYear modelYear;
+ modelYear.year = static_cast<uint32_t>(date.get<Tag::modelYear>().year);
+ info.manufactureOrModelDate = modelYear;
+ } else if (date.getTag() == Tag::manufactureYear) {
+ DeviceProductInfo::ManufactureYear manufactureYear;
+ manufactureYear.year = date.get<Tag::manufactureYear>().modelYear.year;
+ info.manufactureOrModelDate = manufactureYear;
+ } else if (date.getTag() == Tag::manufactureWeekAndYear) {
+ DeviceProductInfo::ManufactureWeekAndYear weekAndYear;
+ weekAndYear.year =
+ date.get<Tag::manufactureWeekAndYear>().manufactureYear.modelYear.year;
+ weekAndYear.week = date.get<Tag::manufactureWeekAndYear>().week;
+ info.manufactureOrModelDate = weekAndYear;
+ }
- outInfo->deviceProductInfo = info;
+ outInfo->deviceProductInfo = info;
+ }
}
return statusTFromBinderStatus(status);
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 539a1c140e..e1726b7f34 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -46,6 +46,7 @@ import android.gui.LayerDebugInfo;
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ARect;
+import android.gui.StalledTransactionInfo;
import android.gui.StaticDisplayInfo;
import android.gui.WindowInfosListenerInfo;
@@ -230,20 +231,20 @@ interface ISurfaceComposer {
* The subregion can be optionally rotated. It will also be scaled to
* match the size of the output buffer.
*/
- void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener);
+ oneway void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener);
/**
* Capture the specified screen. This requires the READ_FRAME_BUFFER
* permission.
*/
- void captureDisplayById(long displayId, IScreenCaptureListener listener);
+ oneway void captureDisplayById(long displayId, IScreenCaptureListener listener);
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
* This requires READ_FRAME_BUFFER permission. This function will fail if there
* is a secure window on screen
*/
- void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener);
+ oneway void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener);
/**
* Clears the frame statistics for animations.
@@ -280,8 +281,6 @@ interface ISurfaceComposer {
*/
List<LayerDebugInfo> getLayerDebugInfo();
- boolean getColorManagement();
-
/**
* Gets the composition preference of the default data space and default pixel format,
* as well as the wide color gamut data space and wide color gamut pixel format.
@@ -507,4 +506,10 @@ interface ISurfaceComposer {
void removeWindowInfosListener(IWindowInfosListener windowInfosListener);
OverlayProperties getOverlaySupport();
+
+ /**
+ * Returns an instance of StalledTransaction if a transaction from the passed pid has not been
+ * applied in SurfaceFlinger due to an unsignaled fence. Otherwise, null is returned.
+ */
+ @nullable StalledTransactionInfo getStalledTransactionInfo(int pid);
}
diff --git a/libs/ui/include_types/ui/DataspaceUtils.h b/libs/gui/android/gui/StalledTransactionInfo.aidl
index a461cb4e68..e6aa9bd1c7 100644
--- a/libs/ui/include_types/ui/DataspaceUtils.h
+++ b/libs/gui/android/gui/StalledTransactionInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-#pragma once
+package android.gui;
-#include <ui/GraphicTypes.h>
-
-namespace android {
-
-inline bool isHdrDataspace(ui::Dataspace dataspace) {
- const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
-
- return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
-}
-
-} // namespace android \ No newline at end of file
+/** @hide */
+parcelable StalledTransactionInfo {
+ String layerName;
+ long bufferId;
+ long frameNumber;
+} \ No newline at end of file
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 073cc08bff..cd738acde2 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -24,6 +24,7 @@ package {
cc_defaults {
name: "libgui_fuzzer_defaults",
+ defaults: ["android.hardware.power-ndk_shared"],
static_libs: [
"android.hidl.token@1.0-utils",
"libbinder_random_parcel",
@@ -46,7 +47,6 @@ cc_defaults {
"android.hardware.configstore-utils",
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.power-V4-ndk",
"android.hidl.token@1.0",
"libSurfaceFlingerProp",
"libgui",
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 4c7d0562af..a3816873a7 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -110,7 +110,6 @@ public:
(override));
MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override));
MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override));
- MOCK_METHOD(binder::Status, getColorManagement, (bool*), (override));
MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*),
(override));
MOCK_METHOD(binder::Status, getDisplayedContentSamplingAttributes,
@@ -158,6 +157,8 @@ public:
MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
(override));
MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
+ MOCK_METHOD(binder::Status, getStalledTransactionInfo,
+ (int32_t, std::optional<gui::StalledTransactionInfo>*), (override));
};
class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 8d0828d88e..22c2be7bc7 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -34,13 +34,13 @@
#include <mutex>
#include <condition_variable>
-#define ATRACE_BUFFER_INDEX(index) \
- do { \
- if (ATRACE_ENABLED()) { \
- char ___traceBuf[1024]; \
- snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.string(), (index)); \
- android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \
- } \
+#define ATRACE_BUFFER_INDEX(index) \
+ do { \
+ if (ATRACE_ENABLED()) { \
+ char ___traceBuf[1024]; \
+ snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.c_str(), (index)); \
+ android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \
+ } \
} while (false)
namespace android {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 3cf57b11e9..dbcbd3bd62 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -371,6 +371,10 @@ public:
//! Get token for a physical display given its stable ID
static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
+ // Returns StalledTransactionInfo if a transaction from the provided pid has not been applied
+ // due to an unsignaled fence.
+ static std::optional<gui::StalledTransactionInfo> getStalledTransactionInfo(pid_t pid);
+
struct SCHash {
std::size_t operator()(const sp<SurfaceControl>& sc) const {
return std::hash<SurfaceControl *>{}(sc.get());
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
index 004d87574a..32dc88ba84 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -298,7 +298,7 @@ struct TWGraphicBufferProducer : public BASE {
}
Return<void> getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override {
- _hidl_cb(mBase->getConsumerName().string());
+ _hidl_cb(mBase->getConsumerName().c_str());
return Void();
}
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index cd90168784..9618502b6b 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -447,7 +447,8 @@ TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) {
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ // ensure the buffer queue transaction has been committed
+ Transaction().apply(true /* synchronous */);
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -531,7 +532,9 @@ TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ // ensure the buffer queue transaction has been committed
+ Transaction().apply(true /* synchronous */);
+
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -590,7 +593,9 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ // ensure the buffer queue transaction has been committed
+ Transaction().apply(true /* synchronous */);
+
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
@@ -653,7 +658,8 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ // ensure the buffer queue transaction has been committed
+ Transaction().apply(true /* synchronous */);
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -720,7 +726,8 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ // ensure the buffer queue transaction has been committed
+ Transaction().apply(true /* synchronous */);
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
// Verify cropped region is scaled correctly.
@@ -767,7 +774,9 @@ TEST_F(BLASTBufferQueueTest, ScalingModeChanges) {
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
- adapter.waitForCallbacks();
+
+ // ensure the buffer queue transaction has been committed
+ Transaction().apply(true /* synchronous */);
}
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -802,7 +811,8 @@ TEST_F(BLASTBufferQueueTest, ScalingModeChanges) {
NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
0, Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
- adapter.waitForCallbacks();
+ // ensure the buffer queue transaction has been committed
+ Transaction().apply(true /* synchronous */);
}
// capture screen and verify that it is red
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
@@ -1323,43 +1333,6 @@ TEST_F(BLASTBufferQueueTest, QueryNativeWindowQueuesToWindowComposer) {
ASSERT_EQ(queuesToNativeWindow, 1);
}
-// Test a slow producer doesn't hold up a faster producer from the same client. Essentially tests
-// BBQ uses separate transaction queues.
-TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {
- sp<SurfaceControl> bgSurface =
- mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState);
- ASSERT_NE(nullptr, bgSurface.get());
- Transaction t;
- t.setLayerStack(bgSurface, ui::DEFAULT_LAYER_STACK)
- .show(bgSurface)
- .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
- .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
- .apply();
-
- BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
- sp<IGraphicBufferProducer> slowIgbProducer;
- setUpProducer(slowAdapter, slowIgbProducer);
- nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
- queueBuffer(slowIgbProducer, 0 /* r */, 255 /* g */, 0 /* b */, presentTimeDelay);
-
- BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
- sp<IGraphicBufferProducer> fastIgbProducer;
- setUpProducer(fastAdapter, fastIgbProducer);
- uint8_t r = 255;
- uint8_t g = 0;
- uint8_t b = 0;
- queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */);
- fastAdapter.waitForCallbacks();
-
- // capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
-
- ASSERT_NO_FATAL_FAILURE(
- checkScreenCapture(r, g, b,
- {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
-}
-
TEST_F(BLASTBufferQueueTest, TransformHint) {
// Transform hint is provided to BBQ via the surface control passed by WM
mSurfaceControl->setTransformHint(ui::Transform::ROT_90);
@@ -1432,7 +1405,7 @@ public:
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
- adapter.waitForCallbacks();
+ Transaction().apply(true /* synchronous */);
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
switch (tr) {
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index d585881582..0168877478 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -21,8 +21,10 @@
#include "MockConsumer.h"
#include <gui/BufferItem.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/BufferQueue.h>
#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
#include <ui/GraphicBuffer.h>
@@ -37,6 +39,7 @@
#include <gtest/gtest.h>
+#include <future>
#include <thread>
using namespace std::chrono_literals;
@@ -1258,4 +1261,86 @@ TEST_F(BufferQueueTest, TestProducerConnectDisconnect) {
ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
}
+class Latch {
+public:
+ explicit Latch(int expected) : mExpected(expected) {}
+ Latch(const Latch&) = delete;
+ Latch& operator=(const Latch&) = delete;
+
+ void CountDown() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mExpected--;
+ if (mExpected <= 0) {
+ mCV.notify_all();
+ }
+ }
+
+ void Wait() {
+ std::unique_lock<std::mutex> lock(mLock);
+ mCV.wait(lock, [&] { return mExpected == 0; });
+ }
+
+private:
+ int mExpected;
+ std::mutex mLock;
+ std::condition_variable mCV;
+};
+
+struct OneshotOnDequeuedListener final : public BufferItemConsumer::FrameAvailableListener {
+ OneshotOnDequeuedListener(std::function<void()>&& oneshot)
+ : mOneshotRunnable(std::move(oneshot)) {}
+
+ std::function<void()> mOneshotRunnable;
+
+ void run() {
+ if (mOneshotRunnable) {
+ mOneshotRunnable();
+ mOneshotRunnable = nullptr;
+ }
+ }
+
+ void onFrameDequeued(const uint64_t) override { run(); }
+
+ void onFrameAvailable(const BufferItem&) override {}
+};
+
+// See b/270004534
+TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<BufferItemConsumer> bufferConsumer =
+ sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ ASSERT_NE(nullptr, bufferConsumer.get());
+ sp<Surface> surface = sp<Surface>::make(producer);
+ native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
+ native_window_set_buffers_dimensions(surface.get(), 100, 100);
+
+ Latch triggerDisconnect(1);
+ Latch resumeCallback(1);
+ auto luckyListener = sp<OneshotOnDequeuedListener>::make([&]() {
+ triggerDisconnect.CountDown();
+ resumeCallback.Wait();
+ });
+ bufferConsumer->setFrameAvailableListener(luckyListener);
+
+ std::future<void> disconnecter = std::async(std::launch::async, [&]() {
+ triggerDisconnect.Wait();
+ luckyListener = nullptr;
+ bufferConsumer = nullptr;
+ resumeCallback.CountDown();
+ });
+
+ std::future<void> render = std::async(std::launch::async, [=]() {
+ ANativeWindow_Buffer buffer;
+ surface->lock(&buffer, nullptr);
+ surface->unlockAndPost();
+ });
+
+ ASSERT_EQ(std::future_status::ready, render.wait_for(1s));
+ EXPECT_EQ(nullptr, luckyListener.get());
+ EXPECT_EQ(nullptr, bufferConsumer.get());
+}
+
} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 4d5bd5b3fa..662e9fe74a 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -24,6 +24,7 @@
#include <memory>
+#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/keycodes.h>
#include <android/native_window.h>
@@ -74,6 +75,26 @@ sp<IInputFlinger> getInputFlinger() {
static const int LAYER_BASE = INT32_MAX - 10;
static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
+class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener {
+public:
+ binder::Status onWindowInfosReported() override {
+ std::lock_guard<std::mutex> lock{mMutex};
+ mWindowInfosReported = true;
+ mConditionVariable.notify_one();
+ return binder::Status::ok();
+ }
+
+ void wait() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ mConditionVariable.wait(lock, [&] { return mWindowInfosReported; });
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mConditionVariable;
+ bool mWindowInfosReported{false};
+};
+
class InputSurface {
public:
InputSurface(const sp<SurfaceControl> &sc, int width, int height, bool noInputChannel = false) {
@@ -264,7 +285,10 @@ public:
t.setPosition(mSurfaceControl, x, y);
t.setCrop(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
- t.apply(true);
+ auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make();
+ t.addWindowInfosReportedListener(reportedListener);
+ t.apply();
+ reportedListener->wait();
}
void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index afeea42546..ae79e5bace 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -177,7 +177,7 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config,
while ((err = glGetError()) != GL_NO_ERROR) {
msg += String8::format(", %#x", err);
}
- return ::testing::AssertionFailure(::testing::Message(msg.string()));
+ return ::testing::AssertionFailure(::testing::Message(msg.c_str()));
}
if (r >= 0 && abs(r - int(pixel[0])) > tolerance) {
msg += String8::format("r(%d isn't %d)", pixel[0], r);
@@ -201,7 +201,7 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config,
msg += String8::format("a(%d isn't %d)", pixel[3], a);
}
if (!msg.isEmpty()) {
- return ::testing::AssertionFailure(::testing::Message(msg.string()));
+ return ::testing::AssertionFailure(::testing::Message(msg.c_str()));
} else {
return ::testing::AssertionSuccess();
}
@@ -236,8 +236,8 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config,
msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]",
r1.left, r1.top, r1.right, r1.bottom,
r2.left, r2.top, r2.right, r2.bottom);
- fprintf(stderr, "assertRectEq: %s\n", msg.string());
- return ::testing::AssertionFailure(::testing::Message(msg.string()));
+ fprintf(stderr, "assertRectEq: %s\n", msg.c_str());
+ return ::testing::AssertionFailure(::testing::Message(msg.c_str()));
} else {
return ::testing::AssertionSuccess();
}
diff --git a/libs/gui/tests/OWNERS b/libs/gui/tests/OWNERS
index 156efdb883..48cd30d39d 100644
--- a/libs/gui/tests/OWNERS
+++ b/libs/gui/tests/OWNERS
@@ -1,3 +1,6 @@
# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > Surfaces
# Bug component: 316245 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp
# Buganizer template url: https://b.corp.google.com/issues/new?component=316245&template=1018194 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp
+
+# Android > Android OS & Apps > graphics > Core Graphics Stack (CoGS)
+# Bug component: 1075130
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index f34561f668..ccd0e59616 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -59,7 +59,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
for (int i = 0; i < 4; i++) {
- SCOPED_TRACE(String8::format("frame %d", i).string());
+ SCOPED_TRACE(String8::format("frame %d", i).c_str());
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
&anb));
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index e2b4f3d035..f76c0be265 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -147,8 +147,9 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
for (int i = 0; i < 5; i++) {
const android_native_rect_t& crop(crops[i]);
- SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }",
- crop.left, crop.top, crop.right, crop.bottom).string());
+ SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left, crop.top,
+ crop.right, crop.bottom)
+ .c_str());
ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop));
@@ -308,7 +309,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) {
mFW->waitForFrame();
for (int i = 0; i < numFrames; i++) {
- SCOPED_TRACE(String8::format("frame %d", i).string());
+ SCOPED_TRACE(String8::format("frame %d", i).c_str());
// We must wait for each frame to come in because if we ever do an
// updateTexImage call that doesn't consume a newly available buffer
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 567604dbec..e89998ff3d 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -415,7 +415,7 @@ TEST_F(SurfaceTest, GetConsumerName) {
sp<ANativeWindow> window(surface);
native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
- EXPECT_STREQ("TestConsumer", surface->getConsumerName().string());
+ EXPECT_STREQ("TestConsumer", surface->getConsumerName().c_str());
}
TEST_F(SurfaceTest, GetWideColorSupport) {
@@ -879,10 +879,6 @@ public:
return binder::Status::ok();
}
- binder::Status getColorManagement(bool* /*outGetColorManagement*/) override {
- return binder::Status::ok();
- }
-
binder::Status getCompositionPreference(gui::CompositionPreference* /*outPref*/) override {
return binder::Status::ok();
}
@@ -1016,6 +1012,11 @@ public:
return binder::Status::ok();
}
+ binder::Status getStalledTransactionInfo(
+ int32_t /*pid*/, std::optional<gui::StalledTransactionInfo>* /*result*/) override {
+ return binder::Status::ok();
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index bade68629d..c218e1e858 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -18,6 +18,7 @@
#include <linux/input-event-codes.h>
#include <linux/input.h>
+#include <strings.h>
#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
@@ -523,6 +524,14 @@ std::string getLabel(const label* labels, int value) {
return labels->name != nullptr ? labels->name : std::to_string(value);
}
+std::optional<int> getValue(const label* labels, const char* searchLabel) {
+ if (labels == nullptr) return {};
+ while (labels->name != nullptr && ::strcasecmp(labels->name, searchLabel) != 0) {
+ labels++;
+ }
+ return labels->name != nullptr ? std::make_optional(labels->value) : std::nullopt;
+}
+
const label* getCodeLabelsForType(int32_t type) {
switch (type) {
case EV_SYN:
@@ -556,7 +565,7 @@ const label* getValueLabelsForTypeAndCode(int32_t type, int32_t code) {
if (type == EV_KEY) {
return ev_key_value_labels;
}
- if (type == EV_MSC && code == ABS_MT_TOOL_TYPE) {
+ if (type == EV_ABS && code == ABS_MT_TOOL_TYPE) {
return mt_tool_labels;
}
return nullptr;
@@ -572,4 +581,17 @@ EvdevEventLabel InputEventLookup::getLinuxEvdevLabel(int32_t type, int32_t code,
};
}
+std::optional<int> InputEventLookup::getLinuxEvdevEventTypeByLabel(const char* label) {
+ return getValue(ev_labels, label);
+}
+
+std::optional<int> InputEventLookup::getLinuxEvdevEventCodeByLabel(int32_t type,
+ const char* label) {
+ return getValue(getCodeLabelsForType(type), label);
+}
+
+std::optional<int> InputEventLookup::getLinuxEvdevInputPropByLabel(const char* label) {
+ return getValue(input_prop_labels, label);
+}
+
} // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d9b7700f4f..3446540ccf 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -75,10 +75,20 @@ bool debugTransportPublisher() {
/**
* Log debug messages about touch event resampling.
- * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG" (requires restart)
+ *
+ * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
*/
-const bool DEBUG_RESAMPLING =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+bool debugResampling() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_TRANSPORT_RESAMPLING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
+ ANDROID_LOG_INFO);
+ return DEBUG_TRANSPORT_RESAMPLING;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+}
} // namespace
@@ -1158,7 +1168,7 @@ void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) {
state.recentCoordinatesAreIdentical(id)) {
PointerCoords& msgCoords = msg.body.motion.pointers[i].coords;
const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
- ALOGD_IF(DEBUG_RESAMPLING, "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
+ ALOGD_IF(debugResampling(), "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(),
msgCoords.getY());
msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
@@ -1181,13 +1191,13 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
ssize_t index = findTouchState(event->getDeviceId(), event->getSource());
if (index < 0) {
- ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, no touch state for device.");
+ ALOGD_IF(debugResampling(), "Not resampled, no touch state for device.");
return;
}
TouchState& touchState = mTouchStates[index];
if (touchState.historySize < 1) {
- ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, no history for device.");
+ ALOGD_IF(debugResampling(), "Not resampled, no history for device.");
return;
}
@@ -1197,7 +1207,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
for (size_t i = 0; i < pointerCount; i++) {
uint32_t id = event->getPointerId(i);
if (!current->idBits.hasBit(id)) {
- ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, missing id %d", id);
+ ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id);
return;
}
}
@@ -1213,7 +1223,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
other = &future;
nsecs_t delta = future.eventTime - current->eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
- ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too small: %" PRId64 " ns.",
+ ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.",
delta);
return;
}
@@ -1224,17 +1234,17 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
other = touchState.getHistory(1);
nsecs_t delta = current->eventTime - other->eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
- ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too small: %" PRId64 " ns.",
+ ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.",
delta);
return;
} else if (delta > RESAMPLE_MAX_DELTA) {
- ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too large: %" PRId64 " ns.",
+ ALOGD_IF(debugResampling(), "Not resampled, delta time is too large: %" PRId64 " ns.",
delta);
return;
}
nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION);
if (sampleTime > maxPredict) {
- ALOGD_IF(DEBUG_RESAMPLING,
+ ALOGD_IF(debugResampling(),
"Sample time is too far in the future, adjusting prediction "
"from %" PRId64 " to %" PRId64 " ns.",
sampleTime - current->eventTime, maxPredict - current->eventTime);
@@ -1242,7 +1252,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
}
alpha = float(current->eventTime - sampleTime) / delta;
} else {
- ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, insufficient data.");
+ ALOGD_IF(debugResampling(), "Not resampled, insufficient data.");
return;
}
@@ -1284,13 +1294,13 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
lerp(currentCoords.getY(), otherCoords.getY(), alpha));
resampledCoords.isResampled = true;
- ALOGD_IF(DEBUG_RESAMPLING,
+ ALOGD_IF(debugResampling(),
"[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
"other (%0.3f, %0.3f), alpha %0.3f",
id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha);
} else {
- ALOGD_IF(DEBUG_RESAMPLING, "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id,
+ ALOGD_IF(debugResampling(), "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id,
resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
currentCoords.getY());
}
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index 341eb6f920..6c602e031d 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -31,7 +31,7 @@ namespace android {
// --- InputVerifier ---
InputVerifier::InputVerifier(const std::string& name)
- : mVerifier(android::input::verifier::create(name)){};
+ : mVerifier(android::input::verifier::create(rust::String::lossy(name))){};
Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
uint32_t pointerCount,
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 12c9e533c3..d571917ff9 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -140,7 +140,7 @@ status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format) {
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
+ tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
#endif
if (status != OK) {
ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str());
@@ -297,7 +297,7 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t
if (!findKey(ch, &keyCode, &metaState)) {
#if DEBUG_MAPPING
ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
- deviceId, toString(chars, numChars).string(), ch);
+ deviceId, toString(chars, numChars).c_str(), ch);
#endif
return false;
}
@@ -309,8 +309,8 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t
addMetaKeys(outEvents, deviceId, metaState, false, now, &currentMetaState);
}
#if DEBUG_MAPPING
- ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
- deviceId, toString(chars, numChars).string(), int32_t(outEvents.size()));
+ ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", deviceId,
+ toString(chars, numChars).c_str(), int32_t(outEvents.size()));
for (size_t i = 0; i < outEvents.size(); i++) {
ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.",
outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
@@ -756,8 +756,8 @@ KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Form
status_t KeyCharacterMap::Parser::parse() {
while (!mTokenizer->isEof()) {
#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
#endif
mTokenizer->skipDelimiters(WHITESPACE);
@@ -779,8 +779,8 @@ status_t KeyCharacterMap::Parser::parse() {
status_t status = parseKey();
if (status) return status;
} else {
- ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
+ ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(),
+ keywordToken.c_str());
return BAD_VALUE;
}
break;
@@ -795,10 +795,9 @@ status_t KeyCharacterMap::Parser::parse() {
mTokenizer->skipDelimiters(WHITESPACE);
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
- ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
- return BAD_VALUE;
+ ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
+ mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str());
+ return BAD_VALUE;
}
}
@@ -807,27 +806,27 @@ status_t KeyCharacterMap::Parser::parse() {
if (mState != STATE_TOP) {
ALOGE("%s: Unterminated key description at end of file.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (mMap->mType == KeyboardType::UNKNOWN) {
ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (mFormat == Format::BASE) {
if (mMap->mType == KeyboardType::OVERLAY) {
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
} else if (mFormat == Format::OVERLAY) {
if (mMap->mType != KeyboardType::OVERLAY) {
ALOGE("%s: Overlay keyboard layout missing required keyboard "
- "'type OVERLAY' declaration.",
- mTokenizer->getLocation().string());
+ "'type OVERLAY' declaration.",
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
}
@@ -837,8 +836,7 @@ status_t KeyCharacterMap::Parser::parse() {
status_t KeyCharacterMap::Parser::parseType() {
if (mMap->mType != KeyboardType::UNKNOWN) {
- ALOGE("%s: Duplicate keyboard 'type' declaration.",
- mTokenizer->getLocation().string());
+ ALOGE("%s: Duplicate keyboard 'type' declaration.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -860,8 +858,8 @@ status_t KeyCharacterMap::Parser::parseType() {
} else if (typeToken == "OVERLAY") {
type = KeyboardType::OVERLAY;
} else {
- ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
- typeToken.string());
+ ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().c_str(),
+ typeToken.c_str());
return BAD_VALUE;
}
@@ -878,8 +876,8 @@ status_t KeyCharacterMap::Parser::parseMap() {
mTokenizer->skipDelimiters(WHITESPACE);
return parseMapKey();
}
- ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
+ ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().c_str(),
+ keywordToken.c_str());
return BAD_VALUE;
}
@@ -893,26 +891,26 @@ status_t KeyCharacterMap::Parser::parseMapKey() {
}
char* end;
- int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
+ int32_t code = int32_t(strtol(codeToken.c_str(), &end, 0));
if (*end) {
- ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
const auto it = map.find(code);
if (it != map.end()) {
- ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str());
if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
+ ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ keyCodeToken.c_str());
return BAD_VALUE;
}
@@ -926,23 +924,23 @@ status_t KeyCharacterMap::Parser::parseMapKey() {
status_t KeyCharacterMap::Parser::parseKey() {
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str());
if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
+ ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ keyCodeToken.c_str());
return BAD_VALUE;
}
if (mMap->mKeys.find(*keyCode) != mMap->mKeys.end()) {
- ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
+ ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().c_str(),
+ keyCodeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
if (openBraceToken != "{") {
- ALOGE("%s: Expected '{' after key code label, got '%s'.",
- mTokenizer->getLocation().string(), openBraceToken.string());
+ ALOGE("%s: Expected '{' after key code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ openBraceToken.c_str());
return BAD_VALUE;
}
@@ -971,10 +969,10 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
properties.emplace_back(PROPERTY_NUMBER);
} else {
int32_t metaState;
- status_t status = parseModifier(token.string(), &metaState);
+ status_t status = parseModifier(token.c_str(), &metaState);
if (status) {
ALOGE("%s: Expected a property name or modifier, got '%s'.",
- mTokenizer->getLocation().string(), token.string());
+ mTokenizer->getLocation().c_str(), token.c_str());
return status;
}
properties.emplace_back(PROPERTY_META, metaState);
@@ -992,8 +990,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
}
}
- ALOGE("%s: Expected ',' or ':' after property name.",
- mTokenizer->getLocation().string());
+ ALOGE("%s: Expected ',' or ':' after property name.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -1011,18 +1008,17 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
char16_t character;
status_t status = parseCharacterLiteral(&character);
if (status || !character) {
- ALOGE("%s: Invalid character literal for key.",
- mTokenizer->getLocation().string());
+ ALOGE("%s: Invalid character literal for key.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (haveCharacter) {
ALOGE("%s: Cannot combine multiple character literals or 'none'.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (haveReplacement) {
ALOGE("%s: Cannot combine character literal with replace action.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
behavior.character = character;
@@ -1032,28 +1028,27 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
if (token == "none") {
if (haveCharacter) {
ALOGE("%s: Cannot combine multiple character literals or 'none'.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (haveReplacement) {
ALOGE("%s: Cannot combine 'none' with replace action.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
haveCharacter = true;
} else if (token == "fallback") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str());
if (!keyCode) {
ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
- mTokenizer->getLocation().string(),
- token.string());
+ mTokenizer->getLocation().c_str(), token.c_str());
return BAD_VALUE;
}
if (haveFallback || haveReplacement) {
ALOGE("%s: Cannot combine multiple fallback/replacement key codes.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
behavior.fallbackKeyCode = *keyCode;
@@ -1061,29 +1056,27 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
} else if (token == "replace") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str());
if (!keyCode) {
ALOGE("%s: Invalid key code label for replace, got '%s'.",
- mTokenizer->getLocation().string(),
- token.string());
+ mTokenizer->getLocation().c_str(), token.c_str());
return BAD_VALUE;
}
if (haveCharacter) {
ALOGE("%s: Cannot combine character literal with replace action.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (haveFallback || haveReplacement) {
ALOGE("%s: Cannot combine multiple fallback/replacement key codes.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
behavior.replacementKeyCode = *keyCode;
haveReplacement = true;
} else {
- ALOGE("%s: Expected a key behavior after ':'.",
- mTokenizer->getLocation().string());
+ ALOGE("%s: Expected a key behavior after ':'.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
}
@@ -1096,7 +1089,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
switch (property.property) {
case PROPERTY_LABEL:
if (key.label) {
- ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().string());
+ ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
key.label = behavior.character;
@@ -1106,7 +1099,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
break;
case PROPERTY_NUMBER:
if (key.number) {
- ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().string());
+ ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
key.number = behavior.character;
@@ -1118,7 +1111,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() {
for (const Behavior& b : key.behaviors) {
if (b.metaState == property.metaState) {
ALOGE("%s: Duplicate key behavior for modifier.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
}
@@ -1185,8 +1178,8 @@ status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_
return BAD_VALUE;
}
if (combinedMeta & metaState) {
- ALOGE("%s: Duplicate modifier combination '%s'.",
- mTokenizer->getLocation().string(), token.c_str());
+ ALOGE("%s: Duplicate modifier combination '%s'.", mTokenizer->getLocation().c_str(),
+ token.c_str());
return BAD_VALUE;
}
@@ -1259,7 +1252,7 @@ status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter)
}
Error:
- ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
+ ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index a194513953..ddc9ea457e 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -177,7 +177,7 @@ base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokeni
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ tokenizer->getFilename().c_str(), tokenizer->getLineNumber(),
elapsedTime / 1000000.0);
#endif
if (!status) {
@@ -306,8 +306,8 @@ KeyLayoutMap::Parser::~Parser() {
status_t KeyLayoutMap::Parser::parse() {
while (!mTokenizer->isEof()) {
- ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
mTokenizer->skipDelimiters(WHITESPACE);
@@ -334,16 +334,15 @@ status_t KeyLayoutMap::Parser::parse() {
status_t status = parseRequiredKernelConfig();
if (status) return status;
} else {
- ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
+ ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(),
+ keywordToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str());
return BAD_VALUE;
}
}
@@ -362,26 +361,26 @@ status_t KeyLayoutMap::Parser::parseKey() {
codeToken = mTokenizer->nextToken(WHITESPACE);
}
- std::optional<int> code = parseInt(codeToken.string());
+ std::optional<int> code = parseInt(codeToken.c_str());
if (!code) {
- ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
std::unordered_map<int32_t, Key>& map =
mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
if (map.find(*code) != map.end()) {
- ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str());
if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
+ ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ keyCodeToken.c_str());
return BAD_VALUE;
}
@@ -391,15 +390,15 @@ status_t KeyLayoutMap::Parser::parseKey() {
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
+ std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.c_str());
if (!flag) {
- ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
- flagToken.string());
+ ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().c_str(),
+ flagToken.c_str());
return BAD_VALUE;
}
if (flags & *flag) {
- ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
- flagToken.string());
+ ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().c_str(),
+ flagToken.c_str());
return BAD_VALUE;
}
flags |= *flag;
@@ -417,15 +416,15 @@ status_t KeyLayoutMap::Parser::parseKey() {
status_t KeyLayoutMap::Parser::parseAxis() {
String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> scanCode = parseInt(scanCodeToken.string());
+ std::optional<int> scanCode = parseInt(scanCodeToken.c_str());
if (!scanCode) {
- ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
- scanCodeToken.string());
+ ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().c_str(),
+ scanCodeToken.c_str());
return BAD_VALUE;
}
if (mMap->mAxes.find(*scanCode) != mMap->mAxes.end()) {
- ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
- scanCodeToken.string());
+ ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().c_str(),
+ scanCodeToken.c_str());
return BAD_VALUE;
}
@@ -438,10 +437,10 @@ status_t KeyLayoutMap::Parser::parseAxis() {
mTokenizer->skipDelimiters(WHITESPACE);
String8 axisToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.string());
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.c_str());
if (!axis) {
ALOGE("%s: Expected inverted axis label, got '%s'.",
- mTokenizer->getLocation().string(), axisToken.string());
+ mTokenizer->getLocation().c_str(), axisToken.c_str());
return BAD_VALUE;
}
axisInfo.axis = *axis;
@@ -450,38 +449,38 @@ status_t KeyLayoutMap::Parser::parseAxis() {
mTokenizer->skipDelimiters(WHITESPACE);
String8 splitToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> splitValue = parseInt(splitToken.string());
+ std::optional<int> splitValue = parseInt(splitToken.c_str());
if (!splitValue) {
ALOGE("%s: Expected split value, got '%s'.",
- mTokenizer->getLocation().string(), splitToken.string());
+ mTokenizer->getLocation().c_str(), splitToken.c_str());
return BAD_VALUE;
}
axisInfo.splitValue = *splitValue;
mTokenizer->skipDelimiters(WHITESPACE);
String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.c_str());
if (!axis) {
ALOGE("%s: Expected low axis label, got '%s'.",
- mTokenizer->getLocation().string(), lowAxisToken.string());
+ mTokenizer->getLocation().c_str(), lowAxisToken.c_str());
return BAD_VALUE;
}
axisInfo.axis = *axis;
mTokenizer->skipDelimiters(WHITESPACE);
String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
+ std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.c_str());
if (!highAxis) {
ALOGE("%s: Expected high axis label, got '%s'.",
- mTokenizer->getLocation().string(), highAxisToken.string());
+ mTokenizer->getLocation().c_str(), highAxisToken.c_str());
return BAD_VALUE;
}
axisInfo.highAxis = *highAxis;
} else {
- std::optional<int> axis = InputEventLookup::getAxisByLabel(token.string());
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(token.c_str());
if (!axis) {
ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
- mTokenizer->getLocation().string(), token.string());
+ mTokenizer->getLocation().c_str(), token.c_str());
return BAD_VALUE;
}
axisInfo.axis = *axis;
@@ -496,16 +495,16 @@ status_t KeyLayoutMap::Parser::parseAxis() {
if (keywordToken == "flat") {
mTokenizer->skipDelimiters(WHITESPACE);
String8 flatToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> flatOverride = parseInt(flatToken.string());
+ std::optional<int> flatOverride = parseInt(flatToken.c_str());
if (!flatOverride) {
ALOGE("%s: Expected flat value, got '%s'.",
- mTokenizer->getLocation().string(), flatToken.string());
+ mTokenizer->getLocation().c_str(), flatToken.c_str());
return BAD_VALUE;
}
axisInfo.flatOverride = *flatOverride;
} else {
- ALOGE("%s: Expected keyword 'flat', got '%s'.",
- mTokenizer->getLocation().string(), keywordToken.string());
+ ALOGE("%s: Expected keyword 'flat', got '%s'.", mTokenizer->getLocation().c_str(),
+ keywordToken.c_str());
return BAD_VALUE;
}
}
@@ -527,27 +526,27 @@ status_t KeyLayoutMap::Parser::parseLed() {
mTokenizer->skipDelimiters(WHITESPACE);
codeToken = mTokenizer->nextToken(WHITESPACE);
}
- std::optional<int> code = parseInt(codeToken.string());
+ std::optional<int> code = parseInt(codeToken.c_str());
if (!code) {
- ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
std::unordered_map<int32_t, Led>& map =
mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
if (map.find(*code) != map.end()) {
- ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
+ std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.c_str());
if (!ledCode) {
- ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
- ledCodeToken.string());
+ ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ ledCodeToken.c_str());
return BAD_VALUE;
}
@@ -569,7 +568,7 @@ static std::optional<InputDeviceSensorType> getSensorType(const char* token) {
}
static std::optional<int32_t> getSensorDataIndex(String8 token) {
- std::string tokenStr(token.string());
+ std::string tokenStr(token.c_str());
if (tokenStr == "X") {
return 0;
} else if (tokenStr == "Y") {
@@ -594,26 +593,26 @@ static std::optional<int32_t> getSensorDataIndex(String8 token) {
// sensor 0x05 GYROSCOPE Z
status_t KeyLayoutMap::Parser::parseSensor() {
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> code = parseInt(codeToken.string());
+ std::optional<int> code = parseInt(codeToken.c_str());
if (!code) {
- ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(),
- "abs code", codeToken.string());
+ ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().c_str(),
+ "abs code", codeToken.c_str());
return BAD_VALUE;
}
std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode;
if (map.find(*code) != map.end()) {
- ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(),
- "abs code", codeToken.string());
+ ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().c_str(),
+ "abs code", codeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 sensorTypeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.string());
+ std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.c_str());
if (!typeOpt) {
- ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().string(),
- sensorTypeToken.string());
+ ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ sensorTypeToken.c_str());
return BAD_VALUE;
}
InputDeviceSensorType sensorType = typeOpt.value();
@@ -621,8 +620,8 @@ status_t KeyLayoutMap::Parser::parseSensor() {
String8 sensorDataIndexToken = mTokenizer->nextToken(WHITESPACE);
std::optional<int32_t> indexOpt = getSensorDataIndex(sensorDataIndexToken);
if (!indexOpt) {
- ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().string(),
- sensorDataIndexToken.string());
+ ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().c_str(),
+ sensorDataIndexToken.c_str());
return BAD_VALUE;
}
int32_t sensorDataIndex = indexOpt.value();
@@ -643,12 +642,12 @@ status_t KeyLayoutMap::Parser::parseSensor() {
// requires_kernel_config CONFIG_HID_PLAYSTATION
status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() {
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
- std::string configName = codeToken.string();
+ std::string configName = codeToken.c_str();
const auto result = mMap->mRequiredKernelConfigs.emplace(configName);
if (!result.second) {
ALOGE("%s: Duplicate entry for required kernel config %s.",
- mTokenizer->getLocation().string(), configName.c_str());
+ mTokenizer->getLocation().c_str(), configName.c_str());
return BAD_VALUE;
}
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 80a8d03ab9..0961a9d975 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -138,7 +138,8 @@ android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
// Pass input event to the MetricsManager.
if (!mMetricsManager) {
mMetricsManager =
- std::make_optional<MotionPredictorMetricsManager>(mModel->predictionInterval(),
+ std::make_optional<MotionPredictorMetricsManager>(mModel->config()
+ .predictionInterval,
mModel->outputLength());
}
mMetricsManager->onRecord(event);
@@ -184,8 +185,18 @@ std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) {
const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) {
- // TODO(b/266747654): Stop predictions if confidence and/or predicted pressure are below
- // some thresholds.
+ if (predictedR[i] < mModel->config().distanceNoiseFloor) {
+ // Stop predicting when the predicted output is below the model's noise floor.
+ //
+ // We assume that all subsequent predictions in the batch are unreliable because later
+ // predictions are conditional on earlier predictions, and a state of noise is not a
+ // good basis for prediction.
+ //
+ // The UX trade-off is that this potentially sacrifices some predictions when the input
+ // device starts to speed up, but avoids producing noisy predictions as it slows down.
+ break;
+ }
+ // TODO(b/266747654): Stop predictions if confidence is < some threshold.
const TfLiteMotionPredictorSample::Point predictedPoint =
convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
@@ -197,7 +208,7 @@ std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) {
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, predictedPoint.y);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
- predictionTime += mModel->predictionInterval();
+ predictionTime += mModel->config().predictionInterval;
if (i == 0) {
hasPredictions = true;
prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 548f894d22..315f5a6d4f 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -163,8 +163,8 @@ PropertyMap::Parser::~Parser() {}
status_t PropertyMap::Parser::parse() {
while (!mTokenizer->isEof()) {
#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
#endif
mTokenizer->skipDelimiters(WHITESPACE);
@@ -172,7 +172,7 @@ status_t PropertyMap::Parser::parse() {
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
if (keyToken.isEmpty()) {
- ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+ ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -180,7 +180,7 @@ status_t PropertyMap::Parser::parse() {
if (mTokenizer->nextChar() != '=') {
ALOGE("%s: Expected '=' between property key and value.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -189,20 +189,20 @@ status_t PropertyMap::Parser::parse() {
String8 valueToken = mTokenizer->nextToken(WHITESPACE);
if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
if (!mTokenizer->isEol()) {
- ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
return BAD_VALUE;
}
if (mMap->hasProperty(keyToken.string())) {
ALOGE("%s: Duplicate property value for key '%s'.",
- mTokenizer->getLocation().string(), keyToken.string());
+ mTokenizer->getLocation().c_str(), keyToken.c_str());
return BAD_VALUE;
}
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 9f4aaa8337..5984b4d3b9 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -100,6 +100,16 @@ int64_t parseXMLInt64(const tinyxml2::XMLElement& configRoot, const char* elemen
return value;
}
+float parseXMLFloat(const tinyxml2::XMLElement& configRoot, const char* elementName) {
+ const tinyxml2::XMLElement* element = configRoot.FirstChildElement(elementName);
+ LOG_ALWAYS_FATAL_IF(!element, "Could not find '%s' element", elementName);
+
+ float value = 0;
+ LOG_ALWAYS_FATAL_IF(element->QueryFloatText(&value) != tinyxml2::XML_SUCCESS,
+ "Failed to parse %s: %s", elementName, element->GetText());
+ return value;
+}
+
// A TFLite ErrorReporter that logs to logcat.
class LoggingErrorReporter : public tflite::ErrorReporter {
public:
@@ -152,6 +162,7 @@ std::unique_ptr<tflite::OpResolver> createOpResolver() {
::tflite::ops::builtin::Register_CONCATENATION());
resolver->AddBuiltin(::tflite::BuiltinOperator_FULLY_CONNECTED,
::tflite::ops::builtin::Register_FULLY_CONNECTED());
+ resolver->AddBuiltin(::tflite::BuiltinOperator_GELU, ::tflite::ops::builtin::Register_GELU());
return resolver;
}
@@ -208,13 +219,7 @@ void TfLiteMotionPredictorBuffers::pushSample(int64_t timestamp,
float phi = 0;
float orientation = 0;
- // Ignore the sample if there is no movement. These samples can occur when there's change to a
- // property other than the coordinates and pollute the input to the model.
- if (r == 0) {
- return;
- }
-
- if (!mAxisFrom) { // Second point.
+ if (!mAxisFrom && r > 0) { // Second point.
// We can only determine the distance from the first point, and not any
// angle. However, if the second point forms an axis, the orientation can
// be transformed relative to that axis.
@@ -235,8 +240,10 @@ void TfLiteMotionPredictorBuffers::pushSample(int64_t timestamp,
}
// Update the axis for the next point.
- mAxisFrom = mAxisTo;
- mAxisTo = sample;
+ if (r > 0) {
+ mAxisFrom = mAxisTo;
+ mAxisTo = sample;
+ }
// Push the current sample onto the end of the input buffers.
mInputR.pushBack(r);
@@ -272,15 +279,18 @@ std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create()
// Parse configuration file.
const tinyxml2::XMLElement* configRoot = configDocument.FirstChildElement("motion-predictor");
LOG_ALWAYS_FATAL_IF(!configRoot);
- const nsecs_t predictionInterval = parseXMLInt64(*configRoot, "prediction-interval");
+ Config config{
+ .predictionInterval = parseXMLInt64(*configRoot, "prediction-interval"),
+ .distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"),
+ };
return std::unique_ptr<TfLiteMotionPredictorModel>(
- new TfLiteMotionPredictorModel(std::move(modelBuffer), predictionInterval));
+ new TfLiteMotionPredictorModel(std::move(modelBuffer), std::move(config)));
}
TfLiteMotionPredictorModel::TfLiteMotionPredictorModel(
- std::unique_ptr<android::base::MappedFile> model, nsecs_t predictionInterval)
- : mFlatBuffer(std::move(model)), mPredictionInterval(predictionInterval) {
+ std::unique_ptr<android::base::MappedFile> model, Config config)
+ : mFlatBuffer(std::move(model)), mConfig(std::move(config)) {
CHECK(mFlatBuffer);
mErrorReporter = std::make_unique<LoggingErrorReporter>();
mModel = tflite::FlatBufferModel::VerifyAndBuildFromBuffer(mFlatBuffer->data(),
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 8704eee73d..116b778608 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "VelocityTracker"
#include <android-base/logging.h>
+#include <ftl/enum.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
@@ -148,27 +149,19 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r
VelocityTracker::VelocityTracker(const Strategy strategy)
: mLastEventTime(0), mCurrentPointerIdBits(0), mOverrideStrategy(strategy) {}
-VelocityTracker::~VelocityTracker() {
-}
-
bool VelocityTracker::isAxisSupported(int32_t axis) {
return DEFAULT_STRATEGY_BY_AXIS.find(axis) != DEFAULT_STRATEGY_BY_AXIS.end();
}
void VelocityTracker::configureStrategy(int32_t axis) {
const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end();
-
- std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
- if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) {
- createdStrategy = createStrategy(mOverrideStrategy, /*deltaValues=*/isDifferentialAxis);
+ if (isDifferentialAxis || mOverrideStrategy == VelocityTracker::Strategy::DEFAULT) {
+ // Do not allow overrides of strategies for differential axes, for now.
+ mConfiguredStrategies[axis] = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
+ /*deltaValues=*/isDifferentialAxis);
} else {
- createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
- /*deltaValues=*/isDifferentialAxis);
+ mConfiguredStrategies[axis] = createStrategy(mOverrideStrategy, /*deltaValues=*/false);
}
-
- LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
- "Could not create velocity tracker strategy for axis '%" PRId32 "'!", axis);
- mConfiguredStrategies[axis] = std::move(createdStrategy);
}
std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
@@ -216,6 +209,9 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
default:
break;
}
+ LOG(FATAL) << "Invalid strategy: " << ftl::enum_string(strategy)
+ << ", deltaValues = " << deltaValues;
+
return nullptr;
}
@@ -272,12 +268,10 @@ void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t
mConfiguredStrategies[axis]->addMovement(eventTime, pointerId, position);
if (DEBUG_VELOCITY) {
- ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", pointerId=%" PRId32
- ", activePointerId=%s",
- eventTime, pointerId, toString(mActivePointerId).c_str());
-
- ALOGD(" %d: axis=%d, position=%0.3f, velocity=%s", pointerId, axis, position,
- toString(getVelocity(axis, pointerId)).c_str());
+ LOG(INFO) << "VelocityTracker: addMovement axis=" << MotionEvent::getLabel(axis)
+ << ", eventTime=" << eventTime << ", pointerId=" << pointerId
+ << ", activePointerId=" << toString(mActivePointerId) << ", position=" << position
+ << ", velocity=" << toString(getVelocity(axis, pointerId));
}
}
diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp
index 865366bcb2..de62c870ff 100644
--- a/libs/input/VirtualKeyMap.cpp
+++ b/libs/input/VirtualKeyMap.cpp
@@ -79,8 +79,8 @@ VirtualKeyMap::Parser::~Parser() {
status_t VirtualKeyMap::Parser::parse() {
while (!mTokenizer->isEof()) {
#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
#endif
mTokenizer->skipDelimiters(WHITESPACE);
@@ -91,7 +91,7 @@ status_t VirtualKeyMap::Parser::parse() {
String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
if (token != "0x01") {
ALOGE("%s: Unknown virtual key type, expected 0x01.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -103,7 +103,7 @@ status_t VirtualKeyMap::Parser::parse() {
&& parseNextIntField(&defn.height);
if (!success) {
ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -116,9 +116,8 @@ status_t VirtualKeyMap::Parser::parse() {
} while (consumeFieldDelimiterAndSkipWhitespace());
if (!mTokenizer->isEol()) {
- ALOGE("%s: Expected end of line, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
return BAD_VALUE;
}
}
@@ -146,9 +145,9 @@ bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) {
String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
char* end;
- *outValue = strtol(token.string(), &end, 0);
+ *outValue = strtol(token.c_str(), &end, 0);
if (token.isEmpty() || *end != '\0') {
- ALOGE("Expected an integer, got '%s'.", token.string());
+ ALOGE("Expected an integer, got '%s'.", token.c_str());
return false;
}
return true;
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index dab843b48f..8f6f95b7d1 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -57,4 +57,88 @@ interface IInputConstants
/* The default pointer acceleration value. */
const int DEFAULT_POINTER_ACCELERATION = 3;
+
+ /**
+ * Use the default Velocity Tracker Strategy. Different axes may use different default
+ * strategies.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_DEFAULT = -1;
+
+ /**
+ * Velocity Tracker Strategy: Impulse.
+ * Physical model of pushing an object. Quality: VERY GOOD.
+ * Works with duplicate coordinates, unclean finger liftoff.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_IMPULSE = 0;
+
+ /**
+ * Velocity Tracker Strategy: LSQ1.
+ * 1st order least squares. Quality: POOR.
+ * Frequently underfits the touch data especially when the finger accelerates
+ * or changes direction. Often underestimates velocity. The direction
+ * is overly influenced by historical touch points.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_LSQ1 = 1;
+
+ /**
+ * Velocity Tracker Strategy: LSQ2.
+ * 2nd order least squares. Quality: VERY GOOD.
+ * Pretty much ideal, but can be confused by certain kinds of touch data,
+ * particularly if the panel has a tendency to generate delayed,
+ * duplicate or jittery touch coordinates when the finger is released.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_LSQ2 = 2;
+
+ /**
+ * Velocity Tracker Strategy: LSQ3.
+ * 3rd order least squares. Quality: UNUSABLE.
+ * Frequently overfits the touch data yielding wildly divergent estimates
+ * of the velocity when the finger is released.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_LSQ3 = 3;
+
+ /**
+ * Velocity Tracker Strategy: WLSQ2_DELTA.
+ * 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
+ */
+ const int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 4;
+
+ /**
+ * Velocity Tracker Strategy: WLSQ2_CENTRAL.
+ * 2nd order weighted least squares, central weighting. Quality: EXPERIMENTALe
+ */
+ const int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 5;
+
+ /**
+ * Velocity Tracker Strategy: WLSQ2_RECENT.
+ * 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
+ */
+ const int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 6;
+
+ /**
+ * Velocity Tracker Strategy: INT1.
+ * 1st order integrating filter. Quality: GOOD.
+ * Not as good as 'lsq2' because it cannot estimate acceleration but it is
+ * more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
+ * the velocity of a fling but this strategy tends to respond to changes in
+ * direction more quickly and accurately.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_INT1 = 7;
+
+ /**
+ * Velocity Tracker Strategy: INT2.
+ * 2nd order integrating filter. Quality: EXPERIMENTAL.
+ * For comparison purposes only. Unlike 'int1' this strategy can compensate
+ * for acceleration but it typically overestimates the effect.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_INT2 = 8;
+
+ /**
+ * Velocity Tracker Strategy: Legacy.
+ * Legacy velocity tracker algorithm. Quality: POOR.
+ * For comparison purposes only. This algorithm is strongly influenced by
+ * old data points, consistently underestimates velocity and takes a very long
+ * time to adjust to changes in direction.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_LEGACY = 9;
}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 688d941bf6..1d3c434f76 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -23,6 +23,7 @@ pub use input::{DeviceId, MotionAction, MotionFlags};
pub use input_verifier::InputVerifier;
#[cxx::bridge(namespace = "android::input")]
+#[allow(unsafe_op_in_unsafe_fn)]
mod ffi {
#[namespace = "android"]
unsafe extern "C++" {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index cadac88030..86b996b3b6 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -18,6 +18,7 @@ cc_test {
"InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
+ "InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
"RingBuffer_test.cpp",
"TfLiteMotionPredictor_test.cpp",
diff --git a/libs/renderengine/mock/Image.cpp b/libs/input/tests/InputVerifier_test.cpp
index 57f4346f8f..e24fa6ed0b 100644
--- a/libs/renderengine/mock/Image.cpp
+++ b/libs/input/tests/InputVerifier_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-#include <renderengine/mock/Image.h>
+#include <gtest/gtest.h>
+#include <input/InputVerifier.h>
+#include <string>
namespace android {
-namespace renderengine {
-namespace mock {
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Image::Image() = default;
-Image::~Image() = default;
+TEST(InputVerifierTest, CreationWithInvalidUtfStringDoesNotCrash) {
+ constexpr char bytes[] = {static_cast<char>(0xC0), static_cast<char>(0x80)};
+ const std::string name(bytes, sizeof(bytes));
+ InputVerifier verifier(name);
+}
-} // namespace mock
-} // namespace renderengine
} // namespace android
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 7a62f5ec58..4ac7ae920e 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -72,11 +72,20 @@ TEST(MotionPredictorTest, IsPredictionAvailable) {
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
+TEST(MotionPredictorTest, StationaryNoiseFloor) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
+ []() { return true /*enable prediction*/; });
+ predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
+ predictor.record(getMotionEvent(MOVE, 0, 1, 35ms)); // No movement.
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+ ASSERT_EQ(nullptr, predicted);
+}
+
TEST(MotionPredictorTest, Offset) {
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
[]() { return true /*enable prediction*/; });
predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
- predictor.record(getMotionEvent(MOVE, 0, 2, 35ms));
+ predictor.record(getMotionEvent(MOVE, 0, 5, 35ms)); // Move enough to overcome the noise floor.
std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
ASSERT_NE(nullptr, predicted);
ASSERT_GE(predicted->getEventTime(), 41);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index ffebff1e65..1c8ec90373 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -278,6 +278,11 @@ static void computeAndCheckAxisScrollVelocity(
const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions,
std::optional<float> targetVelocity) {
checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
+ // The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall
+ // back to a strategy that supports differential axes.
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions,
+ AMOTION_EVENT_AXIS_SCROLL),
+ targetVelocity);
}
static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions,
diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 0128859ca6..275b7a4888 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -38,10 +38,10 @@
namespace android {
// Macros for including the SurfaceTexture name in log messages
-#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__)
-#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__)
-#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__)
-#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
+#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
+#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
+#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
static const struct {
uint32_t width, height;
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index cf16739e89..32b229d77c 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -19,7 +19,7 @@
#include <surfacetexture/SurfaceTexture.h>
// Macro for including the SurfaceTexture name in log messages
-#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
namespace android {
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index d3d4cbafdf..9f610e1a50 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -26,10 +26,10 @@
namespace android {
// Macros for including the SurfaceTexture name in log messages
-#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
static const mat4 mtxIdentity;
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index ad4cc4a229..9fa5569e32 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -450,7 +450,7 @@ enum ADataSpace {
*
* Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
*/
- ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
/**
* Adobe RGB
@@ -471,20 +471,20 @@ enum ADataSpace {
ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
/**
- * ITU-R Recommendation 601 (BT.601) - 525-line
+ * ITU-R Recommendation 601 (BT.601) - 625-line
*
- * Standard-definition television, 525 Lines (NTSC)
+ * Standard-definition television, 625 Lines (PAL)
*
- * Use limited range, SMPTE 170M transfer and BT.601_525 standard.
+ * Use limited range, SMPTE 170M transfer and BT.601_625 standard.
*/
ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
/**
- * ITU-R Recommendation 709 (BT.709)
+ * ITU-R Recommendation 601 (BT.601) - 525-line
*
- * High-definition television
+ * Standard-definition television, 525 Lines (NTSC)
*
- * Use limited range, SMPTE 170M transfer and BT.709 standard.
+ * Use limited range, SMPTE 170M transfer and BT.601_525 standard.
*/
ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index a5bcc6293a..a2ec57cd3c 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -61,6 +61,7 @@ impl AHardwareBuffer {
/// program termination.
///
/// Available since API level 26.
+ #[inline]
pub fn new(
width: u32,
height: u32,
@@ -199,6 +200,7 @@ mod ahardwarebuffer_tests {
#[test]
#[should_panic]
fn take_from_raw_panics_on_null() {
+ // SAFETY: Passing a null pointer is safe, it should just panic.
unsafe { AHardwareBuffer::take_from_raw(ptr::null_mut()) };
}
@@ -216,9 +218,13 @@ mod ahardwarebuffer_tests {
};
let mut raw_buffer_ptr = ptr::null_mut();
+ // SAFETY: The pointers are valid because they come from references, and
+ // `AHardwareBuffer_allocate` doesn't retain them after it returns.
let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut raw_buffer_ptr) };
assert_eq!(status, 0);
+ // SAFETY: The pointer must be valid because it was just allocated successfully, and we
+ // don't use it after calling this.
let buffer = unsafe { AHardwareBuffer::take_from_raw(raw_buffer_ptr as *mut c_void) };
assert_eq!(buffer.width(), 1024);
}
diff --git a/libs/nativewindow/tests/benchmark/Android.bp b/libs/nativewindow/tests/benchmark/Android.bp
new file mode 100644
index 0000000000..6f844cf864
--- /dev/null
+++ b/libs/nativewindow/tests/benchmark/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+ name: "nativewindow_benchmark_defaults_cc",
+ shared_libs: ["libnativewindow"],
+ static_libs: [
+ "libbase",
+ "libgoogle-benchmark-main",
+ ],
+ test_suites: [
+ "device-tests",
+ "NativeWindowBenchmarks",
+ ],
+}
+
+cc_benchmark {
+ name: "nativewindow_buffer_benchmarks_cc",
+ srcs: ["buffer_benchmarks.cc"],
+ defaults: ["nativewindow_benchmark_defaults_cc"],
+}
+
+rust_defaults {
+ name: "nativewindow_benchmark_defaults_rs",
+ rustlibs: [
+ "libnativewindow_rs",
+ "libcriterion",
+ ],
+ test_suites: [
+ "device-tests",
+ "NativeWindowBenchmarks",
+ ],
+}
+
+rust_benchmark {
+ name: "nativewindow_buffer_benchmarks_rs",
+ srcs: ["buffer_benchmarks.rs"],
+ defaults: ["nativewindow_benchmark_defaults_rs"],
+}
diff --git a/libs/nativewindow/tests/benchmark/README.md b/libs/nativewindow/tests/benchmark/README.md
new file mode 100644
index 0000000000..7eae538dd2
--- /dev/null
+++ b/libs/nativewindow/tests/benchmark/README.md
@@ -0,0 +1,22 @@
+# libnativewindow Benchmarks
+
+This directory contains benchmarks for the C++ and Rust variants of
+libnativewindow.
+
+## Running
+
+It is currently a little tricky to get statistics from Rust benchmarks directly
+from tradefed. But we can hack it by using atest to build/push, then running
+the benchmarks by hand to get stats.
+
+```
+ $ atest nativewindow_buffer_benchmarks_rs nativewindow_buffer_benchmarks_cc -d
+ $ adb shell /data/local/tmp/nativewindow_buffer_benchmarks_cc/x86_64/nativewindow_buffer_benchmarks_cc
+ $ adb shell /data/local/tmp/nativewindow_buffer_benchmarks_rs/x86_64/nativewindow_buffer_benchmarks_rs --bench
+```
+
+## Results
+
+On a remote emulator, the results we see from the benchmarks from Rust and C++
+seem to be roughly equivalent! Allocating/deallocating a 720p buffer takes
+~2.3ms on each.
diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc b/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc
new file mode 100644
index 0000000000..9b31993809
--- /dev/null
+++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <android-base/macros.h>
+#include <android/hardware_buffer.h>
+#include <benchmark/benchmark.h>
+
+constexpr AHardwareBuffer_Desc k720pDesc = {.width = 1280,
+ .height = 720,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ .stride = 0};
+
+static void BM_BufferAllocationDeallocation(benchmark::State& state) {
+ AHardwareBuffer* buffer = nullptr;
+ for (auto _ : state) {
+ int status = AHardwareBuffer_allocate(&k720pDesc, &buffer);
+ if (UNLIKELY(status != 0)) {
+ state.SkipWithError("Unable to allocate buffer.");
+ }
+ AHardwareBuffer_release(buffer);
+ buffer = nullptr;
+ }
+}
+BENCHMARK(BM_BufferAllocationDeallocation);
+
+static void BM_AHardwareBuffer_Id(benchmark::State& state) {
+ AHardwareBuffer* buffer = nullptr;
+ int status = AHardwareBuffer_allocate(&k720pDesc, &buffer);
+ if (UNLIKELY(status != 0)) {
+ state.SkipWithError("Unable to allocate buffer.");
+ }
+
+ for (auto _ : state) {
+ uint64_t id = 0;
+ int status = AHardwareBuffer_getId(buffer, &id);
+ if (UNLIKELY(status != 0)) {
+ state.SkipWithError("Unable to get ID.");
+ }
+ }
+
+ AHardwareBuffer_release(buffer);
+}
+BENCHMARK(BM_AHardwareBuffer_Id);
+
+static void BM_AHardwareBuffer_Desc(benchmark::State& state) {
+ AHardwareBuffer* buffer = nullptr;
+ int status = AHardwareBuffer_allocate(&k720pDesc, &buffer);
+ if (UNLIKELY(status != 0)) {
+ state.SkipWithError("Unable to allocate buffer.");
+ }
+
+ for (auto _ : state) {
+ AHardwareBuffer_Desc desc = {};
+ AHardwareBuffer_describe(buffer, &desc);
+ }
+
+ AHardwareBuffer_release(buffer);
+}
+BENCHMARK(BM_AHardwareBuffer_Desc);
+
+BENCHMARK_MAIN();
diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
new file mode 100644
index 0000000000..fbd49c0b50
--- /dev/null
+++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Benchmark for libnativewindow AHardwareBuffer bindings
+
+#![allow(dead_code)]
+#![allow(missing_docs)]
+
+use criterion::*;
+use nativewindow::*;
+
+#[inline]
+fn create_720p_buffer() -> AHardwareBuffer {
+ AHardwareBuffer::new(
+ 1280,
+ 720,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ )
+ .unwrap()
+}
+
+fn criterion_benchmark(c: &mut Criterion) {
+ c.bench_function("allocate_deallocate", |b| {
+ b.iter(|| {
+ let buffer = create_720p_buffer();
+ drop(buffer);
+ })
+ });
+
+ let buffer = create_720p_buffer();
+ c.bench_with_input(BenchmarkId::new("id", "buffer"), &buffer, |b, buffer| {
+ b.iter(|| {
+ buffer.id();
+ })
+ });
+
+ // This benchmark exercises getters that need to fetch data via an
+ // underlying call to AHardwareBuffer_describe.
+ c.bench_with_input(BenchmarkId::new("desc", "buffer"), &buffer, |b, buffer| {
+ b.iter(|| {
+ buffer.width();
+ })
+ });
+}
+
+criterion_group!(benches, criterion_benchmark);
+criterion_main!(benches);
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 8d19c45527..ba2eb7d224 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -56,30 +56,8 @@ cc_defaults {
filegroup {
name: "librenderengine_sources",
srcs: [
- "Description.cpp",
"ExternalTexture.cpp",
- "Mesh.cpp",
"RenderEngine.cpp",
- "Texture.cpp",
- ],
-}
-
-filegroup {
- name: "librenderengine_gl_sources",
- srcs: [
- "gl/GLESRenderEngine.cpp",
- "gl/GLExtensions.cpp",
- "gl/GLFramebuffer.cpp",
- "gl/GLImage.cpp",
- "gl/GLShadowTexture.cpp",
- "gl/GLShadowVertexGenerator.cpp",
- "gl/GLSkiaShadowPort.cpp",
- "gl/GLVertexBuffer.cpp",
- "gl/ImageManager.cpp",
- "gl/Program.cpp",
- "gl/ProgramCache.cpp",
- "gl/filters/BlurFilter.cpp",
- "gl/filters/GenericProgram.cpp",
],
}
@@ -96,6 +74,7 @@ filegroup {
"skia/AutoBackendTexture.cpp",
"skia/Cache.cpp",
"skia/ColorSpaces.cpp",
+ "skia/GLExtensions.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
"skia/SkiaVkRenderEngine.cpp",
@@ -136,7 +115,6 @@ cc_library_static {
],
srcs: [
":librenderengine_sources",
- ":librenderengine_gl_sources",
":librenderengine_threaded_sources",
":librenderengine_skia_sources",
],
@@ -155,8 +133,6 @@ cc_library_static {
name: "librenderengine_mocks",
defaults: ["librenderengine_defaults"],
srcs: [
- "mock/Framebuffer.cpp",
- "mock/Image.cpp",
"mock/RenderEngine.cpp",
],
static_libs: [
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
deleted file mode 100644
index 245c9e1e30..0000000000
--- a/libs/renderengine/Description.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2013 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 <renderengine/private/Description.h>
-
-#include <stdint.h>
-
-#include <utils/TypeHelpers.h>
-
-namespace android {
-namespace renderengine {
-
-Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) {
- ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK);
- switch (transfer) {
- case ui::Dataspace::TRANSFER_ST2084:
- return Description::TransferFunction::ST2084;
- case ui::Dataspace::TRANSFER_HLG:
- return Description::TransferFunction::HLG;
- case ui::Dataspace::TRANSFER_LINEAR:
- return Description::TransferFunction::LINEAR;
- default:
- return Description::TransferFunction::SRGB;
- }
-}
-
-bool Description::hasInputTransformMatrix() const {
- const mat4 identity;
- return inputTransformMatrix != identity;
-}
-
-bool Description::hasOutputTransformMatrix() const {
- const mat4 identity;
- return outputTransformMatrix != identity;
-}
-
-bool Description::hasColorMatrix() const {
- const mat4 identity;
- return colorMatrix != identity;
-}
-
-bool Description::hasDisplayColorMatrix() const {
- const mat4 identity;
- return displayColorMatrix != identity;
-}
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
index 9eb42cd8e1..6f2a96a87b 100644
--- a/libs/renderengine/ExternalTexture.cpp
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -27,14 +27,6 @@ ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer,
: mBuffer(buffer), mRenderEngine(renderEngine), mWritable(usage & WRITEABLE) {
LOG_ALWAYS_FATAL_IF(buffer == nullptr,
"Attempted to bind a null buffer to an external texture!");
- // GLESRenderEngine has a separate texture cache for output buffers,
- if (usage == WRITEABLE &&
- (mRenderEngine.getRenderEngineType() ==
- renderengine::RenderEngine::RenderEngineType::GLES ||
- mRenderEngine.getRenderEngineType() ==
- renderengine::RenderEngine::RenderEngineType::THREADED)) {
- return;
- }
mRenderEngine.mapExternalTextureBuffer(mBuffer, mWritable);
}
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
deleted file mode 100644
index ed2f45fdf5..0000000000
--- a/libs/renderengine/Mesh.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2013 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 <renderengine/Mesh.h>
-
-#include <utils/Log.h>
-
-namespace android {
-namespace renderengine {
-
-Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
- size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize,
- size_t indexCount)
- : mVertexCount(vertexCount),
- mVertexSize(vertexSize),
- mTexCoordsSize(texCoordSize),
- mCropCoordsSize(cropCoordsSize),
- mShadowColorSize(shadowColorSize),
- mShadowParamsSize(shadowParamsSize),
- mPrimitive(primitive),
- mIndexCount(indexCount) {
- if (vertexCount == 0) {
- mVertices.resize(1);
- mVertices[0] = 0.0f;
- mStride = 0;
- return;
- }
- size_t stride = vertexSize + texCoordSize + cropCoordsSize + shadowColorSize + shadowParamsSize;
- size_t remainder = (stride * vertexCount) / vertexCount;
- // Since all of the input parameters are unsigned, if stride is less than
- // either vertexSize or texCoordSize, it must have overflowed. remainder
- // will be equal to stride as long as stride * vertexCount doesn't overflow.
- if ((stride < vertexSize) || (remainder != stride)) {
- ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu, %zu, %zu)", vertexCount, vertexSize,
- texCoordSize, cropCoordsSize, shadowColorSize, shadowParamsSize);
- mVertices.resize(1);
- mVertices[0] = 0.0f;
- mVertexCount = 0;
- mVertexSize = 0;
- mTexCoordsSize = 0;
- mCropCoordsSize = 0;
- mShadowColorSize = 0;
- mShadowParamsSize = 0;
- mStride = 0;
- return;
- }
-
- mVertices.resize(stride * vertexCount);
- mStride = stride;
- mIndices.resize(indexCount);
-}
-
-Mesh::Primitive Mesh::getPrimitive() const {
- return mPrimitive;
-}
-
-float const* Mesh::getPositions() const {
- return mVertices.data();
-}
-float* Mesh::getPositions() {
- return mVertices.data();
-}
-
-float const* Mesh::getTexCoords() const {
- return mVertices.data() + mVertexSize;
-}
-float* Mesh::getTexCoords() {
- return mVertices.data() + mVertexSize;
-}
-
-float const* Mesh::getCropCoords() const {
- return mVertices.data() + mVertexSize + mTexCoordsSize;
-}
-float* Mesh::getCropCoords() {
- return mVertices.data() + mVertexSize + mTexCoordsSize;
-}
-
-float const* Mesh::getShadowColor() const {
- return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
-}
-float* Mesh::getShadowColor() {
- return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
-}
-
-float const* Mesh::getShadowParams() const {
- return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
-}
-float* Mesh::getShadowParams() {
- return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
-}
-
-uint16_t const* Mesh::getIndices() const {
- return mIndices.data();
-}
-
-uint16_t* Mesh::getIndices() {
- return mIndices.data();
-}
-
-size_t Mesh::getVertexCount() const {
- return mVertexCount;
-}
-
-size_t Mesh::getVertexSize() const {
- return mVertexSize;
-}
-
-size_t Mesh::getTexCoordsSize() const {
- return mTexCoordsSize;
-}
-
-size_t Mesh::getShadowColorSize() const {
- return mShadowColorSize;
-}
-
-size_t Mesh::getShadowParamsSize() const {
- return mShadowParamsSize;
-}
-
-size_t Mesh::getByteStride() const {
- return mStride * sizeof(float);
-}
-
-size_t Mesh::getStride() const {
- return mStride;
-}
-
-size_t Mesh::getIndexCount() const {
- return mIndexCount;
-}
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index 5d23a5ef8d..66e1aa1ca1 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1075131
+
adyabr@google.com
alecmouri@google.com
djsollen@google.com
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index d08c2213ad..a8e92c6807 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -18,7 +18,6 @@
#include <cutils/properties.h>
#include <log/log.h>
-#include "gl/GLESRenderEngine.h"
#include "renderengine/ExternalTexture.h"
#include "threaded/RenderEngineThreaded.h"
@@ -30,11 +29,6 @@ namespace renderengine {
std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
switch (args.renderEngineType) {
- case RenderEngineType::THREADED:
- ALOGD("Threaded RenderEngine with GLES Backend");
- return renderengine::threaded::RenderEngineThreaded::create(
- [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); },
- args.renderEngineType);
case RenderEngineType::SKIA_GL:
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
@@ -56,10 +50,6 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg
return android::renderengine::skia::SkiaVkRenderEngine::create(args);
},
args.renderEngineType);
- case RenderEngineType::GLES:
- default:
- ALOGD("RenderEngine with GLES Backend");
- return renderengine::gl::GLESRenderEngine::create(args);
}
}
diff --git a/libs/renderengine/Texture.cpp b/libs/renderengine/Texture.cpp
deleted file mode 100644
index 154cde80b9..0000000000
--- a/libs/renderengine/Texture.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2013 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 <renderengine/Texture.h>
-
-namespace android {
-namespace renderengine {
-
-Texture::Texture()
- : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {}
-
-Texture::Texture(Target textureTarget, uint32_t textureName)
- : mTextureName(textureName),
- mTextureTarget(textureTarget),
- mWidth(0),
- mHeight(0),
- mFiltering(false) {}
-
-void Texture::init(Target textureTarget, uint32_t textureName) {
- mTextureName = textureName;
- mTextureTarget = textureTarget;
-}
-
-Texture::~Texture() {}
-
-void Texture::setMatrix(float const* matrix) {
- mTextureMatrix = mat4(matrix);
-}
-
-void Texture::setFiltering(bool enabled) {
- mFiltering = enabled;
-}
-
-void Texture::setDimensions(size_t width, size_t height) {
- mWidth = width;
- mHeight = height;
-}
-
-uint32_t Texture::getTextureName() const {
- return mTextureName;
-}
-
-uint32_t Texture::getTextureTarget() const {
- return mTextureTarget;
-}
-
-const mat4& Texture::getMatrix() const {
- return mTextureMatrix;
-}
-
-bool Texture::getFiltering() const {
- return mFiltering;
-}
-
-size_t Texture::getWidth() const {
- return mWidth;
-}
-
-size_t Texture::getHeight() const {
- return mHeight;
-}
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index bd7b617ae7..ce2c7d7b6b 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -43,10 +43,6 @@ std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) {
return "skiavk";
case RenderEngine::RenderEngineType::SKIA_VK_THREADED:
return "skiavkthreaded";
- case RenderEngine::RenderEngineType::GLES:
- case RenderEngine::RenderEngineType::THREADED:
- LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?");
- return "unused";
}
}
@@ -121,7 +117,6 @@ static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngi
.setSupportsBackgroundBlur(true)
.setContextPriority(RenderEngine::ContextPriority::REALTIME)
.setRenderEngineType(type)
- .setUseColorManagerment(true)
.build();
return RenderEngine::create(args);
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
deleted file mode 100644
index 0d7df101f4..0000000000
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ /dev/null
@@ -1,1866 +0,0 @@
-/*
- * Copyright 2013 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_NDEBUG 0
-#include "EGL/egl.h"
-#undef LOG_TAG
-#define LOG_TAG "RenderEngine"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <sched.h>
-#include <cmath>
-#include <fstream>
-#include <sstream>
-#include <unordered_set>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <android-base/stringprintf.h>
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <gui/DebugEGLImageTracker.h>
-#include <renderengine/Mesh.h>
-#include <renderengine/Texture.h>
-#include <renderengine/private/Description.h>
-#include <sync/sync.h>
-#include <ui/ColorSpace.h>
-#include <ui/DebugUtils.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <utils/KeyedVector.h>
-#include <utils/Trace.h>
-#include "GLESRenderEngine.h"
-#include "GLExtensions.h"
-#include "GLFramebuffer.h"
-#include "GLImage.h"
-#include "GLShadowVertexGenerator.h"
-#include "Program.h"
-#include "ProgramCache.h"
-#include "filters/BlurFilter.h"
-
-bool checkGlError(const char* op, int lineNumber) {
- bool errorFound = false;
- GLint error = glGetError();
- while (error != GL_NO_ERROR) {
- errorFound = true;
- error = glGetError();
- ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
- }
- return errorFound;
-}
-
-static constexpr bool outputDebugPPMs = false;
-
-void writePPM(const char* basename, GLuint width, GLuint height) {
- ALOGV("writePPM #%s: %d x %d", basename, width, height);
-
- std::vector<GLubyte> pixels(width * height * 4);
- std::vector<GLubyte> outBuffer(width * height * 3);
-
- // TODO(courtneygo): We can now have float formats, need
- // to remove this code or update to support.
- // Make returned pixels fit in uint32_t, one byte per component
- glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
- if (checkGlError(__FUNCTION__, __LINE__)) {
- return;
- }
-
- std::string filename(basename);
- filename.append(".ppm");
- std::ofstream file(filename.c_str(), std::ios::binary);
- if (!file.is_open()) {
- ALOGE("Unable to open file: %s", filename.c_str());
- ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
- "surfaceflinger to write debug images");
- return;
- }
-
- file << "P6\n";
- file << width << "\n";
- file << height << "\n";
- file << 255 << "\n";
-
- auto ptr = reinterpret_cast<char*>(pixels.data());
- auto outPtr = reinterpret_cast<char*>(outBuffer.data());
- for (int y = height - 1; y >= 0; y--) {
- char* data = ptr + y * width * sizeof(uint32_t);
-
- for (GLuint x = 0; x < width; x++) {
- // Only copy R, G and B components
- outPtr[0] = data[0];
- outPtr[1] = data[1];
- outPtr[2] = data[2];
- data += sizeof(uint32_t);
- outPtr += 3;
- }
- }
- file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
-}
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class BindNativeBufferAsFramebuffer {
-public:
- BindNativeBufferAsFramebuffer(GLESRenderEngine& engine, ANativeWindowBuffer* buffer,
- const bool useFramebufferCache)
- : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
- mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
- useFramebufferCache)
- ? mEngine.bindFrameBuffer(mFramebuffer)
- : NO_MEMORY;
- }
- ~BindNativeBufferAsFramebuffer() {
- mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
- mEngine.unbindFrameBuffer(mFramebuffer);
- }
- status_t getStatus() const { return mStatus; }
-
-private:
- GLESRenderEngine& mEngine;
- Framebuffer* mFramebuffer;
- status_t mStatus;
-};
-
-using base::StringAppendF;
-using ui::Dataspace;
-
-static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
- EGLint wanted, EGLConfig* outConfig) {
- EGLint numConfigs = -1, n = 0;
- eglGetConfigs(dpy, nullptr, 0, &numConfigs);
- std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
- eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
- configs.resize(n);
-
- if (!configs.empty()) {
- if (attribute != EGL_NONE) {
- for (EGLConfig config : configs) {
- EGLint value = 0;
- eglGetConfigAttrib(dpy, config, attribute, &value);
- if (wanted == value) {
- *outConfig = config;
- return NO_ERROR;
- }
- }
- } else {
- // just pick the first one
- *outConfig = configs[0];
- return NO_ERROR;
- }
- }
-
- return NAME_NOT_FOUND;
-}
-
-static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
- EGLConfig* config) {
- // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
- // it is to be used with WIFI displays
- status_t err;
- EGLint wantedAttribute;
- EGLint wantedAttributeValue;
-
- std::vector<EGLint> attribs;
- if (renderableType) {
- const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format);
- const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102;
-
- // Default to 8 bits per channel.
- const EGLint tmpAttribs[] = {
- EGL_RENDERABLE_TYPE,
- renderableType,
- EGL_RECORDABLE_ANDROID,
- EGL_TRUE,
- EGL_SURFACE_TYPE,
- EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
- EGL_FRAMEBUFFER_TARGET_ANDROID,
- EGL_TRUE,
- EGL_RED_SIZE,
- is1010102 ? 10 : 8,
- EGL_GREEN_SIZE,
- is1010102 ? 10 : 8,
- EGL_BLUE_SIZE,
- is1010102 ? 10 : 8,
- EGL_ALPHA_SIZE,
- is1010102 ? 2 : 8,
- EGL_NONE,
- };
- std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)),
- std::back_inserter(attribs));
- wantedAttribute = EGL_NONE;
- wantedAttributeValue = EGL_NONE;
- } else {
- // if no renderable type specified, fallback to a simplified query
- wantedAttribute = EGL_NATIVE_VISUAL_ID;
- wantedAttributeValue = format;
- }
-
- err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue,
- config);
- if (err == NO_ERROR) {
- EGLint caveat;
- if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
- ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
- }
-
- return err;
-}
-
-std::optional<RenderEngine::ContextPriority> GLESRenderEngine::createContextPriority(
- const RenderEngineCreationArgs& args) {
- if (!GLExtensions::getInstance().hasContextPriority()) {
- return std::nullopt;
- }
-
- switch (args.contextPriority) {
- case RenderEngine::ContextPriority::REALTIME:
- if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
- return RenderEngine::ContextPriority::REALTIME;
- } else {
- ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
- return RenderEngine::ContextPriority::HIGH;
- }
- case RenderEngine::ContextPriority::HIGH:
- case RenderEngine::ContextPriority::MEDIUM:
- case RenderEngine::ContextPriority::LOW:
- return args.contextPriority;
- default:
- return std::nullopt;
- }
-}
-
-std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
- // initialize EGL for the default display
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (!eglInitialize(display, nullptr, nullptr)) {
- LOG_ALWAYS_FATAL("failed to initialize EGL. EGL error=0x%x", eglGetError());
- }
-
- const auto eglVersion = eglQueryString(display, EGL_VERSION);
- if (!eglVersion) {
- checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
- }
-
- // Use the Android impl to grab EGL_NV_context_priority_realtime
- const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
- if (!eglExtensions) {
- checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
- }
-
- GLExtensions& extensions = GLExtensions::getInstance();
- extensions.initWithEGLStrings(eglVersion, eglExtensions);
-
- // The code assumes that ES2 or later is available if this extension is
- // supported.
- EGLConfig config = EGL_NO_CONFIG;
- if (!extensions.hasNoConfigContext()) {
- config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
- }
-
- const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
- EGLContext protectedContext = EGL_NO_CONTEXT;
- if (args.enableProtectedContext && extensions.hasProtectedContent()) {
- protectedContext =
- createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
- ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
- }
-
- EGLContext ctxt =
- createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
-
- // if can't create a GL context, we can only abort.
- LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
-
- EGLSurface stub = EGL_NO_SURFACE;
- if (!extensions.hasSurfacelessContext()) {
- stub = createStubEglPbufferSurface(display, config, args.pixelFormat,
- Protection::UNPROTECTED);
- LOG_ALWAYS_FATAL_IF(stub == EGL_NO_SURFACE, "can't create stub pbuffer");
- }
- EGLBoolean success = eglMakeCurrent(display, stub, stub, ctxt);
- LOG_ALWAYS_FATAL_IF(!success, "can't make stub pbuffer current");
- extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
- glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
-
- EGLSurface protectedStub = EGL_NO_SURFACE;
- if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
- protectedStub = createStubEglPbufferSurface(display, config, args.pixelFormat,
- Protection::PROTECTED);
- ALOGE_IF(protectedStub == EGL_NO_SURFACE, "can't create protected stub pbuffer");
- }
-
- // now figure out what version of GL did we actually get
- GlesVersion version = parseGlesVersion(extensions.getVersion());
-
- LOG_ALWAYS_FATAL_IF(args.supportsBackgroundBlur && version < GLES_VERSION_3_0,
- "Blurs require OpenGL ES 3.0. Please unset ro.surface_flinger.supports_background_blur");
-
- // initialize the renderer while GL is current
- std::unique_ptr<GLESRenderEngine> engine;
- switch (version) {
- case GLES_VERSION_1_0:
- case GLES_VERSION_1_1:
- LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
- break;
- case GLES_VERSION_2_0:
- case GLES_VERSION_3_0:
- engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, stub,
- protectedContext, protectedStub);
- break;
- }
-
- ALOGI("OpenGL ES informations:");
- ALOGI("vendor : %s", extensions.getVendor());
- ALOGI("renderer : %s", extensions.getRenderer());
- ALOGI("version : %s", extensions.getVersion());
- ALOGI("extensions: %s", extensions.getExtensions());
- ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
- ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
- return engine;
-}
-
-EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
- status_t err;
- EGLConfig config;
-
- // First try to get an ES3 config
- err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
- if (err != NO_ERROR) {
- // If ES3 fails, try to get an ES2 config
- err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
- if (err != NO_ERROR) {
- // If ES2 still doesn't work, probably because we're on the emulator.
- // try a simplified query
- ALOGW("no suitable EGLConfig found, trying a simpler query");
- err = selectEGLConfig(display, format, 0, &config);
- if (err != NO_ERROR) {
- // this EGL is too lame for android
- LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
- }
- }
- }
-
- if (logConfig) {
- // print some debugging info
- EGLint r, g, b, a;
- eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
- eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
- eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
- eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
- ALOGI("EGL information:");
- ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
- ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
- ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
- ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
- ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
- }
-
- return config;
-}
-
-GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
- EGLConfig config, EGLContext ctxt, EGLSurface stub,
- EGLContext protectedContext, EGLSurface protectedStub)
- : RenderEngine(args.renderEngineType),
- mEGLDisplay(display),
- mEGLConfig(config),
- mEGLContext(ctxt),
- mStubSurface(stub),
- mProtectedEGLContext(protectedContext),
- mProtectedStubSurface(protectedStub),
- mVpWidth(0),
- mVpHeight(0),
- mFramebufferImageCacheSize(args.imageCacheSize),
- mUseColorManagement(args.useColorManagement),
- mPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) {
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
-
- // Initialize protected EGL Context.
- if (mProtectedEGLContext != EGL_NO_CONTEXT) {
- EGLBoolean success = eglMakeCurrent(display, mProtectedStubSurface, mProtectedStubSurface,
- mProtectedEGLContext);
- ALOGE_IF(!success, "can't make protected context current");
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
- success = eglMakeCurrent(display, mStubSurface, mStubSurface, mEGLContext);
- LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
- }
-
- // mColorBlindnessCorrection = M;
-
- if (mUseColorManagement) {
- const ColorSpace srgb(ColorSpace::sRGB());
- const ColorSpace displayP3(ColorSpace::DisplayP3());
- const ColorSpace bt2020(ColorSpace::BT2020());
-
- // no chromatic adaptation needed since all color spaces use D65 for their white points.
- mSrgbToXyz = mat4(srgb.getRGBtoXYZ());
- mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ());
- mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ());
- mXyzToSrgb = mat4(srgb.getXYZtoRGB());
- mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
- mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
-
- // Compute sRGB to Display P3 and BT2020 transform matrix.
- // NOTE: For now, we are limiting output wide color space support to
- // Display-P3 and BT2020 only.
- mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz;
- mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz;
-
- // Compute Display P3 to sRGB and BT2020 transform matrix.
- mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz;
- mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz;
-
- // Compute BT2020 to sRGB and Display P3 transform matrix
- mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz;
- mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz;
- }
-
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.traceGpuCompletion", value, "0");
- if (atoi(value)) {
- mTraceGpuCompletion = true;
- mFlushTracer = std::make_unique<FlushTracer>(this);
- }
-
- if (args.supportsBackgroundBlur) {
- mBlurFilter = new BlurFilter(*this);
- checkErrors("BlurFilter creation");
- }
-
- mImageManager = std::make_unique<ImageManager>(this);
- mImageManager->initThread();
- mDrawingBuffer = createFramebuffer();
- sp<GraphicBuffer> buf =
- sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBA_8888, 1,
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
- "placeholder");
-
- const status_t err = buf->initCheck();
- if (err != OK) {
- ALOGE("Error allocating placeholder buffer: %d", err);
- return;
- }
- mPlaceholderBuffer = buf.get();
- EGLint attributes[] = {
- EGL_NONE,
- };
- mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- mPlaceholderBuffer, attributes);
- ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
- eglGetError());
-
- mShadowTexture = std::make_unique<GLShadowTexture>();
-}
-
-GLESRenderEngine::~GLESRenderEngine() {
- // Destroy the image manager first.
- mImageManager = nullptr;
- mShadowTexture = nullptr;
- cleanFramebufferCache();
- ProgramCache::getInstance().purgeCaches();
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- glDisableVertexAttribArray(Program::position);
- unbindFrameBuffer(mDrawingBuffer.get());
- mDrawingBuffer = nullptr;
- eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
- mImageCache.clear();
- if (mStubSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEGLDisplay, mStubSurface);
- }
- if (mProtectedStubSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEGLDisplay, mProtectedStubSurface);
- }
- if (mEGLContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEGLDisplay, mEGLContext);
- }
- if (mProtectedEGLContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEGLDisplay, mProtectedEGLContext);
- }
- eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglTerminate(mEGLDisplay);
- eglReleaseThread();
-}
-
-std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() {
- return std::make_unique<GLFramebuffer>(*this);
-}
-
-std::unique_ptr<Image> GLESRenderEngine::createImage() {
- return std::make_unique<GLImage>(*this);
-}
-
-Framebuffer* GLESRenderEngine::getFramebufferForDrawing() {
- return mDrawingBuffer.get();
-}
-
-std::future<void> GLESRenderEngine::primeCache() {
- ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
- mUseColorManagement, mPrecacheToneMapperShaderOnly);
- return {};
-}
-
-base::unique_fd GLESRenderEngine::flush() {
- ATRACE_CALL();
- if (!GLExtensions::getInstance().hasNativeFenceSync()) {
- return base::unique_fd();
- }
-
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
- return base::unique_fd();
- }
-
- // native fence fd will not be populated until flush() is done.
- glFlush();
-
- // get the fence fd
- base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
- ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
- }
-
- // Only trace if we have a valid fence, as current usage falls back to
- // calling finish() if the fence fd is invalid.
- if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer) && fenceFd.get() >= 0) {
- mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
- }
-
- return fenceFd;
-}
-
-bool GLESRenderEngine::finish() {
- ATRACE_CALL();
- if (!GLExtensions::getInstance().hasFenceSync()) {
- ALOGW("no synchronization support");
- return false;
- }
-
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGW("failed to create EGL fence sync: %#x", eglGetError());
- return false;
- }
-
- if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer)) {
- mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
- }
-
- return waitSync(sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR);
-}
-
-bool GLESRenderEngine::waitSync(EGLSyncKHR sync, EGLint flags) {
- EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, flags, 2000000000 /*2 sec*/);
- EGLint error = eglGetError();
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (result != EGL_CONDITION_SATISFIED_KHR) {
- if (result == EGL_TIMEOUT_EXPIRED_KHR) {
- ALOGW("fence wait timed out");
- } else {
- ALOGW("error waiting on EGL fence: %#x", error);
- }
- return false;
- }
-
- return true;
-}
-
-bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) {
- if (!GLExtensions::getInstance().hasNativeFenceSync() ||
- !GLExtensions::getInstance().hasWaitSync()) {
- return false;
- }
-
- // release the fd and transfer the ownership to EGLSync
- EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
- return false;
- }
-
- // XXX: The spec draft is inconsistent as to whether this should return an
- // EGLint or void. Ignore the return value for now, as it's not strictly
- // needed.
- eglWaitSyncKHR(mEGLDisplay, sync, 0);
- EGLint error = eglGetError();
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (error != EGL_SUCCESS) {
- ALOGE("failed to wait for EGL native fence sync: %#x", error);
- return false;
- }
-
- return true;
-}
-
-void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
- ATRACE_CALL();
- glDisable(GL_BLEND);
- glClearColor(red, green, blue, alpha);
- glClear(GL_COLOR_BUFFER_BIT);
-}
-
-void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue,
- float alpha) {
- size_t c;
- Rect const* r = region.getArray(&c);
- Mesh mesh = Mesh::Builder()
- .setPrimitive(Mesh::TRIANGLES)
- .setVertices(c * 6 /* count */, 2 /* size */)
- .build();
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- for (size_t i = 0; i < c; i++, r++) {
- position[i * 6 + 0].x = r->left;
- position[i * 6 + 0].y = r->top;
- position[i * 6 + 1].x = r->left;
- position[i * 6 + 1].y = r->bottom;
- position[i * 6 + 2].x = r->right;
- position[i * 6 + 2].y = r->bottom;
- position[i * 6 + 3].x = r->left;
- position[i * 6 + 3].y = r->top;
- position[i * 6 + 4].x = r->right;
- position[i * 6 + 4].y = r->bottom;
- position[i * 6 + 5].x = r->right;
- position[i * 6 + 5].y = r->top;
- }
- setupFillWithColor(red, green, blue, alpha);
- drawMesh(mesh);
-}
-
-void GLESRenderEngine::setScissor(const Rect& region) {
- glScissor(region.left, region.top, region.getWidth(), region.getHeight());
- glEnable(GL_SCISSOR_TEST);
-}
-
-void GLESRenderEngine::disableScissor() {
- glDisable(GL_SCISSOR_TEST);
-}
-
-void GLESRenderEngine::genTextures(size_t count, uint32_t* names) {
- glGenTextures(count, names);
-}
-
-void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
- for (int i = 0; i < count; ++i) {
- mTextureView.erase(names[i]);
- }
- glDeleteTextures(count, names);
-}
-
-void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
- ATRACE_CALL();
- const GLImage& glImage = static_cast<const GLImage&>(image);
- const GLenum target = GL_TEXTURE_EXTERNAL_OES;
-
- glBindTexture(target, texName);
- if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) {
- glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage()));
- }
-}
-
-void GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& bufferFence) {
- ATRACE_CALL();
-
- bool found = false;
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- auto cachedImage = mImageCache.find(buffer->getId());
- found = (cachedImage != mImageCache.end());
- }
-
- // If we couldn't find the image in the cache at this time, then either
- // SurfaceFlinger messed up registering the buffer ahead of time or we got
- // backed up creating other EGLImages.
- if (!found) {
- status_t cacheResult = mImageManager->cache(buffer);
- if (cacheResult != NO_ERROR) {
- ALOGE("Error with caching buffer: %d", cacheResult);
- return;
- }
- }
-
- // Whether or not we needed to cache, re-check mImageCache to make sure that
- // there's an EGLImage. The current threading model guarantees that we don't
- // destroy a cached image until it's really not needed anymore (i.e. this
- // function should not be called), so the only possibility is that something
- // terrible went wrong and we should just bind something and move on.
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- auto cachedImage = mImageCache.find(buffer->getId());
-
- if (cachedImage == mImageCache.end()) {
- // We failed creating the image if we got here, so bail out.
- ALOGE("Failed to create an EGLImage when rendering");
- bindExternalTextureImage(texName, *createImage());
- return;
- }
-
- bindExternalTextureImage(texName, *cachedImage->second);
- mTextureView.insert_or_assign(texName, buffer->getId());
- }
-
- // Wait for the new buffer to be ready.
- if (bufferFence != nullptr && bufferFence->isValid()) {
- if (GLExtensions::getInstance().hasWaitSync()) {
- base::unique_fd fenceFd(bufferFence->dup());
- if (fenceFd == -1) {
- ALOGE("error dup'ing fence fd: %d", errno);
- return;
- }
- if (!waitFence(std::move(fenceFd))) {
- ALOGE("failed to wait on fence fd");
- return;
- }
- } else {
- status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer");
- if (err != NO_ERROR) {
- ALOGE("error waiting for fence: %d", err);
- return;
- }
- }
- }
-
- return;
-}
-
-void GLESRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
- bool /*isRenderable*/) {
- ATRACE_CALL();
- mImageManager->cacheAsync(buffer, nullptr);
-}
-
-std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::cacheExternalTextureBufferForTesting(
- const sp<GraphicBuffer>& buffer) {
- auto barrier = std::make_shared<ImageManager::Barrier>();
- mImageManager->cacheAsync(buffer, barrier);
- return barrier;
-}
-
-status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) {
- if (buffer == nullptr) {
- return BAD_VALUE;
- }
-
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- if (mImageCache.count(buffer->getId()) > 0) {
- // If there's already an image then fail fast here.
- return NO_ERROR;
- }
- }
- ATRACE_CALL();
-
- // Create the image without holding a lock so that we don't block anything.
- std::unique_ptr<Image> newImage = createImage();
-
- bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
- buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
- if (!created) {
- ALOGE("Failed to create image. id=%" PRIx64 " size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
- buffer->getId(), buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
- buffer->getUsage(), buffer->getPixelFormat());
- return NO_INIT;
- }
-
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- if (mImageCache.count(buffer->getId()) > 0) {
- // In theory it's possible for another thread to recache the image,
- // so bail out if another thread won.
- return NO_ERROR;
- }
- mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
- }
-
- return NO_ERROR;
-}
-
-void GLESRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
- mImageManager->releaseAsync(buffer->getId(), nullptr);
-}
-
-std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting(
- uint64_t bufferId) {
- auto barrier = std::make_shared<ImageManager::Barrier>();
- mImageManager->releaseAsync(bufferId, barrier);
- return barrier;
-}
-
-void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) {
- std::unique_ptr<Image> image;
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- const auto& cachedImage = mImageCache.find(bufferId);
-
- if (cachedImage != mImageCache.end()) {
- ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
- // Move the buffer out of cache first, so that we can destroy
- // without holding the cache's lock.
- image = std::move(cachedImage->second);
- mImageCache.erase(bufferId);
- return;
- }
- }
- ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
-}
-
-int GLESRenderEngine::getContextPriority() {
- int value;
- eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
- return value;
-}
-
-FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
- // Translate win by the rounded corners rect coordinates, to have all values in
- // layer coordinate space.
- FloatRect cropWin = layer.geometry.boundaries;
- const FloatRect& roundedCornersCrop = layer.geometry.roundedCornersCrop;
- cropWin.left -= roundedCornersCrop.left;
- cropWin.right -= roundedCornersCrop.left;
- cropWin.top -= roundedCornersCrop.top;
- cropWin.bottom -= roundedCornersCrop.top;
- Mesh::VertexArray<vec2> cropCoords(mesh.getCropCoordArray<vec2>());
- cropCoords[0] = vec2(cropWin.left, cropWin.top);
- cropCoords[1] = vec2(cropWin.left, cropWin.top + cropWin.getHeight());
- cropCoords[2] = vec2(cropWin.right, cropWin.top + cropWin.getHeight());
- cropCoords[3] = vec2(cropWin.right, cropWin.top);
-
- setupCornerRadiusCropSize(roundedCornersCrop.getWidth(), roundedCornersCrop.getHeight());
- return cropWin;
-}
-
-void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display,
- const LayerSettings& layer, const Mesh& mesh) {
- // We separate the layer into 3 parts essentially, such that we only turn on blending for the
- // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
- FloatRect bounds = layer.geometry.roundedCornersCrop;
-
- // Explicitly compute the transform from the clip rectangle to the physical
- // display. Normally, this is done in glViewport but we explicitly compute
- // it here so that we can get the scissor bounds correct.
- const Rect& source = display.clip;
- const Rect& destination = display.physicalDisplay;
- // Here we compute the following transform:
- // 1. Translate the top left corner of the source clip to (0, 0)
- // 2. Rotate the clip rectangle about the origin in accordance with the
- // orientation flag
- // 3. Translate the top left corner back to the origin.
- // 4. Scale the clip rectangle to the destination rectangle dimensions
- // 5. Translate the top left corner to the destination rectangle's top left
- // corner.
- const mat4 translateSource = mat4::translate(vec4(-source.left, -source.top, 0, 1));
- mat4 rotation;
- int displacementX = 0;
- int displacementY = 0;
- float destinationWidth = static_cast<float>(destination.getWidth());
- float destinationHeight = static_cast<float>(destination.getHeight());
- float sourceWidth = static_cast<float>(source.getWidth());
- float sourceHeight = static_cast<float>(source.getHeight());
- const float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
- switch (display.orientation) {
- case ui::Transform::ROT_90:
- rotation = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
- displacementX = source.getHeight();
- std::swap(sourceHeight, sourceWidth);
- break;
- case ui::Transform::ROT_180:
- rotation = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
- displacementY = source.getHeight();
- displacementX = source.getWidth();
- break;
- case ui::Transform::ROT_270:
- rotation = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
- displacementY = source.getWidth();
- std::swap(sourceHeight, sourceWidth);
- break;
- default:
- break;
- }
-
- const mat4 intermediateTranslation = mat4::translate(vec4(displacementX, displacementY, 0, 1));
- const mat4 scale = mat4::scale(
- vec4(destinationWidth / sourceWidth, destinationHeight / sourceHeight, 1, 1));
- const mat4 translateDestination =
- mat4::translate(vec4(destination.left, destination.top, 0, 1));
- const mat4 globalTransform =
- translateDestination * scale * intermediateTranslation * rotation * translateSource;
-
- const mat4 transformMatrix = globalTransform * layer.geometry.positionTransform;
- const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
- const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
- const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
- const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
- bounds = FloatRect(std::min(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
- std::min(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]),
- std::max(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
- std::max(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]));
-
- // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
- // and the middle part without rounded corners.
- const int32_t radius = ceil(
- (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0);
- const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
- setScissor(topRect);
- drawMesh(mesh);
- const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom);
- setScissor(bottomRect);
- drawMesh(mesh);
-
- // The middle part of the layer can turn off blending.
- if (topRect.bottom < bottomRect.top) {
- const Rect middleRect(bounds.left, bounds.top + radius, bounds.right,
- bounds.bottom - radius);
- setScissor(middleRect);
- mState.cornerRadius = 0.0;
- disableBlending();
- drawMesh(mesh);
- }
- disableScissor();
-}
-
-status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
- ATRACE_CALL();
- GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
- EGLImageKHR eglImage = glFramebuffer->getEGLImage();
- uint32_t textureName = glFramebuffer->getTextureName();
- uint32_t framebufferName = glFramebuffer->getFramebufferName();
-
- // Bind the texture and turn our EGLImage into a texture
- glBindTexture(GL_TEXTURE_2D, textureName);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage);
-
- // Bind the Framebuffer to render into
- glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
-
- uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
- glStatus);
-
- return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
-}
-
-void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) {
- ATRACE_CALL();
-
- // back to main framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-bool GLESRenderEngine::canSkipPostRenderCleanup() const {
- return mPriorResourcesCleaned ||
- (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled);
-}
-
-void GLESRenderEngine::cleanupPostRender() {
- ATRACE_CALL();
-
- if (canSkipPostRenderCleanup()) {
- // If we don't have a prior frame needing cleanup, then don't do anything.
- return;
- }
-
- // Bind the texture to placeholder so that backing image data can be freed.
- GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
- glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
-
- // Release the cached fence here, so that we don't churn reallocations when
- // we could no-op repeated calls of this method instead.
- mLastDrawFence = nullptr;
- mPriorResourcesCleaned = true;
-}
-
-void GLESRenderEngine::cleanFramebufferCache() {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- // Bind the texture to placeholder so that backing image data can be freed.
- GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
- glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
-
- while (!mFramebufferImageCache.empty()) {
- EGLImageKHR expired = mFramebufferImageCache.front().second;
- mFramebufferImageCache.pop_front();
- eglDestroyImageKHR(mEGLDisplay, expired);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
-}
-
-void GLESRenderEngine::checkErrors() const {
- checkErrors(nullptr);
-}
-
-void GLESRenderEngine::checkErrors(const char* tag) const {
- do {
- // there could be more than one error flag
- GLenum error = glGetError();
- if (error == GL_NO_ERROR) break;
- if (tag == nullptr) {
- ALOGE("GL error 0x%04x", int(error));
- } else {
- ALOGE("GL error: %s -> 0x%04x", tag, int(error));
- }
- } while (true);
-}
-
-bool GLESRenderEngine::supportsProtectedContent() const {
- return mProtectedEGLContext != EGL_NO_CONTEXT;
-}
-
-void GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
- if (useProtectedContext == mInProtectedContext ||
- (useProtectedContext && !supportsProtectedContent())) {
- return;
- }
-
- const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface;
- const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
- if (eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE) {
- mInProtectedContext = useProtectedContext;
- }
-}
-EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
- bool isProtected,
- bool useFramebufferCache) {
- sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
- if (useFramebufferCache) {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- for (const auto& image : mFramebufferImageCache) {
- if (image.first == graphicBuffer->getId()) {
- return image.second;
- }
- }
- }
- EGLint attributes[] = {
- isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
- isProtected ? EGL_TRUE : EGL_NONE,
- EGL_NONE,
- };
- EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- nativeBuffer, attributes);
- if (useFramebufferCache) {
- if (image != EGL_NO_IMAGE_KHR) {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
- EGLImageKHR expired = mFramebufferImageCache.front().second;
- mFramebufferImageCache.pop_front();
- eglDestroyImageKHR(mEGLDisplay, expired);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
- mFramebufferImageCache.push_back({graphicBuffer->getId(), image});
- }
- }
-
- if (image != EGL_NO_IMAGE_KHR) {
- DEBUG_EGL_IMAGE_TRACKER_CREATE();
- }
- return image;
-}
-
-void GLESRenderEngine::drawLayersInternal(
- const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence) {
- ATRACE_CALL();
- if (layers.empty()) {
- ALOGV("Drawing empty layer stack");
- resultPromise->set_value(Fence::NO_FENCE);
- return;
- }
-
- if (bufferFence.get() >= 0) {
- // Duplicate the fence for passing to waitFence.
- base::unique_fd bufferFenceDup(dup(bufferFence.get()));
- if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
- ATRACE_NAME("Waiting before draw");
- sync_wait(bufferFence.get(), -1);
- }
- }
-
- if (buffer == nullptr) {
- ALOGE("No output buffer provided. Aborting GPU composition.");
- resultPromise->set_value(base::unexpected(BAD_VALUE));
- return;
- }
-
- validateOutputBufferUsage(buffer->getBuffer());
-
- std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
- // Gathering layers that requested blur, we'll need them to decide when to render to an
- // offscreen buffer, and when to render to the native buffer.
- std::deque<const LayerSettings> blurLayers;
- if (CC_LIKELY(mBlurFilter != nullptr)) {
- for (const auto& layer : layers) {
- if (layer.backgroundBlurRadius > 0) {
- blurLayers.push_back(layer);
- }
- }
- }
- const auto blurLayersSize = blurLayers.size();
-
- if (blurLayersSize == 0) {
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
- buffer->getBuffer()
- .get()
- ->getNativeBuffer(),
- useFramebufferCache);
- if (fbo->getStatus() != NO_ERROR) {
- ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors();
- resultPromise->set_value(base::unexpected(fbo->getStatus()));
- return;
- }
- setViewportAndProjection(display.physicalDisplay, display.clip);
- } else {
- setViewportAndProjection(display.physicalDisplay, display.clip);
- auto status =
- mBlurFilter->setAsDrawTarget(display, blurLayers.front().backgroundBlurRadius);
- if (status != NO_ERROR) {
- ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors();
- resultPromise->set_value(base::unexpected(status));
- return;
- }
- }
-
- // clear the entire buffer, sometimes when we reuse buffers we'd persist
- // ghost images otherwise.
- // we also require a full transparent framebuffer for overlays. This is
- // probably not quite efficient on all GPUs, since we could filter out
- // opaque layers.
- clearWithColor(0.0, 0.0, 0.0, 0.0);
-
- setOutputDataSpace(display.outputDataspace);
- setDisplayMaxLuminance(display.maxLuminance);
- setDisplayColorTransform(display.colorTransform);
-
- const mat4 projectionMatrix =
- ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
-
- Mesh mesh = Mesh::Builder()
- .setPrimitive(Mesh::TRIANGLE_FAN)
- .setVertices(4 /* count */, 2 /* size */)
- .setTexCoords(2 /* size */)
- .setCropCoords(2 /* size */)
- .build();
- for (const auto& layer : layers) {
- if (blurLayers.size() > 0 && blurLayers.front() == layer) {
- blurLayers.pop_front();
-
- auto status = mBlurFilter->prepare();
- if (status != NO_ERROR) {
- ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors("Can't render first blur pass");
- resultPromise->set_value(base::unexpected(status));
- return;
- }
-
- if (blurLayers.size() == 0) {
- // Done blurring, time to bind the native FBO and render our blur onto it.
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
- buffer.get()
- ->getBuffer()
- ->getNativeBuffer(),
- useFramebufferCache);
- status = fbo->getStatus();
- setViewportAndProjection(display.physicalDisplay, display.clip);
- } else {
- // There's still something else to blur, so let's keep rendering to our FBO
- // instead of to the display.
- status = mBlurFilter->setAsDrawTarget(display,
- blurLayers.front().backgroundBlurRadius);
- }
- if (status != NO_ERROR) {
- ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors("Can't bind native framebuffer");
- resultPromise->set_value(base::unexpected(status));
- return;
- }
-
- status = mBlurFilter->render(blurLayersSize > 1);
- if (status != NO_ERROR) {
- ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors("Can't render blur filter");
- resultPromise->set_value(base::unexpected(status));
- return;
- }
- }
-
- // Ensure luminance is at least 100 nits to avoid div-by-zero
- const float maxLuminance = std::max(100.f, layer.source.buffer.maxLuminanceNits);
- mState.maxMasteringLuminance = maxLuminance;
- mState.maxContentLuminance = maxLuminance;
- mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
-
- const FloatRect bounds = layer.geometry.boundaries;
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- position[0] = vec2(bounds.left, bounds.top);
- position[1] = vec2(bounds.left, bounds.bottom);
- position[2] = vec2(bounds.right, bounds.bottom);
- position[3] = vec2(bounds.right, bounds.top);
-
- setupLayerCropping(layer, mesh);
- setColorTransform(layer.colorTransform);
-
- bool usePremultipliedAlpha = true;
- bool disableTexture = true;
- bool isOpaque = false;
- if (layer.source.buffer.buffer != nullptr) {
- disableTexture = false;
- isOpaque = layer.source.buffer.isOpaque;
-
- sp<GraphicBuffer> gBuf = layer.source.buffer.buffer->getBuffer();
- validateInputBufferUsage(gBuf);
- bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
- layer.source.buffer.fence);
-
- usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
- Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
- mat4 texMatrix = layer.source.buffer.textureTransform;
-
- texture.setMatrix(texMatrix.asArray());
- texture.setFiltering(layer.source.buffer.useTextureFiltering);
-
- texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
- setSourceY410BT2020(layer.source.buffer.isY410BT2020);
-
- renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
- texCoords[0] = vec2(0.0, 0.0);
- texCoords[1] = vec2(0.0, 1.0);
- texCoords[2] = vec2(1.0, 1.0);
- texCoords[3] = vec2(1.0, 0.0);
- setupLayerTexturing(texture);
-
- // Do not cache protected EGLImage, protected memory is limited.
- if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
- unmapExternalTextureBuffer(std::move(gBuf));
- }
- }
-
- const half3 solidColor = layer.source.solidColor;
- const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
- const float radius =
- (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) /
- 2.0f;
- // Buffer sources will have a black solid color ignored in the shader,
- // so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius);
- if (layer.disableBlending) {
- glDisable(GL_BLEND);
- }
- setSourceDataSpace(layer.sourceDataspace);
-
- if (layer.shadow.length > 0.0f) {
- handleShadow(layer.geometry.boundaries, radius, layer.shadow);
- }
- // We only want to do a special handling for rounded corners when having rounded corners
- // is the only reason it needs to turn on blending, otherwise, we handle it like the
- // usual way since it needs to turn on blending anyway.
- else if (radius > 0.0 && color.a >= 1.0f && isOpaque) {
- handleRoundedCorners(display, layer, mesh);
- } else {
- drawMesh(mesh);
- }
-
- // Cleanup if there's a buffer source
- if (layer.source.buffer.buffer != nullptr) {
- disableBlending();
- setSourceY410BT2020(false);
- disableTexturing();
- }
- }
-
- base::unique_fd drawFence = flush();
-
- // If flush failed or we don't support native fences, we need to force the
- // gl command stream to be executed.
- if (drawFence.get() < 0) {
- bool success = finish();
- if (!success) {
- ALOGE("Failed to flush RenderEngine commands");
- checkErrors();
- // Chances are, something illegal happened (either the caller passed
- // us bad parameters, or we messed up our shader generation).
- resultPromise->set_value(base::unexpected(INVALID_OPERATION));
- return;
- }
- mLastDrawFence = nullptr;
- } else {
- // The caller takes ownership of drawFence, so we need to duplicate the
- // fd here.
- mLastDrawFence = new Fence(dup(drawFence.get()));
- }
- mPriorResourcesCleaned = false;
-
- checkErrors();
- resultPromise->set_value(sp<Fence>::make(std::move(drawFence)));
-}
-
-void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
- ATRACE_CALL();
- mVpWidth = viewport.getWidth();
- mVpHeight = viewport.getHeight();
-
- // We pass the the top left corner instead of the bottom left corner,
- // because since we're rendering off-screen first.
- glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight);
-
- mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1);
-}
-
-void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color, float cornerRadius) {
- mState.isPremultipliedAlpha = premultipliedAlpha;
- mState.isOpaque = opaque;
- mState.color = color;
- mState.cornerRadius = cornerRadius;
-
- if (disableTexture) {
- mState.textureEnabled = false;
- }
-
- if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
- glEnable(GL_BLEND);
- glBlendFuncSeparate(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
- GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
-void GLESRenderEngine::setSourceY410BT2020(bool enable) {
- mState.isY410BT2020 = enable;
-}
-
-void GLESRenderEngine::setSourceDataSpace(Dataspace source) {
- mDataSpace = source;
-}
-
-void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) {
- mOutputDataSpace = dataspace;
-}
-
-void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
- mState.displayMaxLuminance = maxLuminance;
-}
-
-void GLESRenderEngine::setupLayerTexturing(const Texture& texture) {
- GLuint target = texture.getTextureTarget();
- glBindTexture(target, texture.getTextureName());
- GLenum filter = GL_NEAREST;
- if (texture.getFiltering()) {
- filter = GL_LINEAR;
- }
- glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
-
- mState.texture = texture;
- mState.textureEnabled = true;
-}
-
-void GLESRenderEngine::setColorTransform(const mat4& colorTransform) {
- mState.colorMatrix = colorTransform;
-}
-
-void GLESRenderEngine::setDisplayColorTransform(const mat4& colorTransform) {
- mState.displayColorMatrix = colorTransform;
-}
-
-void GLESRenderEngine::disableTexturing() {
- mState.textureEnabled = false;
-}
-
-void GLESRenderEngine::disableBlending() {
- glDisable(GL_BLEND);
-}
-
-void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) {
- mState.isPremultipliedAlpha = true;
- mState.isOpaque = false;
- mState.color = half4(r, g, b, a);
- mState.textureEnabled = false;
- glDisable(GL_BLEND);
-}
-
-void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) {
- mState.cropSize = half2(width, height);
-}
-
-void GLESRenderEngine::drawMesh(const Mesh& mesh) {
- ATRACE_CALL();
- if (mesh.getTexCoordsSize()) {
- glEnableVertexAttribArray(Program::texCoords);
- glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getTexCoords());
- }
-
- glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getPositions());
-
- if (mState.cornerRadius > 0.0f) {
- glEnableVertexAttribArray(Program::cropCoords);
- glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getCropCoords());
- }
-
- if (mState.drawShadows) {
- glEnableVertexAttribArray(Program::shadowColor);
- glVertexAttribPointer(Program::shadowColor, mesh.getShadowColorSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getShadowColor());
-
- glEnableVertexAttribArray(Program::shadowParams);
- glVertexAttribPointer(Program::shadowParams, mesh.getShadowParamsSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getShadowParams());
- }
-
- Description managedState = mState;
- // By default, DISPLAY_P3 is the only supported wide color output. However,
- // when HDR content is present, hardware composer may be able to handle
- // BT2020 data space, in that case, the output data space is set to be
- // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
- // to respect this and convert non-HDR content to HDR format.
- if (mUseColorManagement) {
- Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
- Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
- Dataspace outputStandard =
- static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK);
- Dataspace outputTransfer =
- static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
- bool needsXYZConversion = needsXYZTransformMatrix();
-
- // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or
- // STANDARD_BT2020, it will be treated as STANDARD_BT709
- if (inputStandard != Dataspace::STANDARD_DCI_P3 &&
- inputStandard != Dataspace::STANDARD_BT2020) {
- inputStandard = Dataspace::STANDARD_BT709;
- }
-
- if (needsXYZConversion) {
- // The supported input color spaces are standard RGB, Display P3 and BT2020.
- switch (inputStandard) {
- case Dataspace::STANDARD_DCI_P3:
- managedState.inputTransformMatrix = mDisplayP3ToXyz;
- break;
- case Dataspace::STANDARD_BT2020:
- managedState.inputTransformMatrix = mBt2020ToXyz;
- break;
- default:
- managedState.inputTransformMatrix = mSrgbToXyz;
- break;
- }
-
- // The supported output color spaces are BT2020, Display P3 and standard RGB.
- switch (outputStandard) {
- case Dataspace::STANDARD_BT2020:
- managedState.outputTransformMatrix = mXyzToBt2020;
- break;
- case Dataspace::STANDARD_DCI_P3:
- managedState.outputTransformMatrix = mXyzToDisplayP3;
- break;
- default:
- managedState.outputTransformMatrix = mXyzToSrgb;
- break;
- }
- } else if (inputStandard != outputStandard) {
- // At this point, the input data space and output data space could be both
- // HDR data spaces, but they match each other, we do nothing in this case.
- // In addition to the case above, the input data space could be
- // - scRGB linear
- // - scRGB non-linear
- // - sRGB
- // - Display P3
- // - BT2020
- // The output data spaces could be
- // - sRGB
- // - Display P3
- // - BT2020
- switch (outputStandard) {
- case Dataspace::STANDARD_BT2020:
- if (inputStandard == Dataspace::STANDARD_BT709) {
- managedState.outputTransformMatrix = mSrgbToBt2020;
- } else if (inputStandard == Dataspace::STANDARD_DCI_P3) {
- managedState.outputTransformMatrix = mDisplayP3ToBt2020;
- }
- break;
- case Dataspace::STANDARD_DCI_P3:
- if (inputStandard == Dataspace::STANDARD_BT709) {
- managedState.outputTransformMatrix = mSrgbToDisplayP3;
- } else if (inputStandard == Dataspace::STANDARD_BT2020) {
- managedState.outputTransformMatrix = mBt2020ToDisplayP3;
- }
- break;
- default:
- if (inputStandard == Dataspace::STANDARD_DCI_P3) {
- managedState.outputTransformMatrix = mDisplayP3ToSrgb;
- } else if (inputStandard == Dataspace::STANDARD_BT2020) {
- managedState.outputTransformMatrix = mBt2020ToSrgb;
- }
- break;
- }
- }
-
- // we need to convert the RGB value to linear space and convert it back when:
- // - there is a color matrix that is not an identity matrix, or
- // - there is an output transform matrix that is not an identity matrix, or
- // - the input transfer function doesn't match the output transfer function.
- if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() ||
- inputTransfer != outputTransfer) {
- managedState.inputTransferFunction =
- Description::dataSpaceToTransferFunction(inputTransfer);
- managedState.outputTransferFunction =
- Description::dataSpaceToTransferFunction(outputTransfer);
- }
- }
-
- ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
- managedState);
-
- if (mState.drawShadows) {
- glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT,
- mesh.getIndices());
- } else {
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
- }
-
- if (mUseColorManagement && outputDebugPPMs) {
- static uint64_t managedColorFrameCount = 0;
- std::ostringstream out;
- out << "/data/texture_out" << managedColorFrameCount++;
- writePPM(out.str().c_str(), mVpWidth, mVpHeight);
- }
-
- if (mesh.getTexCoordsSize()) {
- glDisableVertexAttribArray(Program::texCoords);
- }
-
- if (mState.cornerRadius > 0.0f) {
- glDisableVertexAttribArray(Program::cropCoords);
- }
-
- if (mState.drawShadows) {
- glDisableVertexAttribArray(Program::shadowColor);
- glDisableVertexAttribArray(Program::shadowParams);
- }
-}
-
-size_t GLESRenderEngine::getMaxTextureSize() const {
- return mMaxTextureSize;
-}
-
-size_t GLESRenderEngine::getMaxViewportDims() const {
- return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
-}
-
-void GLESRenderEngine::dump(std::string& result) {
- const GLExtensions& extensions = GLExtensions::getInstance();
- ProgramCache& cache = ProgramCache::getInstance();
-
- StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
- StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
- StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
- extensions.getVersion());
- StringAppendF(&result, "%s\n", extensions.getExtensions());
- StringAppendF(&result, "RenderEngine supports protected context: %d\n",
- supportsProtectedContent());
- StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
- StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n",
- cache.getSize(mEGLContext));
- StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n",
- cache.getSize(mProtectedEGLContext));
- StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n",
- dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
- dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size());
- StringAppendF(&result, "Dumping buffer ids...\n");
- for (const auto& [id, unused] : mImageCache) {
- StringAppendF(&result, "0x%" PRIx64 "\n", id);
- }
- }
- {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n",
- mFramebufferImageCache.size());
- StringAppendF(&result, "Dumping buffer ids...\n");
- for (const auto& [id, unused] : mFramebufferImageCache) {
- StringAppendF(&result, "0x%" PRIx64 "\n", id);
- }
- }
-}
-
-GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) {
- int major, minor;
- if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
- if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
- ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
- return GLES_VERSION_1_0;
- }
- }
-
- if (major == 1 && minor == 0) return GLES_VERSION_1_0;
- if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
- if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
- if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
-
- ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
- return GLES_VERSION_1_0;
-}
-
-EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext,
- std::optional<ContextPriority> contextPriority,
- Protection protection) {
- EGLint renderableType = 0;
- if (config == EGL_NO_CONFIG) {
- renderableType = EGL_OPENGL_ES3_BIT;
- } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
- LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
- }
- EGLint contextClientVersion = 0;
- if (renderableType & EGL_OPENGL_ES3_BIT) {
- contextClientVersion = 3;
- } else if (renderableType & EGL_OPENGL_ES2_BIT) {
- contextClientVersion = 2;
- } else if (renderableType & EGL_OPENGL_ES_BIT) {
- contextClientVersion = 1;
- } else {
- LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
- }
-
- std::vector<EGLint> contextAttributes;
- contextAttributes.reserve(7);
- contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
- contextAttributes.push_back(contextClientVersion);
- if (contextPriority) {
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
- switch (*contextPriority) {
- case ContextPriority::REALTIME:
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
- break;
- case ContextPriority::MEDIUM:
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
- break;
- case ContextPriority::LOW:
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
- break;
- case ContextPriority::HIGH:
- default:
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
- break;
- }
- }
- if (protection == Protection::PROTECTED) {
- contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
- contextAttributes.push_back(EGL_TRUE);
- }
- contextAttributes.push_back(EGL_NONE);
-
- EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
-
- if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
- // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
- // EGL_NO_CONTEXT so that we can abort.
- if (config != EGL_NO_CONFIG) {
- return context;
- }
- // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should
- // try to fall back to GLES 2.
- contextAttributes[1] = 2;
- context = eglCreateContext(display, config, shareContext, contextAttributes.data());
- }
-
- return context;
-}
-
-EGLSurface GLESRenderEngine::createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection) {
- EGLConfig stubConfig = config;
- if (stubConfig == EGL_NO_CONFIG) {
- stubConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
- }
- std::vector<EGLint> attributes;
- attributes.reserve(7);
- attributes.push_back(EGL_WIDTH);
- attributes.push_back(1);
- attributes.push_back(EGL_HEIGHT);
- attributes.push_back(1);
- if (protection == Protection::PROTECTED) {
- attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
- attributes.push_back(EGL_TRUE);
- }
- attributes.push_back(EGL_NONE);
-
- return eglCreatePbufferSurface(display, stubConfig, attributes.data());
-}
-
-bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
- const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
- const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
- return standard == Dataspace::STANDARD_BT2020 &&
- (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
-}
-
-// For convenience, we want to convert the input color space to XYZ color space first,
-// and then convert from XYZ color space to output color space when
-// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
-// HDR content will be tone-mapped to SDR; Or,
-// - there are HDR PQ and HLG contents presented at the same time, where we want to convert
-// HLG content to PQ content.
-// In either case above, we need to operate the Y value in XYZ color space. Thus, when either
-// input data space or output data space is HDR data space, and the input transfer function
-// doesn't match the output transfer function, we would enable an intermediate transfrom to
-// XYZ color space.
-bool GLESRenderEngine::needsXYZTransformMatrix() const {
- const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
- const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
- const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
- const Dataspace outputTransfer =
- static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
-
- return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
-}
-
-bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- const auto& cachedImage = mImageCache.find(bufferId);
- return cachedImage != mImageCache.end();
-}
-
-bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
- const auto& entry = mTextureView.find(texName);
- return entry != mTextureView.end();
-}
-
-std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
- const auto& entry = mTextureView.find(texName);
- return entry != mTextureView.end() ? entry->second : std::nullopt;
-}
-
-bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
- [=](std::pair<uint64_t, EGLImageKHR> image) {
- return image.first == bufferId;
- });
-}
-
-// FlushTracer implementation
-GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
- mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
-}
-
-GLESRenderEngine::FlushTracer::~FlushTracer() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mRunning = false;
- }
- mCondition.notify_all();
- if (mThread.joinable()) {
- mThread.join();
- }
-}
-
-void GLESRenderEngine::FlushTracer::queueSync(EGLSyncKHR sync) {
- std::lock_guard<std::mutex> lock(mMutex);
- char name[64];
- const uint64_t frameNum = mFramesQueued++;
- snprintf(name, sizeof(name), "Queueing sync for frame: %lu",
- static_cast<unsigned long>(frameNum));
- ATRACE_NAME(name);
- mQueue.push({sync, frameNum});
- ATRACE_INT("GPU Frames Outstanding", mQueue.size());
- mCondition.notify_one();
-}
-
-void GLESRenderEngine::FlushTracer::loop() {
- while (mRunning) {
- QueueEntry entry;
- {
- std::lock_guard<std::mutex> lock(mMutex);
-
- mCondition.wait(mMutex,
- [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
-
- if (!mRunning) {
- // if mRunning is false, then FlushTracer is being destroyed, so
- // bail out now.
- break;
- }
- entry = mQueue.front();
- mQueue.pop();
- }
- {
- char name[64];
- snprintf(name, sizeof(name), "waiting for frame %lu",
- static_cast<unsigned long>(entry.mFrameNum));
- ATRACE_NAME(name);
- mEngine->waitSync(entry.mSync, 0);
- }
- }
-}
-
-void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius,
- const ShadowSettings& settings) {
- ATRACE_CALL();
- const float casterZ = settings.length / 2.0f;
- const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ,
- settings.casterIsTranslucent, settings.ambientColor,
- settings.spotColor, settings.lightPos,
- settings.lightRadius);
-
- // setup mesh for both shadows
- Mesh mesh = Mesh::Builder()
- .setPrimitive(Mesh::TRIANGLES)
- .setVertices(shadows.getVertexCount(), 2 /* size */)
- .setShadowAttrs()
- .setIndices(shadows.getIndexCount())
- .build();
-
- Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>();
- Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>();
- Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>();
- shadows.fillVertices(position, shadowColor, shadowParams);
- shadows.fillIndices(mesh.getIndicesArray());
-
- mState.cornerRadius = 0.0f;
- mState.drawShadows = true;
- setupLayerTexturing(mShadowTexture->getTexture());
- drawMesh(mesh);
- mState.drawShadows = false;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
deleted file mode 100644
index 402ff529d7..0000000000
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_GLESRENDERENGINE_H_
-#define SF_GLESRENDERENGINE_H_
-
-#include <condition_variable>
-#include <deque>
-#include <mutex>
-#include <queue>
-#include <thread>
-#include <unordered_map>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-#include <android-base/thread_annotations.h>
-#include <renderengine/RenderEngine.h>
-#include <renderengine/private/Description.h>
-#include <sys/types.h>
-#include <ui/FenceResult.h>
-#include "GLShadowTexture.h"
-#include "ImageManager.h"
-
-#define EGL_NO_CONFIG ((EGLConfig)0)
-
-namespace android {
-
-namespace renderengine {
-
-class Mesh;
-class Texture;
-
-namespace gl {
-
-class GLImage;
-class BlurFilter;
-
-class GLESRenderEngine : public RenderEngine {
-public:
- static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
-
- GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
- EGLContext ctxt, EGLSurface stub, EGLContext protectedContext,
- EGLSurface protectedStub);
- ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
-
- std::future<void> primeCache() override;
- void genTextures(size_t count, uint32_t* names) override;
- void deleteTextures(size_t count, uint32_t const* names) override;
- bool isProtected() const { return mInProtectedContext; }
- bool supportsProtectedContent() const override;
- void useProtectedContext(bool useProtectedContext) override;
- void cleanupPostRender() override;
- int getContextPriority() override;
- bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
- void onActiveDisplaySizeChanged(ui::Size size) override {}
-
- EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
- // Creates an output image for rendering to
- EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- bool useFramebufferCache)
- EXCLUDES(mFramebufferImageCacheMutex);
-
- // Test-only methods
- // Returns true iff mImageCache contains an image keyed by bufferId
- bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
- // Returns true iff texName was previously generated by RenderEngine and was
- // not destroyed.
- bool isTextureNameKnownForTesting(uint32_t texName);
- // Returns the buffer ID of the content bound to texName, or nullopt if no
- // such mapping exists.
- std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName);
- // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
- bool isFramebufferImageCachedForTesting(uint64_t bufferId)
- EXCLUDES(mFramebufferImageCacheMutex);
- // These are wrappers around public methods above, but exposing Barrier
- // objects so that tests can block.
- std::shared_ptr<ImageManager::Barrier> cacheExternalTextureBufferForTesting(
- const sp<GraphicBuffer>& buffer);
- std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId);
-
-protected:
- Framebuffer* getFramebufferForDrawing();
- void dump(std::string& result) override EXCLUDES(mRenderingMutex)
- EXCLUDES(mFramebufferImageCacheMutex);
- size_t getMaxTextureSize() const override;
- size_t getMaxViewportDims() const override;
- void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
- EXCLUDES(mRenderingMutex);
- void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) EXCLUDES(mRenderingMutex);
- bool canSkipPostRenderCleanup() const override;
- void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const DisplaySettings& display,
- const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
-
-private:
- friend class BindNativeBufferAsFramebuffer;
-
- enum GlesVersion {
- GLES_VERSION_1_0 = 0x10000,
- GLES_VERSION_1_1 = 0x10001,
- GLES_VERSION_2_0 = 0x20000,
- GLES_VERSION_3_0 = 0x30000,
- };
-
- static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
- static GlesVersion parseGlesVersion(const char* str);
- static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext,
- std::optional<ContextPriority> contextPriority,
- Protection protection);
- static std::optional<RenderEngine::ContextPriority> createContextPriority(
- const RenderEngineCreationArgs& args);
- static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection);
- std::unique_ptr<Framebuffer> createFramebuffer();
- std::unique_ptr<Image> createImage();
- void checkErrors() const;
- void checkErrors(const char* tag) const;
- void setScissor(const Rect& region);
- void disableScissor();
- bool waitSync(EGLSyncKHR sync, EGLint flags);
- status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer)
- EXCLUDES(mRenderingMutex);
- void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex);
- status_t bindFrameBuffer(Framebuffer* framebuffer);
- void unbindFrameBuffer(Framebuffer* framebuffer);
- void bindExternalTextureImage(uint32_t texName, const Image& image);
- void bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
- void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override;
-
- // A data space is considered HDR data space if it has BT2020 color space
- // with PQ or HLG transfer function.
- bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
- bool needsXYZTransformMatrix() const;
- // Defines the viewport, and sets the projection matrix to the projection
- // defined by the clip.
- void setViewportAndProjection(Rect viewport, Rect clip);
- // Evicts stale images from the buffer cache.
- void evictImages(const std::vector<LayerSettings>& layers);
- // Computes the cropping window for the layer and sets up cropping
- // coordinates for the mesh.
- FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
-
- // We do a special handling for rounded corners when it's possible to turn off blending
- // for the majority of the layer. The rounded corners needs to turn on blending such that
- // we can set the alpha value correctly, however, only the corners need this, and since
- // blending is an expensive operation, we want to turn off blending when it's not necessary.
- void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
- const Mesh& mesh);
- base::unique_fd flush();
- bool finish();
- bool waitFence(base::unique_fd fenceFd);
- void clearWithColor(float red, float green, float blue, float alpha);
- void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha);
- void handleShadow(const FloatRect& casterRect, float casterCornerRadius,
- const ShadowSettings& shadowSettings);
- void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color, float cornerRadius);
- void setupLayerTexturing(const Texture& texture);
- void setupFillWithColor(float r, float g, float b, float a);
- void setColorTransform(const mat4& colorTransform);
- void setDisplayColorTransform(const mat4& colorTransform);
- void disableTexturing();
- void disableBlending();
- void setupCornerRadiusCropSize(float width, float height);
-
- // HDR and color management related functions and state
- void setSourceY410BT2020(bool enable);
- void setSourceDataSpace(ui::Dataspace source);
- void setOutputDataSpace(ui::Dataspace dataspace);
- void setDisplayMaxLuminance(const float maxLuminance);
-
- // drawing
- void drawMesh(const Mesh& mesh);
-
- EGLDisplay mEGLDisplay;
- EGLConfig mEGLConfig;
- EGLContext mEGLContext;
- EGLSurface mStubSurface;
- EGLContext mProtectedEGLContext;
- EGLSurface mProtectedStubSurface;
- GLint mMaxViewportDims[2];
- GLint mMaxTextureSize;
- GLuint mVpWidth;
- GLuint mVpHeight;
- Description mState;
- std::unique_ptr<GLShadowTexture> mShadowTexture = nullptr;
-
- mat4 mSrgbToXyz;
- mat4 mDisplayP3ToXyz;
- mat4 mBt2020ToXyz;
- mat4 mXyzToSrgb;
- mat4 mXyzToDisplayP3;
- mat4 mXyzToBt2020;
- mat4 mSrgbToDisplayP3;
- mat4 mSrgbToBt2020;
- mat4 mDisplayP3ToSrgb;
- mat4 mDisplayP3ToBt2020;
- mat4 mBt2020ToSrgb;
- mat4 mBt2020ToDisplayP3;
-
- bool mInProtectedContext = false;
- // If set to true, then enables tracing flush() and finish() to systrace.
- bool mTraceGpuCompletion = false;
- // Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately)
- // the last recently used buffer should be kicked out.
- uint32_t mFramebufferImageCacheSize = 0;
-
- // Cache of output images, keyed by corresponding GraphicBuffer ID.
- std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache
- GUARDED_BY(mFramebufferImageCacheMutex);
- // The only reason why we have this mutex is so that we don't segfault when
- // dumping info.
- std::mutex mFramebufferImageCacheMutex;
-
- // Current dataspace of layer being rendered
- ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
-
- // Current output dataspace of the render engine
- ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN;
-
- // Whether device supports color management, currently color management
- // supports sRGB, DisplayP3 color spaces.
- const bool mUseColorManagement = false;
-
- // Whether only shaders performing tone mapping from HDR to SDR will be generated on
- // primeCache().
- const bool mPrecacheToneMapperShaderOnly = false;
-
- // Cache of GL images that we'll store per GraphicBuffer ID
- std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
- std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
-
- // Mutex guarding rendering operations, so that:
- // 1. GL operations aren't interleaved, and
- // 2. Internal state related to rendering that is potentially modified by
- // multiple threads is guaranteed thread-safe.
- std::mutex mRenderingMutex;
-
- std::unique_ptr<Framebuffer> mDrawingBuffer;
- // this is a 1x1 RGB buffer, but over-allocate in case a driver wants more
- // memory or if it needs to satisfy alignment requirements. In this case:
- // assume that each channel requires 4 bytes, and add 3 additional bytes to
- // ensure that we align on a word. Allocating 16 bytes will provide a
- // guarantee that we don't clobber memory.
- uint32_t mPlaceholderDrawBuffer[4];
- // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but
- // instead these are intended for cleaning up texture memory with the
- // GL_TEXTURE_EXTERNAL_OES target.
- ANativeWindowBuffer* mPlaceholderBuffer = nullptr;
- EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR;
- sp<Fence> mLastDrawFence;
- // Store a separate boolean checking if prior resources were cleaned up, as
- // devices that don't support native sync fences can't rely on a last draw
- // fence that doesn't exist.
- bool mPriorResourcesCleaned = true;
-
- // Blur effect processor, only instantiated when a layer requests it.
- BlurFilter* mBlurFilter = nullptr;
-
- class FlushTracer {
- public:
- FlushTracer(GLESRenderEngine* engine);
- ~FlushTracer();
- void queueSync(EGLSyncKHR sync) EXCLUDES(mMutex);
-
- struct QueueEntry {
- EGLSyncKHR mSync = nullptr;
- uint64_t mFrameNum = 0;
- };
-
- private:
- void loop();
- GLESRenderEngine* const mEngine;
- std::thread mThread;
- std::condition_variable_any mCondition;
- std::mutex mMutex;
- std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
- uint64_t mFramesQueued GUARDED_BY(mMutex) = 0;
- bool mRunning = true;
- };
- friend class FlushTracer;
- friend class ImageManager;
- friend class GLFramebuffer;
- friend class BlurFilter;
- friend class GenericProgram;
- std::unique_ptr<FlushTracer> mFlushTracer;
- std::unique_ptr<ImageManager> mImageManager;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
-
-#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
deleted file mode 100644
index 58d6caa48a..0000000000
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "GLFramebuffer.h"
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2ext.h>
-#include <GLES3/gl3.h>
-#include <gui/DebugEGLImageTracker.h>
-#include <nativebase/nativebase.h>
-#include <utils/Trace.h>
-#include "GLESRenderEngine.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
- : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
- glGenTextures(1, &mTextureName);
- glGenFramebuffers(1, &mFramebufferName);
-}
-
-GLFramebuffer::~GLFramebuffer() {
- setNativeWindowBuffer(nullptr, false, false);
- glDeleteFramebuffers(1, &mFramebufferName);
- glDeleteTextures(1, &mTextureName);
-}
-
-bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- const bool useFramebufferCache) {
- ATRACE_CALL();
- if (mEGLImage != EGL_NO_IMAGE_KHR) {
- if (!usingFramebufferCache) {
- eglDestroyImageKHR(mEGLDisplay, mEGLImage);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
- mEGLImage = EGL_NO_IMAGE_KHR;
- mBufferWidth = 0;
- mBufferHeight = 0;
- }
-
- if (nativeBuffer) {
- mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected,
- useFramebufferCache);
- if (mEGLImage == EGL_NO_IMAGE_KHR) {
- return false;
- }
- usingFramebufferCache = useFramebufferCache;
- mBufferWidth = nativeBuffer->width;
- mBufferHeight = nativeBuffer->height;
- }
- return true;
-}
-
-void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height, void* data) {
- ATRACE_CALL();
-
- glBindTexture(GL_TEXTURE_2D, mTextureName);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
-
- mBufferHeight = height;
- mBufferWidth = width;
- mEngine.checkErrors("Allocating Fbo texture");
-
- bind();
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
- mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- unbind();
- glBindTexture(GL_TEXTURE_2D, 0);
-
- if (mStatus != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Frame buffer is not complete. Error %d", mStatus);
- }
-}
-
-void GLFramebuffer::bind() const {
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
-}
-
-void GLFramebuffer::bindAsReadBuffer() const {
- glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
-}
-
-void GLFramebuffer::bindAsDrawBuffer() const {
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
-}
-
-void GLFramebuffer::unbind() const {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
deleted file mode 100644
index 6757695ddb..0000000000
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-#include <renderengine/Framebuffer.h>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLESRenderEngine;
-
-class GLFramebuffer : public renderengine::Framebuffer {
-public:
- explicit GLFramebuffer(GLESRenderEngine& engine);
- explicit GLFramebuffer(GLESRenderEngine& engine, bool multiTarget);
- ~GLFramebuffer() override;
-
- bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- const bool useFramebufferCache) override;
- void allocateBuffers(uint32_t width, uint32_t height, void* data = nullptr);
- EGLImageKHR getEGLImage() const { return mEGLImage; }
- uint32_t getTextureName() const { return mTextureName; }
- uint32_t getFramebufferName() const { return mFramebufferName; }
- int32_t getBufferHeight() const { return mBufferHeight; }
- int32_t getBufferWidth() const { return mBufferWidth; }
- GLenum getStatus() const { return mStatus; }
- void bind() const;
- void bindAsReadBuffer() const;
- void bindAsDrawBuffer() const;
- void unbind() const;
-
-private:
- GLESRenderEngine& mEngine;
- EGLDisplay mEGLDisplay;
- EGLImageKHR mEGLImage;
- bool usingFramebufferCache = false;
- GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
- uint32_t mTextureName, mFramebufferName;
-
- int32_t mBufferHeight = 0;
- int32_t mBufferWidth = 0;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
deleted file mode 100644
index 8497721956..0000000000
--- a/libs/renderengine/gl/GLImage.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "GLImage.h"
-
-#include <vector>
-
-#include <gui/DebugEGLImageTracker.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-#include "GLESRenderEngine.h"
-#include "GLExtensions.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-static std::vector<EGLint> buildAttributeList(bool isProtected) {
- std::vector<EGLint> attrs;
- attrs.reserve(16);
-
- attrs.push_back(EGL_IMAGE_PRESERVED_KHR);
- attrs.push_back(EGL_TRUE);
-
- if (isProtected && GLExtensions::getInstance().hasProtectedContent()) {
- attrs.push_back(EGL_PROTECTED_CONTENT_EXT);
- attrs.push_back(EGL_TRUE);
- }
-
- attrs.push_back(EGL_NONE);
-
- return attrs;
-}
-
-GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
-
-GLImage::~GLImage() {
- setNativeWindowBuffer(nullptr, false);
-}
-
-bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
- ATRACE_CALL();
- if (mEGLImage != EGL_NO_IMAGE_KHR) {
- if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
- ALOGE("failed to destroy image: %#x", eglGetError());
- }
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- mEGLImage = EGL_NO_IMAGE_KHR;
- }
-
- if (buffer) {
- std::vector<EGLint> attrs = buildAttributeList(isProtected);
- mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- static_cast<EGLClientBuffer>(buffer), attrs.data());
- if (mEGLImage == EGL_NO_IMAGE_KHR) {
- ALOGE("failed to create EGLImage: %#x", eglGetError());
- return false;
- }
- DEBUG_EGL_IMAGE_TRACKER_CREATE();
- mProtected = isProtected;
- }
-
- return true;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h
deleted file mode 100644
index 59d6ce3549..0000000000
--- a/libs/renderengine/gl/GLImage.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <android-base/macros.h>
-#include <renderengine/Image.h>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLESRenderEngine;
-
-class GLImage : public renderengine::Image {
-public:
- explicit GLImage(const GLESRenderEngine& engine);
- ~GLImage() override;
-
- bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override;
-
- EGLImageKHR getEGLImage() const { return mEGLImage; }
- bool isProtected() const { return mProtected; }
-
-private:
- EGLDisplay mEGLDisplay;
- EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
- bool mProtected = false;
-
- DISALLOW_COPY_AND_ASSIGN(GLImage);
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLShadowTexture.cpp b/libs/renderengine/gl/GLShadowTexture.cpp
deleted file mode 100644
index 2423a3467e..0000000000
--- a/libs/renderengine/gl/GLShadowTexture.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GLES3/gl3.h>
-
-#include "GLShadowTexture.h"
-#include "GLSkiaShadowPort.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLShadowTexture::GLShadowTexture() {
- fillShadowTextureData(mTextureData, SHADOW_TEXTURE_WIDTH);
-
- glGenTextures(1, &mName);
- glBindTexture(GL_TEXTURE_2D, mName);
- glTexImage2D(GL_TEXTURE_2D, 0 /* base image level */, GL_ALPHA, SHADOW_TEXTURE_WIDTH,
- SHADOW_TEXTURE_HEIGHT, 0 /* border */, GL_ALPHA, GL_UNSIGNED_BYTE, mTextureData);
- mTexture.init(Texture::TEXTURE_2D, mName);
- mTexture.setFiltering(true);
- mTexture.setDimensions(SHADOW_TEXTURE_WIDTH, 1);
-}
-
-GLShadowTexture::~GLShadowTexture() {
- glDeleteTextures(1, &mName);
-}
-
-const Texture& GLShadowTexture::getTexture() {
- return mTexture;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLShadowTexture.h b/libs/renderengine/gl/GLShadowTexture.h
deleted file mode 100644
index 250a9d77d0..0000000000
--- a/libs/renderengine/gl/GLShadowTexture.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <renderengine/Texture.h>
-#include <cstdint>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLShadowTexture {
-public:
- GLShadowTexture();
- ~GLShadowTexture();
-
- const Texture& getTexture();
-
-private:
- static constexpr int SHADOW_TEXTURE_WIDTH = 128;
- static constexpr int SHADOW_TEXTURE_HEIGHT = 1;
-
- GLuint mName;
- Texture mTexture;
- uint8_t mTextureData[SHADOW_TEXTURE_WIDTH];
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.cpp b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
deleted file mode 100644
index 3181f9bebb..0000000000
--- a/libs/renderengine/gl/GLShadowVertexGenerator.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 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 <renderengine/Mesh.h>
-
-#include <math/vec4.h>
-
-#include <ui/Rect.h>
-#include <ui/Transform.h>
-
-#include "GLShadowVertexGenerator.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent, const vec4& ambientColor,
- const vec4& spotColor, const vec3& lightPosition,
- float lightRadius) {
- mDrawAmbientShadow = ambientColor.a > 0.f;
- mDrawSpotShadow = spotColor.a > 0.f;
-
- // Generate geometries and find number of vertices to generate
- if (mDrawAmbientShadow) {
- mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ,
- casterIsTranslucent, ambientColor);
- mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get());
- mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get());
- } else {
- mAmbientShadowVertexCount = 0;
- mAmbientShadowIndexCount = 0;
- }
-
- if (mDrawSpotShadow) {
- mSpotShadowGeometry =
- getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent,
- spotColor, lightPosition, lightRadius);
- mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get());
- mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get());
- } else {
- mSpotShadowVertexCount = 0;
- mSpotShadowIndexCount = 0;
- }
-}
-
-size_t GLShadowVertexGenerator::getVertexCount() const {
- return mAmbientShadowVertexCount + mSpotShadowVertexCount;
-}
-
-size_t GLShadowVertexGenerator::getIndexCount() const {
- return mAmbientShadowIndexCount + mSpotShadowIndexCount;
-}
-
-void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position,
- Mesh::VertexArray<vec4>& color,
- Mesh::VertexArray<vec3>& params) const {
- if (mDrawAmbientShadow) {
- fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position,
- color, params);
- }
- if (mDrawSpotShadow) {
- fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount,
- Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount),
- Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount),
- Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount));
- }
-}
-
-void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const {
- if (mDrawAmbientShadow) {
- fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount,
- 0 /* starting vertex offset */, indices);
- }
- if (mDrawSpotShadow) {
- fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount,
- mAmbientShadowVertexCount /* starting vertex offset */,
- &(indices[mAmbientShadowIndexCount]));
- }
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.h b/libs/renderengine/gl/GLShadowVertexGenerator.h
deleted file mode 100644
index 112f97623b..0000000000
--- a/libs/renderengine/gl/GLShadowVertexGenerator.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 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 <math/vec4.h>
-#include <ui/Rect.h>
-
-#include "GLSkiaShadowPort.h"
-
-namespace android {
-namespace renderengine {
-
-class Mesh;
-
-namespace gl {
-
-/**
- * Generates gl attributes required to draw shadow spot and/or ambient shadows.
- *
- * Each shadow can support different colors. This class generates three vertex attributes for
- * each shadow, its position, color and shadow params(offset and distance). These can be sent
- * using a single glDrawElements call.
- */
-class GLShadowVertexGenerator {
-public:
- GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ,
- bool casterIsTranslucent, const vec4& ambientColor,
- const vec4& spotColor, const vec3& lightPosition, float lightRadius);
- ~GLShadowVertexGenerator() = default;
-
- size_t getVertexCount() const;
- size_t getIndexCount() const;
- void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color,
- Mesh::VertexArray<vec3>& params) const;
- void fillIndices(uint16_t* indices) const;
-
-private:
- bool mDrawAmbientShadow;
- std::unique_ptr<Geometry> mAmbientShadowGeometry;
- int mAmbientShadowVertexCount = 0;
- int mAmbientShadowIndexCount = 0;
-
- bool mDrawSpotShadow;
- std::unique_ptr<Geometry> mSpotShadowGeometry;
- int mSpotShadowVertexCount = 0;
- int mSpotShadowIndexCount = 0;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
deleted file mode 100644
index da8b435854..0000000000
--- a/libs/renderengine/gl/GLSkiaShadowPort.cpp
+++ /dev/null
@@ -1,656 +0,0 @@
-/*
- * Copyright 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 <math/vec4.h>
-
-#include <renderengine/Mesh.h>
-
-#include <ui/Rect.h>
-#include <ui/Transform.h>
-
-#include <utils/Log.h>
-
-#include "GLSkiaShadowPort.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-/**
- * The shadow geometry logic and vertex generation code has been ported from skia shadow
- * fast path OpenGL implementation to draw shadows around rects and rounded rects including
- * circles.
- *
- * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
- *
- * Modifications made:
- * - Switched to using std lib math functions
- * - Fall off function is implemented in vertex shader rather than a shadow texture
- * - Removed transformations applied on the caster rect since the caster will be in local
- * coordinate space and will be transformed by the vertex shader.
- */
-
-static inline float divide_and_pin(float numer, float denom, float min, float max) {
- if (denom == 0.0f) return min;
- return std::clamp(numer / denom, min, max);
-}
-
-static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
-static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
-static constexpr auto kAmbientGeomFactor = 64.0f;
-// Assuming that we have a light height of 600 for the spot shadow,
-// the spot values will reach their maximum at a height of approximately 292.3077.
-// We'll round up to 300 to keep it simple.
-static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
-
-inline float AmbientBlurRadius(float height) {
- return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
-}
-inline float AmbientRecipAlpha(float height) {
- return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Circle Data
-//
-// We have two possible cases for geometry for a circle:
-
-// In the case of a normal fill, we draw geometry for the circle as an octagon.
-static const uint16_t gFillCircleIndices[] = {
- // enter the octagon
- // clang-format off
- 0, 1, 8, 1, 2, 8,
- 2, 3, 8, 3, 4, 8,
- 4, 5, 8, 5, 6, 8,
- 6, 7, 8, 7, 0, 8,
- // clang-format on
-};
-
-// For stroked circles, we use two nested octagons.
-static const uint16_t gStrokeCircleIndices[] = {
- // enter the octagon
- // clang-format off
- 0, 1, 9, 0, 9, 8,
- 1, 2, 10, 1, 10, 9,
- 2, 3, 11, 2, 11, 10,
- 3, 4, 12, 3, 12, 11,
- 4, 5, 13, 4, 13, 12,
- 5, 6, 14, 5, 14, 13,
- 6, 7, 15, 6, 15, 14,
- 7, 0, 8, 7, 8, 15,
- // clang-format on
-};
-
-#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
-static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
-static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
-static const int kVertsPerStrokeCircle = 16;
-static const int kVertsPerFillCircle = 9;
-
-static int circle_type_to_vert_count(bool stroked) {
- return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
-}
-
-static int circle_type_to_index_count(bool stroked) {
- return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
-}
-
-static const uint16_t* circle_type_to_indices(bool stroked) {
- return stroked ? gStrokeCircleIndices : gFillCircleIndices;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// RoundRect Data
-//
-// The geometry for a shadow roundrect is similar to a 9-patch:
-// ____________
-// |_|________|_|
-// | | | |
-// | | | |
-// | | | |
-// |_|________|_|
-// |_|________|_|
-//
-// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
-// shows the upper part of the upper left corner. The bottom triangle would similarly be split
-// into two triangles.)
-// ________
-// |\ \ |
-// | \ \ |
-// | \\ |
-// | \|
-// --------
-//
-// The center of the fan handles the curve of the corner. For roundrects where the stroke width
-// is greater than the corner radius, the outer triangles blend from the curve to the straight
-// sides. Otherwise these triangles will be degenerate.
-//
-// In the case where the stroke width is greater than the corner radius and the
-// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
-// This rectangle extends the coverage values of the center edges of the 9-patch.
-// ____________
-// |_|________|_|
-// | |\ ____ /| |
-// | | | | | |
-// | | |____| | |
-// |_|/______\|_|
-// |_|________|_|
-//
-// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
-
-static const uint16_t gRRectIndices[] = {
- // clang-format off
- // overstroke quads
- // we place this at the beginning so that we can skip these indices when rendering as filled
- 0, 6, 25, 0, 25, 24,
- 6, 18, 27, 6, 27, 25,
- 18, 12, 26, 18, 26, 27,
- 12, 0, 24, 12, 24, 26,
-
- // corners
- 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
- 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
- 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
- 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
-
- // edges
- 0, 5, 11, 0, 11, 6,
- 6, 7, 19, 6, 19, 18,
- 18, 23, 17, 18, 17, 12,
- 12, 13, 1, 12, 1, 0,
-
- // fill quad
- // we place this at the end so that we can skip these indices when rendering as stroked
- 0, 6, 18, 0, 18, 12,
- // clang-format on
-};
-
-// overstroke count
-static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
-// simple stroke count skips overstroke indices
-static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
-// fill count adds final quad to stroke count
-static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
-static const int kVertsPerStrokeRRect = 24;
-static const int kVertsPerOverstrokeRRect = 28;
-static const int kVertsPerFillRRect = 24;
-
-static int rrect_type_to_vert_count(RRectType type) {
- switch (type) {
- case kFill_RRectType:
- return kVertsPerFillRRect;
- case kStroke_RRectType:
- return kVertsPerStrokeRRect;
- case kOverstroke_RRectType:
- return kVertsPerOverstrokeRRect;
- }
- ALOGE("Invalid rect type: %d", type);
- return -1;
-}
-
-static int rrect_type_to_index_count(RRectType type) {
- switch (type) {
- case kFill_RRectType:
- return kIndicesPerFillRRect;
- case kStroke_RRectType:
- return kIndicesPerStrokeRRect;
- case kOverstroke_RRectType:
- return kIndicesPerOverstrokeRRect;
- }
- ALOGE("Invalid rect type: %d", type);
- return -1;
-}
-
-static const uint16_t* rrect_type_to_indices(RRectType type) {
- switch (type) {
- case kFill_RRectType:
- case kStroke_RRectType:
- return gRRectIndices + 6 * 4;
- case kOverstroke_RRectType:
- return gRRectIndices;
- }
- ALOGE("Invalid rect type: %d", type);
- return nullptr;
-}
-
-static void fillInCircleVerts(const Geometry& args, bool isStroked,
- Mesh::VertexArray<vec2>& position,
- Mesh::VertexArray<vec4>& shadowColor,
- Mesh::VertexArray<vec3>& shadowParams) {
- vec4 color = args.fColor;
- float outerRadius = args.fOuterRadius;
- float innerRadius = args.fInnerRadius;
- float blurRadius = args.fBlurRadius;
- float distanceCorrection = outerRadius / blurRadius;
-
- const FloatRect& bounds = args.fDevBounds;
-
- // The inner radius in the vertex data must be specified in normalized space.
- innerRadius = innerRadius / outerRadius;
-
- vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
- float halfWidth = 0.5f * bounds.getWidth();
- float octOffset = 0.41421356237f; // sqrt(2) - 1
- int vertexCount = 0;
-
- position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
- vertexCount++;
-
- if (isStroked) {
- // compute the inner ring
-
- // cosine and sine of pi/8
- float c = 0.923579533f;
- float s = 0.382683432f;
- float r = args.fInnerRadius;
-
- position[vertexCount] = center + vec2(-s * r, -c * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(s * r, -c * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(c * r, -s * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(c * r, s * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(s * r, c * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-s * r, c * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-c * r, s * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-c * r, -s * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
- vertexCount++;
- } else {
- // filled
- position[vertexCount] = center;
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
- }
-}
-
-static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
- Mesh::VertexArray<vec4>& shadowColor,
- Mesh::VertexArray<vec3>& shadowParams) {
- vec4 color = args.fColor;
- float outerRadius = args.fOuterRadius;
-
- const FloatRect& bounds = args.fDevBounds;
-
- float umbraInset = args.fUmbraInset;
- float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
- if (umbraInset > minDim) {
- umbraInset = minDim;
- }
-
- float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
- bounds.left + umbraInset, bounds.right - umbraInset};
- float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
- bounds.left + outerRadius, bounds.right - outerRadius};
- float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
- float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
- bounds.bottom - umbraInset};
- float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
- bounds.bottom - outerRadius, bounds.bottom - outerRadius};
- float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
-
- float blurRadius = args.fBlurRadius;
-
- // In the case where we have to inset more for the umbra, our two triangles in the
- // corner get skewed to a diamond rather than a square. To correct for that,
- // we also skew the vectors we send to the shader that help define the circle.
- // By doing so, we end up with a quarter circle in the corner rather than the
- // elliptical curve.
-
- // This is a bit magical, but it gives us the correct results at extrema:
- // a) umbraInset == outerRadius produces an orthogonal vector
- // b) outerRadius == 0 produces a diagonal vector
- // And visually the corner looks correct.
- vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
- outerVec = normalize(outerVec);
- // We want the circle edge to fall fractionally along the diagonal at
- // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
- //
- // Setting the components of the diagonal offset to the following value will give us that.
- float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
- vec2 diagVec = vec2(diagVal, diagVal);
- float distanceCorrection = umbraInset / blurRadius;
-
- int vertexCount = 0;
- // build corner by corner
- for (int i = 0; i < 4; ++i) {
- // inner point
- position[vertexCount] = vec2(xInner[i], yInner[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
-
- // outer points
- position[vertexCount] = vec2(xOuter[i], yInner[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = vec2(xOuter[i], yMid[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = vec2(xOuter[i], yOuter[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = vec2(xMid[i], yOuter[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = vec2(xInner[i], yOuter[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
- vertexCount++;
- }
-
- // Add the additional vertices for overstroked rrects.
- // Effectively this is an additional stroked rrect, with its
- // parameters equal to those in the center of the 9-patch. This will
- // give constant values across this inner ring.
- if (kOverstroke_RRectType == args.fType) {
- float inset = umbraInset + args.fInnerRadius;
-
- // TL
- position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
-
- // TR
- position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
-
- // BL
- position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
-
- // BR
- position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
- }
-}
-
-int getVertexCountForGeometry(const Geometry& shadowGeometry) {
- if (shadowGeometry.fIsCircle) {
- return circle_type_to_vert_count(shadowGeometry.fType);
- }
-
- return rrect_type_to_vert_count(shadowGeometry.fType);
-}
-
-int getIndexCountForGeometry(const Geometry& shadowGeometry) {
- if (shadowGeometry.fIsCircle) {
- return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
- }
-
- return rrect_type_to_index_count(shadowGeometry.fType);
-}
-
-void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
- Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
- Mesh::VertexArray<vec3> shadowParams) {
- if (shadowGeometry.fIsCircle) {
- fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
- shadowParams);
- } else {
- fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
- }
-}
-
-void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
- int startingVertexOffset, uint16_t* indices) {
- if (shadowGeometry.fIsCircle) {
- const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
- for (int i = 0; i < indexCount; ++i) {
- indices[i] = primIndices[i] + startingVertexOffset;
- }
- } else {
- const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
- for (int i = 0; i < indexCount; ++i) {
- indices[i] = primIndices[i] + startingVertexOffset;
- }
- }
-}
-
-inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
- float lightRadius, float& blurRadius, float& scale, vec2& translate) {
- float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
- blurRadius = lightRadius * zRatio;
- scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
- translate.x = -zRatio * lightX;
- translate.y = -zRatio * lightY;
-}
-
-static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
- float devRadius, float blurRadius,
- float insetWidth) {
- // An insetWidth > 1/2 rect width or height indicates a simple fill.
- const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
-
- FloatRect bounds = devRect;
- float innerRadius = 0.0f;
- float outerRadius = devRadius;
- float umbraInset;
-
- RRectType type = kFill_RRectType;
- if (isCircle) {
- umbraInset = 0;
- } else {
- umbraInset = std::max(outerRadius, blurRadius);
- }
-
- // If stroke is greater than width or height, this is still a fill,
- // otherwise we compute stroke params.
- if (isCircle) {
- innerRadius = devRadius - insetWidth;
- type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
- } else {
- if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
- // We don't worry about a real inner radius, we just need to know if we
- // need to create overstroke vertices.
- innerRadius = std::max(insetWidth - umbraInset, 0.0f);
- type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
- }
- }
- const bool isStroked = (kStroke_RRectType == type);
- return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
- blurRadius, bounds, type, isCircle, isStroked});
-}
-
-std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent,
- const vec4& ambientColor) {
- float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
- const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
- const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
-
- // Outset the shadow rrect to the border of the penumbra
- float ambientPathOutset = devSpaceInsetWidth;
- FloatRect outsetRect(casterRect);
- outsetRect.left -= ambientPathOutset;
- outsetRect.top -= ambientPathOutset;
- outsetRect.right += ambientPathOutset;
- outsetRect.bottom += ambientPathOutset;
-
- float outsetRad = casterCornerRadius + ambientPathOutset;
- if (casterIsTranslucent) {
- // set a large inset to force a fill
- devSpaceInsetWidth = outsetRect.getWidth();
- }
-
- return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
- std::abs(devSpaceInsetWidth));
-}
-
-std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent, const vec4& spotColor,
- const vec3& lightPosition, float lightRadius) {
- float devSpaceSpotBlur;
- float spotScale;
- vec2 spotOffset;
- GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
- devSpaceSpotBlur, spotScale, spotOffset);
- // handle scale of radius due to CTM
- const float srcSpaceSpotBlur = devSpaceSpotBlur;
-
- // Adjust translate for the effect of the scale.
- spotOffset.x += spotScale;
- spotOffset.y += spotScale;
-
- // Compute the transformed shadow rect
- ui::Transform shadowTransform;
- shadowTransform.set(spotOffset.x, spotOffset.y);
- shadowTransform.set(spotScale, 0, 0, spotScale);
- FloatRect spotShadowRect = shadowTransform.transform(casterRect);
- float spotShadowRadius = casterCornerRadius * spotScale;
-
- // Compute the insetWidth
- float blurOutset = srcSpaceSpotBlur;
- float insetWidth = blurOutset;
- if (casterIsTranslucent) {
- // If transparent, just do a fill
- insetWidth += spotShadowRect.getWidth();
- } else {
- // For shadows, instead of using a stroke we specify an inset from the penumbra
- // border. We want to extend this inset area so that it meets up with the caster
- // geometry. The inset geometry will by default already be inset by the blur width.
- //
- // We compare the min and max corners inset by the radius between the original
- // rrect and the shadow rrect. The distance between the two plus the difference
- // between the scaled radius and the original radius gives the distance from the
- // transformed shadow shape to the original shape in that corner. The max
- // of these gives the maximum distance we need to cover.
- //
- // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
- // that to get the full insetWidth.
- float maxOffset;
- if (casterCornerRadius <= 0.f) {
- // Manhattan distance works better for rects
- maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
- std::abs(spotShadowRect.top - casterRect.top)),
- std::max(std::abs(spotShadowRect.right - casterRect.right),
- std::abs(spotShadowRect.bottom - casterRect.bottom)));
- } else {
- float dr = spotShadowRadius - casterCornerRadius;
- vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
- spotShadowRect.top - casterRect.top + dr);
- vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
- spotShadowRect.bottom - casterRect.bottom - dr);
- maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
- dot(lowerRightOffset, lowerRightOffset))) +
- dr;
- }
- insetWidth += std::max(blurOutset, maxOffset);
- }
-
- // Outset the shadow rrect to the border of the penumbra
- spotShadowRadius += blurOutset;
- spotShadowRect.left -= blurOutset;
- spotShadowRect.top -= blurOutset;
- spotShadowRect.right += blurOutset;
- spotShadowRect.bottom += blurOutset;
-
- return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
- 2.0f * devSpaceSpotBlur, std::abs(insetWidth));
-}
-
-void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) {
- for (int i = 0; i < shadowTextureWidth; i++) {
- const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f);
- data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255);
- }
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
deleted file mode 100644
index 912c8bb7b3..0000000000
--- a/libs/renderengine/gl/GLSkiaShadowPort.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 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 <math/vec4.h>
-#include <renderengine/Mesh.h>
-#include <ui/Rect.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-/**
- * The shadow geometry logic and vertex generation code has been ported from skia shadow
- * fast path OpenGL implementation to draw shadows around rects and rounded rects including
- * circles.
- *
- * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
- *
- * Modifications made:
- * - Switched to using std lib math functions
- * - Fall off function is implemented in vertex shader rather than a shadow texture
- * - Removed transformations applied on the caster rect since the caster will be in local
- * coordinate space and will be transformed by the vertex shader.
- */
-
-enum RRectType {
- kFill_RRectType,
- kStroke_RRectType,
- kOverstroke_RRectType,
-};
-
-struct Geometry {
- vec4 fColor;
- float fOuterRadius;
- float fUmbraInset;
- float fInnerRadius;
- float fBlurRadius;
- FloatRect fDevBounds;
- RRectType fType;
- bool fIsCircle;
- bool fIsStroked;
-};
-
-std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent, const vec4& spotColor,
- const vec3& lightPosition, float lightRadius);
-
-std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent,
- const vec4& ambientColor);
-
-int getVertexCountForGeometry(const Geometry& shadowGeometry);
-
-int getIndexCountForGeometry(const Geometry& shadowGeometry);
-
-void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount,
- Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
- Mesh::VertexArray<vec3> shadowParams);
-
-void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
- int startingVertexOffset, uint16_t* indices);
-
-/**
- * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
- * darkness at that spot. Values are determined by an exponential falloff
- * function provided by UX.
- *
- * The texture is used for quick lookup in theshadow shader.
- *
- * textureData - filled with shadow texture data that needs to be at least of
- * size textureWidth
- *
- * textureWidth - width of the texture, height is always 1
- */
-void fillShadowTextureData(uint8_t* textureData, size_t textureWidth);
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.cpp b/libs/renderengine/gl/GLVertexBuffer.cpp
deleted file mode 100644
index e50c471b6d..0000000000
--- a/libs/renderengine/gl/GLVertexBuffer.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "GLVertexBuffer.h"
-
-#include <GLES/gl.h>
-#include <GLES2/gl2.h>
-#include <nativebase/nativebase.h>
-#include <utils/Trace.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLVertexBuffer::GLVertexBuffer() {
- glGenBuffers(1, &mBufferName);
-}
-
-GLVertexBuffer::~GLVertexBuffer() {
- glDeleteBuffers(1, &mBufferName);
-}
-
-void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) {
- ATRACE_CALL();
- bind();
- glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW);
- unbind();
-}
-
-void GLVertexBuffer::bind() const {
- glBindBuffer(GL_ARRAY_BUFFER, mBufferName);
-}
-
-void GLVertexBuffer::unbind() const {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.h b/libs/renderengine/gl/GLVertexBuffer.h
deleted file mode 100644
index c0fd0c1b04..0000000000
--- a/libs/renderengine/gl/GLVertexBuffer.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLESRenderEngine;
-
-class GLVertexBuffer {
-public:
- explicit GLVertexBuffer();
- ~GLVertexBuffer();
-
- void allocateBuffers(const GLfloat data[], const GLuint size);
- uint32_t getBufferName() const { return mBufferName; }
- void bind() const;
- void unbind() const;
-
-private:
- uint32_t mBufferName;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/ImageManager.cpp b/libs/renderengine/gl/ImageManager.cpp
deleted file mode 100644
index 62566494f0..0000000000
--- a/libs/renderengine/gl/ImageManager.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 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_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "RenderEngine"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <pthread.h>
-
-#include <processgroup/sched_policy.h>
-#include <utils/Trace.h>
-#include "GLESRenderEngine.h"
-#include "ImageManager.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {}
-
-void ImageManager::initThread() {
- mThread = std::thread([this]() { threadMain(); });
- pthread_setname_np(mThread.native_handle(), "ImageManager");
- // Use SCHED_FIFO to minimize jitter
- struct sched_param param = {0};
- param.sched_priority = 2;
- if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, &param) != 0) {
- ALOGE("Couldn't set SCHED_FIFO for ImageManager");
- }
-}
-
-ImageManager::~ImageManager() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mRunning = false;
- }
- mCondition.notify_all();
- if (mThread.joinable()) {
- mThread.join();
- }
-}
-
-void ImageManager::cacheAsync(const sp<GraphicBuffer>& buffer,
- const std::shared_ptr<Barrier>& barrier) {
- if (buffer == nullptr) {
- {
- std::lock_guard<std::mutex> lock(barrier->mutex);
- barrier->isOpen = true;
- barrier->result = BAD_VALUE;
- }
- barrier->condition.notify_one();
- return;
- }
- ATRACE_CALL();
- QueueEntry entry = {QueueEntry::Operation::Insert, buffer, buffer->getId(), barrier};
- queueOperation(std::move(entry));
-}
-
-status_t ImageManager::cache(const sp<GraphicBuffer>& buffer) {
- ATRACE_CALL();
- auto barrier = std::make_shared<Barrier>();
- cacheAsync(buffer, barrier);
- std::lock_guard<std::mutex> lock(barrier->mutex);
- barrier->condition.wait(barrier->mutex,
- [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; });
- return barrier->result;
-}
-
-void ImageManager::releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) {
- ATRACE_CALL();
- QueueEntry entry = {QueueEntry::Operation::Delete, nullptr, bufferId, barrier};
- queueOperation(std::move(entry));
-}
-
-void ImageManager::queueOperation(const QueueEntry&& entry) {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mQueue.emplace(entry);
- ATRACE_INT("ImageManagerQueueDepth", mQueue.size());
- }
- mCondition.notify_one();
-}
-
-void ImageManager::threadMain() {
- set_sched_policy(0, SP_FOREGROUND);
- bool run;
- {
- std::lock_guard<std::mutex> lock(mMutex);
- run = mRunning;
- }
- while (run) {
- QueueEntry entry;
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mCondition.wait(mMutex,
- [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
- run = mRunning;
-
- if (!mRunning) {
- // if mRunning is false, then ImageManager is being destroyed, so
- // bail out now.
- break;
- }
-
- entry = mQueue.front();
- mQueue.pop();
- ATRACE_INT("ImageManagerQueueDepth", mQueue.size());
- }
-
- status_t result = NO_ERROR;
- switch (entry.op) {
- case QueueEntry::Operation::Delete:
- mEngine->unbindExternalTextureBufferInternal(entry.bufferId);
- break;
- case QueueEntry::Operation::Insert:
- result = mEngine->cacheExternalTextureBufferInternal(entry.buffer);
- break;
- }
- if (entry.barrier != nullptr) {
- {
- std::lock_guard<std::mutex> entryLock(entry.barrier->mutex);
- entry.barrier->result = result;
- entry.barrier->isOpen = true;
- }
- entry.barrier->condition.notify_one();
- }
- }
-
- ALOGD("Reached end of threadMain, terminating ImageManager thread!");
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/ImageManager.h b/libs/renderengine/gl/ImageManager.h
deleted file mode 100644
index be67de8367..0000000000
--- a/libs/renderengine/gl/ImageManager.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 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 <condition_variable>
-#include <mutex>
-#include <queue>
-#include <thread>
-
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLESRenderEngine;
-
-class ImageManager {
-public:
- struct Barrier {
- std::mutex mutex;
- std::condition_variable_any condition;
- bool isOpen GUARDED_BY(mutex) = false;
- status_t result GUARDED_BY(mutex) = NO_ERROR;
- };
- ImageManager(GLESRenderEngine* engine);
- ~ImageManager();
- // Starts the background thread for the ImageManager
- // We need this to guarantee that the class is fully-constructed before the
- // thread begins running.
- void initThread();
- void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier)
- EXCLUDES(mMutex);
- status_t cache(const sp<GraphicBuffer>& buffer);
- void releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) EXCLUDES(mMutex);
-
-private:
- struct QueueEntry {
- enum class Operation { Delete, Insert };
-
- Operation op = Operation::Delete;
- sp<GraphicBuffer> buffer = nullptr;
- uint64_t bufferId = 0;
- std::shared_ptr<Barrier> barrier = nullptr;
- };
-
- void queueOperation(const QueueEntry&& entry);
- void threadMain();
- GLESRenderEngine* const mEngine;
- std::thread mThread;
- std::condition_variable_any mCondition;
- std::mutex mMutex;
- std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
-
- bool mRunning GUARDED_BY(mMutex) = true;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
deleted file mode 100644
index 26f6166761..0000000000
--- a/libs/renderengine/gl/Program.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-/*Gluint
- * Copyright 2013 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 "Program.h"
-
-#include <stdint.h>
-
-#include <log/log.h>
-#include <math/mat4.h>
-#include <utils/String8.h>
-#include "ProgramCache.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment)
- : mInitialized(false) {
- GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER);
- GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER);
- GLuint programId = glCreateProgram();
- glAttachShader(programId, vertexId);
- glAttachShader(programId, fragmentId);
- glBindAttribLocation(programId, position, "position");
- glBindAttribLocation(programId, texCoords, "texCoords");
- glBindAttribLocation(programId, cropCoords, "cropCoords");
- glBindAttribLocation(programId, shadowColor, "shadowColor");
- glBindAttribLocation(programId, shadowParams, "shadowParams");
- glLinkProgram(programId);
-
- GLint status;
- glGetProgramiv(programId, GL_LINK_STATUS, &status);
- if (status != GL_TRUE) {
- ALOGE("Error while linking shaders:");
- GLint infoLen = 0;
- glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen);
- if (infoLen > 1) {
- GLchar log[infoLen];
- glGetProgramInfoLog(programId, infoLen, 0, &log[0]);
- ALOGE("%s", log);
- }
- glDetachShader(programId, vertexId);
- glDetachShader(programId, fragmentId);
- glDeleteShader(vertexId);
- glDeleteShader(fragmentId);
- glDeleteProgram(programId);
- } else {
- mProgram = programId;
- mVertexShader = vertexId;
- mFragmentShader = fragmentId;
- mInitialized = true;
- mProjectionMatrixLoc = glGetUniformLocation(programId, "projection");
- mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
- mSamplerLoc = glGetUniformLocation(programId, "sampler");
- mColorLoc = glGetUniformLocation(programId, "color");
- mDisplayColorMatrixLoc = glGetUniformLocation(programId, "displayColorMatrix");
- mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
- mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
- mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
- mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
- mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
- mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
- mCropCenterLoc = glGetUniformLocation(programId, "cropCenter");
-
- // set-up the default values for our uniforms
- glUseProgram(programId);
- glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray());
- glEnableVertexAttribArray(0);
- }
-}
-
-Program::~Program() {
- glDetachShader(mProgram, mVertexShader);
- glDetachShader(mProgram, mFragmentShader);
- glDeleteShader(mVertexShader);
- glDeleteShader(mFragmentShader);
- glDeleteProgram(mProgram);
-}
-
-bool Program::isValid() const {
- return mInitialized;
-}
-
-void Program::use() {
- glUseProgram(mProgram);
-}
-
-GLuint Program::getAttrib(const char* name) const {
- // TODO: maybe use a local cache
- return glGetAttribLocation(mProgram, name);
-}
-
-GLint Program::getUniform(const char* name) const {
- // TODO: maybe use a local cache
- return glGetUniformLocation(mProgram, name);
-}
-
-GLuint Program::buildShader(const char* source, GLenum type) {
- GLuint shader = glCreateShader(type);
- glShaderSource(shader, 1, &source, 0);
- glCompileShader(shader);
- GLint status;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
- if (status != GL_TRUE) {
- // Some drivers return wrong values for GL_INFO_LOG_LENGTH
- // use a fixed size instead
- GLchar log[512];
- glGetShaderInfoLog(shader, sizeof(log), 0, log);
- ALOGE("Error while compiling shader: \n%s\n%s", source, log);
- glDeleteShader(shader);
- return 0;
- }
- return shader;
-}
-
-void Program::setUniforms(const Description& desc) {
- // TODO: we should have a mechanism here to not always reset uniforms that
- // didn't change for this program.
-
- if (mSamplerLoc >= 0) {
- glUniform1i(mSamplerLoc, 0);
- glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray());
- }
- if (mColorLoc >= 0) {
- const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
- glUniform4fv(mColorLoc, 1, color);
- }
- if (mDisplayColorMatrixLoc >= 0) {
- glUniformMatrix4fv(mDisplayColorMatrixLoc, 1, GL_FALSE, desc.displayColorMatrix.asArray());
- }
- if (mInputTransformMatrixLoc >= 0) {
- mat4 inputTransformMatrix = desc.inputTransformMatrix;
- glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
- }
- if (mOutputTransformMatrixLoc >= 0) {
- // The output transform matrix and color matrix can be combined as one matrix
- // that is applied right before applying OETF.
- mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix;
- glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray());
- }
- if (mDisplayMaxLuminanceLoc >= 0) {
- glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
- }
- if (mMaxMasteringLuminanceLoc >= 0) {
- glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance);
- }
- if (mMaxContentLuminanceLoc >= 0) {
- glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance);
- }
- if (mCornerRadiusLoc >= 0) {
- glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
- }
- if (mCropCenterLoc >= 0) {
- glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f);
- }
- // these uniforms are always present
- glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray());
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
deleted file mode 100644
index 41f1bf865e..0000000000
--- a/libs/renderengine/gl/Program.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_PROGRAM_H
-#define SF_RENDER_ENGINE_PROGRAM_H
-
-#include <stdint.h>
-
-#include <GLES2/gl2.h>
-#include <renderengine/private/Description.h>
-#include "ProgramCache.h"
-
-namespace android {
-
-class String8;
-
-namespace renderengine {
-namespace gl {
-
-/*
- * Abstracts a GLSL program comprising a vertex and fragment shader
- */
-class Program {
-public:
- // known locations for position and texture coordinates
- enum {
- /* position of each vertex for vertex shader */
- position = 0,
-
- /* UV coordinates for texture mapping */
- texCoords = 1,
-
- /* Crop coordinates, in pixels */
- cropCoords = 2,
-
- /* Shadow color */
- shadowColor = 3,
-
- /* Shadow params */
- shadowParams = 4,
- };
-
- Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
- ~Program();
-
- /* whether this object is usable */
- bool isValid() const;
-
- /* Binds this program to the GLES context */
- void use();
-
- /* Returns the location of the specified attribute */
- GLuint getAttrib(const char* name) const;
-
- /* Returns the location of the specified uniform */
- GLint getUniform(const char* name) const;
-
- /* set-up uniforms from the description */
- void setUniforms(const Description& desc);
-
-private:
- GLuint buildShader(const char* source, GLenum type);
-
- // whether the initialization succeeded
- bool mInitialized;
-
- // Name of the OpenGL program and shaders
- GLuint mProgram;
- GLuint mVertexShader;
- GLuint mFragmentShader;
-
- /* location of the projection matrix uniform */
- GLint mProjectionMatrixLoc;
-
- /* location of the texture matrix uniform */
- GLint mTextureMatrixLoc;
-
- /* location of the sampler uniform */
- GLint mSamplerLoc;
-
- /* location of the color uniform */
- GLint mColorLoc;
-
- /* location of display luminance uniform */
- GLint mDisplayMaxLuminanceLoc;
- /* location of max mastering luminance uniform */
- GLint mMaxMasteringLuminanceLoc;
- /* location of max content luminance uniform */
- GLint mMaxContentLuminanceLoc;
-
- /* location of transform matrix */
- GLint mInputTransformMatrixLoc;
- GLint mOutputTransformMatrixLoc;
- GLint mDisplayColorMatrixLoc;
-
- /* location of corner radius uniform */
- GLint mCornerRadiusLoc;
-
- /* location of surface crop origin uniform, for rounded corner clipping */
- GLint mCropCenterLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
-
-#endif /* SF_RENDER_ENGINE_PROGRAM_H */
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
deleted file mode 100644
index f7f2d54515..0000000000
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ /dev/null
@@ -1,830 +0,0 @@
-/*
- * Copyright 2013 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "ProgramCache.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <log/log.h>
-#include <renderengine/private/Description.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-#include "Program.h"
-
-ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache)
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-/*
- * A simple formatter class to automatically add the endl and
- * manage the indentation.
- */
-
-class Formatter;
-static Formatter& indent(Formatter& f);
-static Formatter& dedent(Formatter& f);
-
-class Formatter {
- String8 mString;
- int mIndent;
- typedef Formatter& (*FormaterManipFunc)(Formatter&);
- friend Formatter& indent(Formatter& f);
- friend Formatter& dedent(Formatter& f);
-
-public:
- Formatter() : mIndent(0) {}
-
- String8 getString() const { return mString; }
-
- friend Formatter& operator<<(Formatter& out, const char* in) {
- for (int i = 0; i < out.mIndent; i++) {
- out.mString.append(" ");
- }
- out.mString.append(in);
- out.mString.append("\n");
- return out;
- }
- friend inline Formatter& operator<<(Formatter& out, const String8& in) {
- return operator<<(out, in.string());
- }
- friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) {
- return (*func)(to);
- }
-};
-Formatter& indent(Formatter& f) {
- f.mIndent++;
- return f;
-}
-Formatter& dedent(Formatter& f) {
- f.mIndent--;
- return f;
-}
-
-void ProgramCache::primeCache(
- EGLContext context, bool useColorManagement, bool toneMapperShaderOnly) {
- auto& cache = mCaches[context];
- uint32_t shaderCount = 0;
-
- if (toneMapperShaderOnly) {
- Key shaderKey;
- // base settings used by HDR->SDR tonemap only
- shaderKey.set(Key::BLEND_MASK | Key::INPUT_TRANSFORM_MATRIX_MASK |
- Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::OUTPUT_TF_MASK |
- Key::OPACITY_MASK | Key::ALPHA_MASK |
- Key::ROUNDED_CORNERS_MASK | Key::TEXTURE_MASK,
- Key::BLEND_NORMAL | Key::INPUT_TRANSFORM_MATRIX_ON |
- Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::OUTPUT_TF_SRGB |
- Key::OPACITY_OPAQUE | Key::ALPHA_EQ_ONE |
- Key::ROUNDED_CORNERS_OFF | Key::TEXTURE_EXT);
- for (int i = 0; i < 4; i++) {
- // Cache input transfer for HLG & ST2084
- shaderKey.set(Key::INPUT_TF_MASK, (i & 1) ?
- Key::INPUT_TF_HLG : Key::INPUT_TF_ST2084);
-
- // Cache Y410 input on or off
- shaderKey.set(Key::Y410_BT2020_MASK, (i & 2) ?
- Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
- if (cache.count(shaderKey) == 0) {
- cache.emplace(shaderKey, generateProgram(shaderKey));
- shaderCount++;
- }
- }
- return;
- }
-
- uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
- | Key::ROUNDED_CORNERS_MASK;
- // Prime the cache for all combinations of the above masks,
- // leaving off the experimental color matrix mask options.
-
- nsecs_t timeBefore = systemTime();
- for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) {
- Key shaderKey;
- shaderKey.set(keyMask, keyVal);
- uint32_t tex = shaderKey.getTextureTarget();
- if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) {
- continue;
- }
- if (cache.count(shaderKey) == 0) {
- cache.emplace(shaderKey, generateProgram(shaderKey));
- shaderCount++;
- }
- }
-
- // Prime for sRGB->P3 conversion
- if (useColorManagement) {
- Key shaderKey;
- shaderKey.set(Key::BLEND_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK |
- Key::OUTPUT_TF_MASK,
- Key::BLEND_PREMULT | Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::INPUT_TF_SRGB |
- Key::OUTPUT_TF_SRGB);
- for (int i = 0; i < 16; i++) {
- shaderKey.set(Key::OPACITY_MASK,
- (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
- shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE);
-
- // Cache rounded corners
- shaderKey.set(Key::ROUNDED_CORNERS_MASK,
- (i & 4) ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
-
- // Cache texture off option for window transition
- shaderKey.set(Key::TEXTURE_MASK, (i & 8) ? Key::TEXTURE_EXT : Key::TEXTURE_OFF);
- if (cache.count(shaderKey) == 0) {
- cache.emplace(shaderKey, generateProgram(shaderKey));
- shaderCount++;
- }
- }
- }
-
- nsecs_t timeAfter = systemTime();
- float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
- ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs);
-}
-
-ProgramCache::Key ProgramCache::computeKey(const Description& description) {
- Key needs;
- needs.set(Key::TEXTURE_MASK,
- !description.textureEnabled
- ? Key::TEXTURE_OFF
- : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
- ? Key::TEXTURE_EXT
- : description.texture.getTextureTarget() == GL_TEXTURE_2D
- ? Key::TEXTURE_2D
- : Key::TEXTURE_OFF)
- .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
- .set(Key::BLEND_MASK,
- description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
- .set(Key::OPACITY_MASK,
- description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
- .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
- description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
- : Key::INPUT_TRANSFORM_MATRIX_OFF)
- .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
- description.hasOutputTransformMatrix() || description.hasColorMatrix()
- ? Key::OUTPUT_TRANSFORM_MATRIX_ON
- : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
- .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK,
- description.hasDisplayColorMatrix() ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
- : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
- .set(Key::ROUNDED_CORNERS_MASK,
- description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
- .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
- needs.set(Key::Y410_BT2020_MASK,
- description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
-
- if (needs.hasTransformMatrix() ||
- (description.inputTransferFunction != description.outputTransferFunction)) {
- switch (description.inputTransferFunction) {
- case Description::TransferFunction::LINEAR:
- default:
- needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
- break;
- case Description::TransferFunction::SRGB:
- needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB);
- break;
- case Description::TransferFunction::ST2084:
- needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084);
- break;
- case Description::TransferFunction::HLG:
- needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG);
- break;
- }
-
- switch (description.outputTransferFunction) {
- case Description::TransferFunction::LINEAR:
- default:
- needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
- break;
- case Description::TransferFunction::SRGB:
- needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB);
- break;
- case Description::TransferFunction::ST2084:
- needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084);
- break;
- case Description::TransferFunction::HLG:
- needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG);
- break;
- }
- }
-
- return needs;
-}
-
-// Generate EOTF that converts signal values to relative display light,
-// both normalized to [0, 1].
-void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) {
- switch (needs.getInputTF()) {
- case Key::INPUT_TF_SRGB:
- fs << R"__SHADER__(
- float EOTF_sRGB(float srgb) {
- return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
- }
-
- vec3 EOTF_sRGB(const vec3 srgb) {
- return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- vec3 EOTF(const vec3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )__SHADER__";
- break;
- case Key::INPUT_TF_ST2084:
- fs << R"__SHADER__(
- vec3 EOTF(const highp vec3 color) {
- const highp float m1 = (2610.0 / 4096.0) / 4.0;
- const highp float m2 = (2523.0 / 4096.0) * 128.0;
- const highp float c1 = (3424.0 / 4096.0);
- const highp float c2 = (2413.0 / 4096.0) * 32.0;
- const highp float c3 = (2392.0 / 4096.0) * 32.0;
-
- highp vec3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / vec3(m2));
- tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
- return pow(tmp, 1.0 / vec3(m1));
- }
- )__SHADER__";
- break;
- case Key::INPUT_TF_HLG:
- fs << R"__SHADER__(
- highp float EOTF_channel(const highp float channel) {
- const highp float a = 0.17883277;
- const highp float b = 0.28466892;
- const highp float c = 0.55991073;
- return channel <= 0.5 ? channel * channel / 3.0 :
- (exp((channel - c) / a) + b) / 12.0;
- }
-
- vec3 EOTF(const highp vec3 color) {
- return vec3(EOTF_channel(color.r), EOTF_channel(color.g),
- EOTF_channel(color.b));
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- vec3 EOTF(const vec3 linear) {
- return linear;
- }
- )__SHADER__";
- break;
- }
-}
-
-void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) {
- // Convert relative light to absolute light.
- switch (needs.getInputTF()) {
- case Key::INPUT_TF_ST2084:
- fs << R"__SHADER__(
- highp vec3 ScaleLuminance(highp vec3 color) {
- return color * 10000.0;
- }
- )__SHADER__";
- break;
- case Key::INPUT_TF_HLG:
- fs << R"__SHADER__(
- highp vec3 ScaleLuminance(highp vec3 color) {
- // The formula is:
- // alpha * pow(Y, gamma - 1.0) * color + beta;
- // where alpha is 1000.0, gamma is 1.2, beta is 0.0.
- return color * 1000.0 * pow(color.y, 0.2);
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- highp vec3 ScaleLuminance(highp vec3 color) {
- return color * displayMaxLuminance;
- }
- )__SHADER__";
- break;
- }
-
- // Tone map absolute light to display luminance range.
- switch (needs.getInputTF()) {
- case Key::INPUT_TF_ST2084:
- case Key::INPUT_TF_HLG:
- switch (needs.getOutputTF()) {
- case Key::OUTPUT_TF_HLG:
- // Right now when mixed PQ and HLG contents are presented,
- // HLG content will always be converted to PQ. However, for
- // completeness, we simply clamp the value to [0.0, 1000.0].
- fs << R"__SHADER__(
- highp vec3 ToneMap(highp vec3 color) {
- return clamp(color, 0.0, 1000.0);
- }
- )__SHADER__";
- break;
- case Key::OUTPUT_TF_ST2084:
- fs << R"__SHADER__(
- highp vec3 ToneMap(highp vec3 color) {
- return color;
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- highp vec3 ToneMap(highp vec3 color) {
- float maxMasteringLumi = maxMasteringLuminance;
- float maxContentLumi = maxContentLuminance;
- float maxInLumi = min(maxMasteringLumi, maxContentLumi);
- float maxOutLumi = displayMaxLuminance;
-
- float nits = color.y;
-
- // clamp to max input luminance
- nits = clamp(nits, 0.0, maxInLumi);
-
- // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
- if (maxInLumi <= maxOutLumi) {
- return color * (maxOutLumi / maxInLumi);
- } else {
- // three control points
- const float x0 = 10.0;
- const float y0 = 17.0;
- float x1 = maxOutLumi * 0.75;
- float y1 = x1;
- float x2 = x1 + (maxInLumi - x1) / 2.0;
- float y2 = y1 + (maxOutLumi - y1) * 0.75;
-
- // horizontal distances between the last three control points
- float h12 = x2 - x1;
- float h23 = maxInLumi - x2;
- // tangents at the last three control points
- float m1 = (y2 - y1) / h12;
- float m3 = (maxOutLumi - y2) / h23;
- float m2 = (m1 + m3) / 2.0;
-
- if (nits < x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- float slope = y0 / x0;
- return color * slope;
- } else if (nits < x1) {
- // scale [x0, x1] to [y0, y1] linearly
- float slope = (y1 - y0) / (x1 - x0);
- nits = y0 + (nits - x0) * slope;
- } else if (nits < x2) {
- // scale [x1, x2] to [y1, y2] using Hermite interp
- float t = (nits - x1) / h12;
- nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
- (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
- } else {
- // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
- float t = (nits - x2) / h23;
- nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
- (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
- }
- }
-
- // color.y is greater than x0 and is thus non-zero
- return color * (nits / color.y);
- }
- )__SHADER__";
- break;
- }
- break;
- default:
- // inverse tone map; the output luminance can be up to maxOutLumi.
- fs << R"__SHADER__(
- highp vec3 ToneMap(highp vec3 color) {
- const float maxOutLumi = 3000.0;
-
- const float x0 = 5.0;
- const float y0 = 2.5;
- float x1 = displayMaxLuminance * 0.7;
- float y1 = maxOutLumi * 0.15;
- float x2 = displayMaxLuminance * 0.9;
- float y2 = maxOutLumi * 0.45;
- float x3 = displayMaxLuminance;
- float y3 = maxOutLumi;
-
- float c1 = y1 / 3.0;
- float c2 = y2 / 2.0;
- float c3 = y3 / 1.5;
-
- float nits = color.y;
-
- float scale;
- if (nits <= x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- const float slope = y0 / x0;
- return color * slope;
- } else if (nits <= x1) {
- // scale [x0, x1] to [y0, y1] using a curve
- float t = (nits - x0) / (x1 - x0);
- nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
- } else if (nits <= x2) {
- // scale [x1, x2] to [y1, y2] using a curve
- float t = (nits - x1) / (x2 - x1);
- nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
- } else {
- // scale [x2, x3] to [y2, y3] using a curve
- float t = (nits - x2) / (x3 - x2);
- nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
- }
-
- // color.y is greater than x0 and is thus non-zero
- return color * (nits / color.y);
- }
- )__SHADER__";
- break;
- }
-
- // convert absolute light to relative light.
- switch (needs.getOutputTF()) {
- case Key::OUTPUT_TF_ST2084:
- fs << R"__SHADER__(
- highp vec3 NormalizeLuminance(highp vec3 color) {
- return color / 10000.0;
- }
- )__SHADER__";
- break;
- case Key::OUTPUT_TF_HLG:
- fs << R"__SHADER__(
- highp vec3 NormalizeLuminance(highp vec3 color) {
- return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2);
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- highp vec3 NormalizeLuminance(highp vec3 color) {
- return color / displayMaxLuminance;
- }
- )__SHADER__";
- break;
- }
-}
-
-// Generate OOTF that modifies the relative scence light to relative display light.
-void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) {
- if (!needs.needsToneMapping()) {
- fs << R"__SHADER__(
- highp vec3 OOTF(const highp vec3 color) {
- return color;
- }
- )__SHADER__";
- } else {
- generateToneMappingProcess(fs, needs);
- fs << R"__SHADER__(
- highp vec3 OOTF(const highp vec3 color) {
- return NormalizeLuminance(ToneMap(ScaleLuminance(color)));
- }
- )__SHADER__";
- }
-}
-
-// Generate OETF that converts relative display light to signal values,
-// both normalized to [0, 1]
-void ProgramCache::generateOETF(Formatter& fs, const Key& needs) {
- switch (needs.getOutputTF()) {
- case Key::OUTPUT_TF_SRGB:
- fs << R"__SHADER__(
- float OETF_sRGB(const float linear) {
- return linear <= 0.0031308 ?
- linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
- }
-
- vec3 OETF_sRGB(const vec3 linear) {
- return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- vec3 OETF(const vec3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )__SHADER__";
- break;
- case Key::OUTPUT_TF_ST2084:
- fs << R"__SHADER__(
- vec3 OETF(const vec3 linear) {
- const highp float m1 = (2610.0 / 4096.0) / 4.0;
- const highp float m2 = (2523.0 / 4096.0) * 128.0;
- const highp float c1 = (3424.0 / 4096.0);
- const highp float c2 = (2413.0 / 4096.0) * 32.0;
- const highp float c3 = (2392.0 / 4096.0) * 32.0;
-
- highp vec3 tmp = pow(linear, vec3(m1));
- tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
- return pow(tmp, vec3(m2));
- }
- )__SHADER__";
- break;
- case Key::OUTPUT_TF_HLG:
- fs << R"__SHADER__(
- highp float OETF_channel(const highp float channel) {
- const highp float a = 0.17883277;
- const highp float b = 0.28466892;
- const highp float c = 0.55991073;
- return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
- a * log(12.0 * channel - b) + c;
- }
-
- vec3 OETF(const highp vec3 color) {
- return vec3(OETF_channel(color.r), OETF_channel(color.g),
- OETF_channel(color.b));
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- vec3 OETF(const vec3 linear) {
- return linear;
- }
- )__SHADER__";
- break;
- }
-}
-
-String8 ProgramCache::generateVertexShader(const Key& needs) {
- Formatter vs;
- if (needs.hasTextureCoords()) {
- vs << "attribute vec4 texCoords;"
- << "varying vec2 outTexCoords;";
- }
- if (needs.hasRoundedCorners()) {
- vs << "attribute lowp vec4 cropCoords;";
- vs << "varying lowp vec2 outCropCoords;";
- }
- if (needs.drawShadows()) {
- vs << "attribute lowp vec4 shadowColor;";
- vs << "varying lowp vec4 outShadowColor;";
- vs << "attribute lowp vec4 shadowParams;";
- vs << "varying lowp vec3 outShadowParams;";
- }
- vs << "attribute vec4 position;"
- << "uniform mat4 projection;"
- << "uniform mat4 texture;"
- << "void main(void) {" << indent << "gl_Position = projection * position;";
- if (needs.hasTextureCoords()) {
- vs << "outTexCoords = (texture * texCoords).st;";
- }
- if (needs.hasRoundedCorners()) {
- vs << "outCropCoords = cropCoords.st;";
- }
- if (needs.drawShadows()) {
- vs << "outShadowColor = shadowColor;";
- vs << "outShadowParams = shadowParams.xyz;";
- }
- vs << dedent << "}";
- return vs.getString();
-}
-
-String8 ProgramCache::generateFragmentShader(const Key& needs) {
- Formatter fs;
- if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
- fs << "#extension GL_OES_EGL_image_external : require";
- }
-
- // default precision is required-ish in fragment shaders
- fs << "precision mediump float;";
-
- if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
- fs << "uniform samplerExternalOES sampler;";
- } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
- fs << "uniform sampler2D sampler;";
- }
-
- if (needs.hasTextureCoords()) {
- fs << "varying highp vec2 outTexCoords;";
- }
-
- if (needs.hasRoundedCorners()) {
- // Rounded corners implementation using a signed distance function.
- fs << R"__SHADER__(
- uniform float cornerRadius;
- uniform vec2 cropCenter;
- varying vec2 outCropCoords;
-
- /**
- * This function takes the current crop coordinates and calculates an alpha value based
- * on the corner radius and distance from the crop center.
- */
- float applyCornerRadius(vec2 cropCoords)
- {
- vec2 position = cropCoords - cropCenter;
- // Scale down the dist vector here, as otherwise large corner
- // radii can cause floating point issues when computing the norm
- vec2 dist = (abs(position) - cropCenter + vec2(cornerRadius)) / 16.0;
- // Once we've found the norm, then scale back up.
- float plane = length(max(dist, vec2(0.0))) * 16.0;
- return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0);
- }
- )__SHADER__";
- }
-
- if (needs.drawShadows()) {
- fs << R"__SHADER__(
- varying lowp vec4 outShadowColor;
- varying lowp vec3 outShadowParams;
-
- /**
- * Returns the shadow color.
- */
- vec4 getShadowColor()
- {
- lowp float d = length(outShadowParams.xy);
- vec2 uv = vec2(outShadowParams.z * (1.0 - d), 0.5);
- lowp float factor = texture2D(sampler, uv).a;
- return outShadowColor * factor;
- }
- )__SHADER__";
- }
-
- if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
- fs << "uniform vec4 color;";
- }
-
- if (needs.isY410BT2020()) {
- fs << R"__SHADER__(
- vec3 convertY410BT2020(const vec3 color) {
- const vec3 offset = vec3(0.0625, 0.5, 0.5);
- const mat3 transform = mat3(
- vec3(1.1678, 1.1678, 1.1678),
- vec3( 0.0, -0.1878, 2.1481),
- vec3(1.6836, -0.6523, 0.0));
- // Y is in G, U is in R, and V is in B
- return clamp(transform * (color.grb - offset), 0.0, 1.0);
- }
- )__SHADER__";
- }
-
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
- needs.hasDisplayColorMatrix()) {
- if (needs.needsToneMapping()) {
- fs << "uniform float displayMaxLuminance;";
- fs << "uniform float maxMasteringLuminance;";
- fs << "uniform float maxContentLuminance;";
- }
-
- if (needs.hasInputTransformMatrix()) {
- fs << "uniform mat4 inputTransformMatrix;";
- fs << R"__SHADER__(
- highp vec3 InputTransform(const highp vec3 color) {
- return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
- }
- )__SHADER__";
- } else {
- fs << R"__SHADER__(
- highp vec3 InputTransform(const highp vec3 color) {
- return color;
- }
- )__SHADER__";
- }
-
- // the transformation from a wider colorspace to a narrower one can
- // result in >1.0 or <0.0 pixel values
- if (needs.hasOutputTransformMatrix()) {
- fs << "uniform mat4 outputTransformMatrix;";
- fs << R"__SHADER__(
- highp vec3 OutputTransform(const highp vec3 color) {
- return clamp(vec3(outputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
- }
- )__SHADER__";
- } else {
- fs << R"__SHADER__(
- highp vec3 OutputTransform(const highp vec3 color) {
- return clamp(color, 0.0, 1.0);
- }
- )__SHADER__";
- }
-
- if (needs.hasDisplayColorMatrix()) {
- fs << "uniform mat4 displayColorMatrix;";
- fs << R"__SHADER__(
- highp vec3 DisplayColorMatrix(const highp vec3 color) {
- return clamp(vec3(displayColorMatrix * vec4(color, 1.0)), 0.0, 1.0);
- }
- )__SHADER__";
- } else {
- fs << R"__SHADER__(
- highp vec3 DisplayColorMatrix(const highp vec3 color) {
- return color;
- }
- )__SHADER__";
- }
-
- generateEOTF(fs, needs);
- generateOOTF(fs, needs);
- generateOETF(fs, needs);
- }
-
- fs << "void main(void) {" << indent;
- if (needs.drawShadows()) {
- fs << "gl_FragColor = getShadowColor();";
- } else {
- if (needs.isTexturing()) {
- fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
- if (needs.isY410BT2020()) {
- fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
- }
- } else {
- fs << "gl_FragColor.rgb = color.rgb;";
- fs << "gl_FragColor.a = 1.0;";
- }
- if (needs.isOpaque()) {
- fs << "gl_FragColor.a = 1.0;";
- }
- }
-
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
- needs.hasDisplayColorMatrix()) {
- if (!needs.isOpaque() && needs.isPremultiplied()) {
- // un-premultiply if needed before linearization
- // avoid divide by 0 by adding 0.5/256 to the alpha channel
- fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
- }
- fs << "gl_FragColor.rgb = "
- "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))"
- ")));";
-
- if (!needs.isOpaque() && needs.isPremultiplied()) {
- // and re-premultiply if needed after gamma correction
- fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
- }
- }
-
- /*
- * Whether applying layer alpha before or after color transform doesn't matter,
- * as long as we can undo premultiplication. But we cannot un-premultiply
- * for color transform if the layer alpha = 0, e.g. 0 / (0 + 0.0019) = 0.
- */
- if (!needs.drawShadows()) {
- if (needs.hasAlpha()) {
- // modulate the current alpha value with alpha set
- if (needs.isPremultiplied()) {
- // ... and the color too if we're premultiplied
- fs << "gl_FragColor *= color.a;";
- } else {
- fs << "gl_FragColor.a *= color.a;";
- }
- }
- }
-
- if (needs.hasRoundedCorners()) {
- if (needs.isPremultiplied()) {
- fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
- } else {
- fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);";
- }
- }
-
- fs << dedent << "}";
- return fs.getString();
-}
-
-std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) {
- ATRACE_CALL();
-
- // vertex shader
- String8 vs = generateVertexShader(needs);
-
- // fragment shader
- String8 fs = generateFragmentShader(needs);
-
- return std::make_unique<Program>(needs, vs.string(), fs.string());
-}
-
-void ProgramCache::useProgram(EGLContext context, const Description& description) {
- // generate the key for the shader based on the description
- Key needs(computeKey(description));
-
- // look-up the program in the cache
- auto& cache = mCaches[context];
- auto it = cache.find(needs);
- if (it == cache.end()) {
- // we didn't find our program, so generate one...
- nsecs_t time = systemTime();
- it = cache.emplace(needs, generateProgram(needs)).first;
- time = systemTime() - time;
-
- ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)",
- context, needs.mKey, uint32_t(ns2ms(time)), cache.size());
- }
-
- // here we have a suitable program for this description
- std::unique_ptr<Program>& program = it->second;
- if (program->isValid()) {
- program->use();
- program->setUniforms(description);
- }
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
deleted file mode 100644
index 535d21cd52..0000000000
--- a/libs/renderengine/gl/ProgramCache.h
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H
-#define SF_RENDER_ENGINE_PROGRAMCACHE_H
-
-#include <memory>
-#include <unordered_map>
-
-#include <EGL/egl.h>
-#include <GLES2/gl2.h>
-#include <renderengine/private/Description.h>
-#include <utils/Singleton.h>
-#include <utils/TypeHelpers.h>
-
-namespace android {
-
-class String8;
-
-namespace renderengine {
-
-struct Description;
-
-namespace gl {
-
-class Formatter;
-class Program;
-
-/*
- * This class generates GLSL programs suitable to handle a given
- * Description. It's responsible for figuring out what to
- * generate from a Description.
- * It also maintains a cache of these Programs.
- */
-class ProgramCache : public Singleton<ProgramCache> {
-public:
- /*
- * Key is used to retrieve a Program in the cache.
- * A Key is generated from a Description.
- */
- class Key {
- friend class ProgramCache;
- typedef uint32_t key_t;
- key_t mKey;
-
- public:
- enum {
- BLEND_SHIFT = 0,
- BLEND_MASK = 1 << BLEND_SHIFT,
- BLEND_PREMULT = 1 << BLEND_SHIFT,
- BLEND_NORMAL = 0 << BLEND_SHIFT,
-
- OPACITY_SHIFT = 1,
- OPACITY_MASK = 1 << OPACITY_SHIFT,
- OPACITY_OPAQUE = 1 << OPACITY_SHIFT,
- OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT,
-
- ALPHA_SHIFT = 2,
- ALPHA_MASK = 1 << ALPHA_SHIFT,
- ALPHA_LT_ONE = 1 << ALPHA_SHIFT,
- ALPHA_EQ_ONE = 0 << ALPHA_SHIFT,
-
- TEXTURE_SHIFT = 3,
- TEXTURE_MASK = 3 << TEXTURE_SHIFT,
- TEXTURE_OFF = 0 << TEXTURE_SHIFT,
- TEXTURE_EXT = 1 << TEXTURE_SHIFT,
- TEXTURE_2D = 2 << TEXTURE_SHIFT,
-
- ROUNDED_CORNERS_SHIFT = 5,
- ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT,
- ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT,
- ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT,
-
- INPUT_TRANSFORM_MATRIX_SHIFT = 6,
- INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
- INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
- INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
-
- OUTPUT_TRANSFORM_MATRIX_SHIFT = 7,
- OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
- OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
- OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
-
- INPUT_TF_SHIFT = 8,
- INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
- INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
- INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
- INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
- INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
-
- OUTPUT_TF_SHIFT = 10,
- OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
- OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
- OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
- OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
- OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
-
- Y410_BT2020_SHIFT = 12,
- Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
- Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
- Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
-
- SHADOW_SHIFT = 13,
- SHADOW_MASK = 1 << SHADOW_SHIFT,
- SHADOW_OFF = 0 << SHADOW_SHIFT,
- SHADOW_ON = 1 << SHADOW_SHIFT,
-
- DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT = 14,
- DISPLAY_COLOR_TRANSFORM_MATRIX_MASK = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
- DISPLAY_COLOR_TRANSFORM_MATRIX_OFF = 0 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
- DISPLAY_COLOR_TRANSFORM_MATRIX_ON = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
- };
-
- inline Key() : mKey(0) {}
- inline Key(const Key& rhs) : mKey(rhs.mKey) {}
-
- inline Key& set(key_t mask, key_t value) {
- mKey = (mKey & ~mask) | value;
- return *this;
- }
-
- inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
- inline bool hasTextureCoords() const { return isTexturing() && !drawShadows(); }
- inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
- inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
- inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
- inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
- inline bool hasRoundedCorners() const {
- return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
- }
- inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; }
- inline bool hasInputTransformMatrix() const {
- return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
- }
- inline bool hasOutputTransformMatrix() const {
- return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
- }
- inline bool hasDisplayColorMatrix() const {
- return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) ==
- DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
- }
- inline bool hasTransformMatrix() const {
- return hasInputTransformMatrix() || hasOutputTransformMatrix();
- }
- inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
- inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
-
- // When HDR and non-HDR contents are mixed, or different types of HDR contents are
- // mixed, we will do a tone mapping process to tone map the input content to output
- // content. Currently, the following conversions handled, they are:
- // * SDR -> HLG
- // * SDR -> PQ
- // * HLG -> PQ
- inline bool needsToneMapping() const {
- int inputTF = getInputTF();
- int outputTF = getOutputTF();
-
- // Return false when converting from SDR to SDR.
- if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) {
- return false;
- }
- if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) {
- return false;
- }
-
- inputTF >>= Key::INPUT_TF_SHIFT;
- outputTF >>= Key::OUTPUT_TF_SHIFT;
- return inputTF != outputTF;
- }
- inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; }
-
- // for use by std::unordered_map
-
- bool operator==(const Key& other) const { return mKey == other.mKey; }
-
- struct Hash {
- size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); }
- };
- };
-
- ProgramCache() = default;
- ~ProgramCache() = default;
-
- // Generate shaders to populate the cache
- void primeCache(const EGLContext context, bool useColorManagement, bool toneMapperShaderOnly);
-
- size_t getSize(const EGLContext context) { return mCaches[context].size(); }
-
- // useProgram lookup a suitable program in the cache or generates one
- // if none can be found.
- void useProgram(const EGLContext context, const Description& description);
-
- void purgeCaches() { mCaches.clear(); }
-
-private:
- // compute a cache Key from a Description
- static Key computeKey(const Description& description);
- // Generate EOTF based from Key.
- static void generateEOTF(Formatter& fs, const Key& needs);
- // Generate necessary tone mapping methods for OOTF.
- static void generateToneMappingProcess(Formatter& fs, const Key& needs);
- // Generate OOTF based from Key.
- static void generateOOTF(Formatter& fs, const Key& needs);
- // Generate OETF based from Key.
- static void generateOETF(Formatter& fs, const Key& needs);
- // generates a program from the Key
- static std::unique_ptr<Program> generateProgram(const Key& needs);
- // generates the vertex shader from the Key
- static String8 generateVertexShader(const Key& needs);
- // generates the fragment shader from the Key
- static String8 generateFragmentShader(const Key& needs);
-
- // Key/Value map used for caching Programs. Currently the cache
- // is never shrunk (and the GL program objects are never deleted).
- std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>>
- mCaches;
-};
-
-} // namespace gl
-} // namespace renderengine
-
-ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key)
-
-} // namespace android
-
-#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
deleted file mode 100644
index 3455e08cfe..0000000000
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "BlurFilter.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-#include <ui/GraphicTypes.h>
-#include <cstdint>
-
-#include <utils/Trace.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-BlurFilter::BlurFilter(GLESRenderEngine& engine)
- : mEngine(engine),
- mCompositionFbo(engine),
- mPingFbo(engine),
- mPongFbo(engine),
- mMixProgram(engine),
- mBlurProgram(engine) {
- mMixProgram.compile(getVertexShader(), getMixFragShader());
- mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
- mMUvLoc = mMixProgram.getAttributeLocation("aUV");
- mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
- mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
- mMMixLoc = mMixProgram.getUniformLocation("uMix");
-
- mBlurProgram.compile(getVertexShader(), getFragmentShader());
- mBPosLoc = mBlurProgram.getAttributeLocation("aPosition");
- mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
- mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
- mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
-
- static constexpr auto size = 2.0f;
- static constexpr auto translation = 1.0f;
- const GLfloat vboData[] = {
- // Vertex data
- translation - size, -translation - size,
- translation - size, -translation + size,
- translation + size, -translation + size,
- // UV data
- 0.0f, 0.0f - translation,
- 0.0f, size - translation,
- size, size - translation
- };
- mMeshBuffer.allocateBuffers(vboData, 12 /* size */);
-}
-
-status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
- ATRACE_NAME("BlurFilter::setAsDrawTarget");
- mRadius = radius;
- mDisplayX = display.physicalDisplay.left;
- mDisplayY = display.physicalDisplay.top;
-
- if (mDisplayWidth < display.physicalDisplay.width() ||
- mDisplayHeight < display.physicalDisplay.height()) {
- ATRACE_NAME("BlurFilter::allocatingTextures");
-
- mDisplayWidth = display.physicalDisplay.width();
- mDisplayHeight = display.physicalDisplay.height();
- mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
-
- const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
- const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
- mPingFbo.allocateBuffers(fboWidth, fboHeight);
- mPongFbo.allocateBuffers(fboWidth, fboHeight);
-
- if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid ping buffer");
- return mPingFbo.getStatus();
- }
- if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid pong buffer");
- return mPongFbo.getStatus();
- }
- if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid composition buffer");
- return mCompositionFbo.getStatus();
- }
- if (!mBlurProgram.isValid()) {
- ALOGE("Invalid shader");
- return GL_INVALID_OPERATION;
- }
- }
-
- mCompositionFbo.bind();
- glViewport(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight());
- return NO_ERROR;
-}
-
-void BlurFilter::drawMesh(GLuint uv, GLuint position) {
-
- glEnableVertexAttribArray(uv);
- glEnableVertexAttribArray(position);
- mMeshBuffer.bind();
- glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE,
- 2 * sizeof(GLfloat) /* stride */, 0 /* offset */);
- glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */,
- (GLvoid*)(6 * sizeof(GLfloat)) /* offset */);
- mMeshBuffer.unbind();
-
- // draw mesh
- glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
-}
-
-status_t BlurFilter::prepare() {
- ATRACE_NAME("BlurFilter::prepare");
-
- // Kawase is an approximation of Gaussian, but it behaves differently from it.
- // A radius transformation is required for approximating them, and also to introduce
- // non-integer steps, necessary to smoothly interpolate large radii.
- const auto radius = mRadius / 6.0f;
-
- // Calculate how many passes we'll do, based on the radius.
- // Too many passes will make the operation expensive.
- const auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
-
- const float radiusByPasses = radius / (float)passes;
- const float stepX = radiusByPasses / (float)mCompositionFbo.getBufferWidth();
- const float stepY = radiusByPasses / (float)mCompositionFbo.getBufferHeight();
-
- // Let's start by downsampling and blurring the composited frame simultaneously.
- mBlurProgram.useProgram();
- glActiveTexture(GL_TEXTURE0);
- glUniform1i(mBTextureLoc, 0);
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glUniform2f(mBOffsetLoc, stepX, stepY);
- glViewport(0, 0, mPingFbo.getBufferWidth(), mPingFbo.getBufferHeight());
- mPingFbo.bind();
- drawMesh(mBUvLoc, mBPosLoc);
-
- // And now we'll ping pong between our textures, to accumulate the result of various offsets.
- GLFramebuffer* read = &mPingFbo;
- GLFramebuffer* draw = &mPongFbo;
- glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
- for (auto i = 1; i < passes; i++) {
- ATRACE_NAME("BlurFilter::renderPass");
- draw->bind();
-
- glBindTexture(GL_TEXTURE_2D, read->getTextureName());
- glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
-
- drawMesh(mBUvLoc, mBPosLoc);
-
- // Swap buffers for next iteration
- auto tmp = draw;
- draw = read;
- read = tmp;
- }
- mLastDrawTarget = read;
-
- return NO_ERROR;
-}
-
-status_t BlurFilter::render(bool multiPass) {
- ATRACE_NAME("BlurFilter::render");
-
- // Now let's scale our blur up. It will be interpolated with the larger composited
- // texture for the first frames, to hide downscaling artifacts.
- GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
-
- // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll
- // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
- // as large as the screen size.
- if (mix >= 1 || multiPass) {
- mLastDrawTarget->bindAsReadBuffer();
- glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
- mLastDrawTarget->getBufferHeight(), mDisplayX, mDisplayY, mDisplayWidth,
- mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
- return NO_ERROR;
- }
-
- mMixProgram.useProgram();
- glUniform1f(mMMixLoc, mix);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mLastDrawTarget->getTextureName());
- glUniform1i(mMTextureLoc, 0);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glUniform1i(mMCompositionTextureLoc, 1);
-
- drawMesh(mMUvLoc, mMPosLoc);
-
- glUseProgram(0);
- glActiveTexture(GL_TEXTURE0);
- mEngine.checkErrors("Drawing blur mesh");
- return NO_ERROR;
-}
-
-string BlurFilter::getVertexShader() const {
- return R"SHADER(#version 300 es
- precision mediump float;
-
- in vec2 aPosition;
- in highp vec2 aUV;
- out highp vec2 vUV;
-
- void main() {
- vUV = aUV;
- gl_Position = vec4(aPosition, 0.0, 1.0);
- }
- )SHADER";
-}
-
-string BlurFilter::getFragmentShader() const {
- return R"SHADER(#version 300 es
- precision mediump float;
-
- uniform sampler2D uTexture;
- uniform vec2 uOffset;
-
- in highp vec2 vUV;
- out vec4 fragColor;
-
- void main() {
- fragColor = texture(uTexture, vUV, 0.0);
- fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
-
- fragColor = vec4(fragColor.rgb * 0.2, 1.0);
- }
- )SHADER";
-}
-
-string BlurFilter::getMixFragShader() const {
- string shader = R"SHADER(#version 300 es
- precision mediump float;
-
- in highp vec2 vUV;
- out vec4 fragColor;
-
- uniform sampler2D uCompositionTexture;
- uniform sampler2D uTexture;
- uniform float uMix;
-
- void main() {
- vec4 blurred = texture(uTexture, vUV);
- vec4 composition = texture(uCompositionTexture, vUV);
- fragColor = mix(composition, blurred, uMix);
- }
- )SHADER";
- return shader;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
deleted file mode 100644
index 593a8fd54e..0000000000
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 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 <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-#include "../GLVertexBuffer.h"
-#include "GenericProgram.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-/**
- * This is an implementation of a Kawase blur, as described in here:
- * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
- * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
- */
-class BlurFilter {
-public:
- // Downsample FBO to improve performance
- static constexpr float kFboScale = 0.25f;
- // Maximum number of render passes
- static constexpr uint32_t kMaxPasses = 4;
- // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
- // image, up to this radius.
- static constexpr float kMaxCrossFadeRadius = 30.0f;
-
- explicit BlurFilter(GLESRenderEngine& engine);
- virtual ~BlurFilter(){};
-
- // Set up render targets, redirecting output to offscreen texture.
- status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
- // Execute blur passes, rendering to offscreen texture.
- status_t prepare();
- // Render blur to the bound framebuffer (screen).
- status_t render(bool multiPass);
-
-private:
- uint32_t mRadius;
- void drawMesh(GLuint uv, GLuint position);
- string getVertexShader() const;
- string getFragmentShader() const;
- string getMixFragShader() const;
-
- GLESRenderEngine& mEngine;
- // Frame buffer holding the composited background.
- GLFramebuffer mCompositionFbo;
- // Frame buffers holding the blur passes.
- GLFramebuffer mPingFbo;
- GLFramebuffer mPongFbo;
- uint32_t mDisplayWidth = 0;
- uint32_t mDisplayHeight = 0;
- uint32_t mDisplayX = 0;
- uint32_t mDisplayY = 0;
- // Buffer holding the final blur pass.
- GLFramebuffer* mLastDrawTarget;
-
- // VBO containing vertex and uv data of a fullscreen triangle.
- GLVertexBuffer mMeshBuffer;
-
- GenericProgram mMixProgram;
- GLuint mMPosLoc;
- GLuint mMUvLoc;
- GLuint mMMixLoc;
- GLuint mMTextureLoc;
- GLuint mMCompositionTextureLoc;
-
- GenericProgram mBlurProgram;
- GLuint mBPosLoc;
- GLuint mBUvLoc;
- GLuint mBTextureLoc;
- GLuint mBOffsetLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/GenericProgram.cpp b/libs/renderengine/gl/filters/GenericProgram.cpp
deleted file mode 100644
index bb35889665..0000000000
--- a/libs/renderengine/gl/filters/GenericProgram.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 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 "GenericProgram.h"
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GenericProgram::GenericProgram(GLESRenderEngine& engine) : mEngine(engine) {}
-
-GenericProgram::~GenericProgram() {
- if (mVertexShaderHandle != 0) {
- if (mProgramHandle != 0) {
- glDetachShader(mProgramHandle, mVertexShaderHandle);
- }
- glDeleteShader(mVertexShaderHandle);
- }
-
- if (mFragmentShaderHandle != 0) {
- if (mProgramHandle != 0) {
- glDetachShader(mProgramHandle, mFragmentShaderHandle);
- }
- glDeleteShader(mFragmentShaderHandle);
- }
-
- if (mProgramHandle != 0) {
- glDeleteProgram(mProgramHandle);
- }
-}
-
-void GenericProgram::compile(string vertexShader, string fragmentShader) {
- mVertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexShader);
- mFragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentShader);
- if (mVertexShaderHandle == 0 || mFragmentShaderHandle == 0) {
- ALOGE("Aborting program creation.");
- return;
- }
- mProgramHandle = createAndLink(mVertexShaderHandle, mFragmentShaderHandle);
- mEngine.checkErrors("Linking program");
-}
-
-void GenericProgram::useProgram() const {
- glUseProgram(mProgramHandle);
-}
-
-GLuint GenericProgram::compileShader(GLuint type, string src) const {
- const GLuint shader = glCreateShader(type);
- if (shader == 0) {
- mEngine.checkErrors("Creating shader");
- return 0;
- }
- const GLchar* charSrc = (const GLchar*)src.c_str();
- glShaderSource(shader, 1, &charSrc, nullptr);
- glCompileShader(shader);
-
- GLint isCompiled = 0;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
- if (isCompiled == GL_FALSE) {
- GLint maxLength = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
- string errorLog;
- errorLog.reserve(maxLength);
- glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data());
- glDeleteShader(shader);
- ALOGE("Error compiling shader: %s", errorLog.c_str());
- return 0;
- }
- return shader;
-}
-GLuint GenericProgram::createAndLink(GLuint vertexShader, GLuint fragmentShader) const {
- const GLuint program = glCreateProgram();
- mEngine.checkErrors("Creating program");
-
- glAttachShader(program, vertexShader);
- glAttachShader(program, fragmentShader);
- glLinkProgram(program);
- mEngine.checkErrors("Linking program");
- return program;
-}
-
-GLuint GenericProgram::getUniformLocation(const string name) const {
- if (mProgramHandle == 0) {
- ALOGE("Can't get location of %s on an invalid program.", name.c_str());
- return -1;
- }
- return glGetUniformLocation(mProgramHandle, (const GLchar*)name.c_str());
-}
-
-GLuint GenericProgram::getAttributeLocation(const string name) const {
- if (mProgramHandle == 0) {
- ALOGE("Can't get location of %s on an invalid program.", name.c_str());
- return -1;
- }
- return glGetAttribLocation(mProgramHandle, (const GLchar*)name.c_str());
-}
-
-bool GenericProgram::isValid() const {
- return mProgramHandle != 0;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/GenericProgram.h b/libs/renderengine/gl/filters/GenericProgram.h
deleted file mode 100644
index 6da2a5af58..0000000000
--- a/libs/renderengine/gl/filters/GenericProgram.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 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 <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GenericProgram {
-public:
- explicit GenericProgram(GLESRenderEngine& renderEngine);
- ~GenericProgram();
- void compile(string vertexShader, string fragmentShader);
- bool isValid() const;
- void useProgram() const;
- GLuint getAttributeLocation(const string name) const;
- GLuint getUniformLocation(const string name) const;
-
-private:
- GLuint compileShader(GLuint type, const string src) const;
- GLuint createAndLink(GLuint vertexShader, GLuint fragmentShader) const;
-
- GLESRenderEngine& mEngine;
- GLuint mVertexShaderHandle = 0;
- GLuint mFragmentShaderHandle = 0;
- GLuint mProgramHandle = 0;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index b3a617c04b..28aa4dd71d 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -46,10 +46,6 @@ struct Buffer {
// Fence that will fire when the buffer is ready to be bound.
sp<Fence> fence = nullptr;
- // Texture identifier to bind the external texture to.
- // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
- uint32_t textureName = 0;
-
// Whether to use filtering when rendering the texture.
bool useTextureFiltering = false;
@@ -64,9 +60,6 @@ struct Buffer {
// overrides the alpha channel of the buffer.
bool isOpaque = false;
- // HDR color-space setting for Y410.
- bool isY410BT2020 = false;
-
float maxLuminanceNits = 0.0;
};
@@ -185,12 +178,10 @@ struct LayerSettings {
// compositionengine/impl/ClientCompositionRequestCache.cpp
static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence &&
- lhs.textureName == rhs.textureName &&
lhs.useTextureFiltering == rhs.useTextureFiltering &&
lhs.textureTransform == rhs.textureTransform &&
lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
- lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
- lhs.maxLuminanceNits == rhs.maxLuminanceNits;
+ lhs.isOpaque == rhs.isOpaque && lhs.maxLuminanceNits == rhs.maxLuminanceNits;
}
static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
@@ -241,13 +232,11 @@ static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
<< (settings.buffer.get() ? decodePixelFormat(settings.buffer->getPixelFormat()).c_str()
: "");
*os << "\n .fence = " << settings.fence.get();
- *os << "\n .textureName = " << settings.textureName;
*os << "\n .useTextureFiltering = " << settings.useTextureFiltering;
*os << "\n .textureTransform = ";
PrintMatrix(settings.textureTransform, os);
*os << "\n .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
*os << "\n .isOpaque = " << settings.isOpaque;
- *os << "\n .isY410BT2020 = " << settings.isY410BT2020;
*os << "\n .maxLuminanceNits = " << settings.maxLuminanceNits;
*os << "\n}";
}
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
deleted file mode 100644
index 167f13f1bc..0000000000
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_MESH_H
-#define SF_RENDER_ENGINE_MESH_H
-
-#include <vector>
-
-#include <stdint.h>
-
-namespace android {
-namespace renderengine {
-
-class Mesh {
-public:
- class Builder;
-
- enum Primitive {
- TRIANGLES = 0x0004, // GL_TRIANGLES
- TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP
- TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN
- };
-
- ~Mesh() = default;
-
- /*
- * VertexArray handles the stride automatically.
- */
- template <typename TYPE>
- class VertexArray {
- friend class Mesh;
- float* mData;
- size_t mStride;
- size_t mOffset = 0;
- VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
-
- public:
- // Returns a vertex array at an offset so its easier to append attributes from
- // multiple sources.
- VertexArray(VertexArray<TYPE>& other, size_t offset)
- : mData(other.mData), mStride(other.mStride), mOffset(offset) {}
-
- TYPE& operator[](size_t index) {
- return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]);
- }
- TYPE const& operator[](size_t index) const {
- return *reinterpret_cast<TYPE const*>(&mData[(index + mOffset) * mStride]);
- }
- };
-
- template <typename TYPE>
- VertexArray<TYPE> getPositionArray() {
- return VertexArray<TYPE>(getPositions(), mStride);
- }
-
- template <typename TYPE>
- VertexArray<TYPE> getTexCoordArray() {
- return VertexArray<TYPE>(getTexCoords(), mStride);
- }
-
- template <typename TYPE>
- VertexArray<TYPE> getCropCoordArray() {
- return VertexArray<TYPE>(getCropCoords(), mStride);
- }
-
- template <typename TYPE>
- VertexArray<TYPE> getShadowColorArray() {
- return VertexArray<TYPE>(getShadowColor(), mStride);
- }
-
- template <typename TYPE>
- VertexArray<TYPE> getShadowParamsArray() {
- return VertexArray<TYPE>(getShadowParams(), mStride);
- }
-
- uint16_t* getIndicesArray() { return getIndices(); }
-
- Primitive getPrimitive() const;
-
- // returns a pointer to the vertices positions
- float const* getPositions() const;
-
- // returns a pointer to the vertices texture coordinates
- float const* getTexCoords() const;
-
- // returns a pointer to the vertices crop coordinates
- float const* getCropCoords() const;
-
- // returns a pointer to colors
- float const* getShadowColor() const;
-
- // returns a pointer to the shadow params
- float const* getShadowParams() const;
-
- // returns a pointer to indices
- uint16_t const* getIndices() const;
-
- // number of vertices in this mesh
- size_t getVertexCount() const;
-
- // dimension of vertices
- size_t getVertexSize() const;
-
- // dimension of texture coordinates
- size_t getTexCoordsSize() const;
-
- size_t getShadowParamsSize() const;
-
- size_t getShadowColorSize() const;
-
- size_t getIndexCount() const;
-
- // return stride in bytes
- size_t getByteStride() const;
-
- // return stride in floats
- size_t getStride() const;
-
-private:
- Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
- size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, size_t indexCount);
- Mesh(const Mesh&);
- Mesh& operator=(const Mesh&);
- Mesh const& operator=(const Mesh&) const;
-
- float* getPositions();
- float* getTexCoords();
- float* getCropCoords();
- float* getShadowColor();
- float* getShadowParams();
- uint16_t* getIndices();
-
- std::vector<float> mVertices;
- size_t mVertexCount;
- size_t mVertexSize;
- size_t mTexCoordsSize;
- size_t mCropCoordsSize;
- size_t mShadowColorSize;
- size_t mShadowParamsSize;
- size_t mStride;
- Primitive mPrimitive;
- std::vector<uint16_t> mIndices;
- size_t mIndexCount;
-};
-
-class Mesh::Builder {
-public:
- Builder& setPrimitive(Primitive primitive) {
- mPrimitive = primitive;
- return *this;
- };
- Builder& setVertices(size_t vertexCount, size_t vertexSize) {
- mVertexCount = vertexCount;
- mVertexSize = vertexSize;
- return *this;
- };
- Builder& setTexCoords(size_t texCoordsSize) {
- mTexCoordsSize = texCoordsSize;
- return *this;
- };
- Builder& setCropCoords(size_t cropCoordsSize) {
- mCropCoordsSize = cropCoordsSize;
- return *this;
- };
- Builder& setShadowAttrs() {
- mShadowParamsSize = 3;
- mShadowColorSize = 4;
- return *this;
- };
- Builder& setIndices(size_t indexCount) {
- mIndexCount = indexCount;
- return *this;
- };
- Mesh build() const {
- return Mesh{mPrimitive, mVertexCount, mVertexSize, mTexCoordsSize,
- mCropCoordsSize, mShadowColorSize, mShadowParamsSize, mIndexCount};
- }
-
-private:
- size_t mVertexCount = 0;
- size_t mVertexSize = 0;
- size_t mTexCoordsSize = 0;
- size_t mCropCoordsSize = 0;
- size_t mShadowColorSize = 0;
- size_t mShadowParamsSize = 0;
- size_t mIndexCount = 0;
- Primitive mPrimitive;
-};
-
-} // namespace renderengine
-} // namespace android
-#endif /* SF_RENDER_ENGINE_MESH_H */
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 0d910c9b29..80aa4c61e6 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -22,8 +22,6 @@
#include <math/mat4.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/ExternalTexture.h>
-#include <renderengine/Framebuffer.h>
-#include <renderengine/Image.h>
#include <renderengine/LayerSettings.h>
#include <stdint.h>
#include <sys/types.h>
@@ -95,8 +93,6 @@ public:
};
enum class RenderEngineType {
- GLES = 1,
- THREADED = 2,
SKIA_GL = 3,
SKIA_GL_THREADED = 4,
SKIA_VK = 5,
@@ -116,9 +112,6 @@ public:
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
- virtual void genTextures(size_t count, uint32_t* names) = 0;
- virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
-
// queries that are required to be thread safe
virtual size_t getMaxTextureSize() const = 0;
virtual size_t getMaxViewportDims() const = 0;
@@ -171,8 +164,6 @@ public:
// being drawn, then the implementation is free to silently ignore this call.
virtual void cleanupPostRender() = 0;
- virtual void cleanFramebufferCache() = 0;
-
// Returns the priority this context was actually created with. Note: this
// may not be the same as specified at context creation time, due to
// implementation limits on the number of contexts that can be created at a
@@ -204,7 +195,7 @@ public:
virtual void setEnableTracing(bool /*tracingEnabled*/) {}
protected:
- RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
+ RenderEngine() : RenderEngine(RenderEngineType::SKIA_GL) {}
RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
@@ -271,14 +262,13 @@ struct RenderEngineCreationArgs {
private:
// must be created by Builder via constructor with full argument list
- RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+ RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize,
bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
bool _supportsBackgroundBlur,
RenderEngine::ContextPriority _contextPriority,
RenderEngine::RenderEngineType _renderEngineType)
: pixelFormat(_pixelFormat),
imageCacheSize(_imageCacheSize),
- useColorManagement(_useColorManagement),
enableProtectedContext(_enableProtectedContext),
precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
supportsBackgroundBlur(_supportsBackgroundBlur),
@@ -298,10 +288,6 @@ struct RenderEngineCreationArgs::Builder {
this->imageCacheSize = imageCacheSize;
return *this;
}
- Builder& setUseColorManagerment(bool useColorManagement) {
- this->useColorManagement = useColorManagement;
- return *this;
- }
Builder& setEnableProtectedContext(bool enableProtectedContext) {
this->enableProtectedContext = enableProtectedContext;
return *this;
@@ -323,16 +309,15 @@ struct RenderEngineCreationArgs::Builder {
return *this;
}
RenderEngineCreationArgs build() const {
- return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
- enableProtectedContext, precacheToneMapperShaderOnly,
- supportsBackgroundBlur, contextPriority, renderEngineType);
+ return RenderEngineCreationArgs(pixelFormat, imageCacheSize, enableProtectedContext,
+ precacheToneMapperShaderOnly, supportsBackgroundBlur,
+ contextPriority, renderEngineType);
}
private:
// 1 means RGBA_8888
int pixelFormat = 1;
uint32_t imageCacheSize = 0;
- bool useColorManagement = true;
bool enableProtectedContext = false;
bool precacheToneMapperShaderOnly = false;
bool supportsBackgroundBlur = false;
diff --git a/libs/renderengine/include/renderengine/Texture.h b/libs/renderengine/include/renderengine/Texture.h
deleted file mode 100644
index c69ace0603..0000000000
--- a/libs/renderengine/include/renderengine/Texture.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_TEXTURE_H
-#define SF_RENDER_ENGINE_TEXTURE_H
-
-#include <stdint.h>
-
-#include <math/mat4.h>
-
-namespace android {
-namespace renderengine {
-
-class Texture {
-public:
- enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 };
-
- Texture();
- Texture(Target textureTarget, uint32_t textureName);
- ~Texture();
-
- void init(Target textureTarget, uint32_t textureName);
-
- void setMatrix(float const* matrix);
- void setFiltering(bool enabled);
- void setDimensions(size_t width, size_t height);
-
- uint32_t getTextureName() const;
- uint32_t getTextureTarget() const;
-
- const mat4& getMatrix() const;
- bool getFiltering() const;
- size_t getWidth() const;
- size_t getHeight() const;
-
-private:
- uint32_t mTextureName;
- uint32_t mTextureTarget;
- size_t mWidth;
- size_t mHeight;
- bool mFiltering;
- mat4 mTextureMatrix;
-};
-
-} // namespace renderengine
-} // namespace android
-#endif /* SF_RENDER_ENGINE_TEXTURE_H */
diff --git a/libs/renderengine/include/renderengine/mock/Framebuffer.h b/libs/renderengine/include/renderengine/mock/Framebuffer.h
deleted file mode 100644
index dfb6a4e41e..0000000000
--- a/libs/renderengine/include/renderengine/mock/Framebuffer.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-#include <renderengine/Framebuffer.h>
-
-namespace android {
-namespace renderengine {
-namespace mock {
-
-class Framebuffer : public renderengine::Framebuffer {
-public:
- Framebuffer();
- ~Framebuffer() override;
-
- MOCK_METHOD3(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool, const bool));
-};
-
-} // namespace mock
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/Image.h b/libs/renderengine/include/renderengine/mock/Image.h
deleted file mode 100644
index 2b0eed1173..0000000000
--- a/libs/renderengine/include/renderengine/mock/Image.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-#include <renderengine/Image.h>
-
-namespace android {
-namespace renderengine {
-namespace mock {
-
-class Image : public renderengine::Image {
-public:
- Image();
- ~Image() override;
-
- MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer* buffer, bool isProtected));
-};
-
-} // namespace mock
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index d3035e24a5..78811908d6 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -19,9 +19,7 @@
#include <gmock/gmock.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>
-#include <renderengine/Mesh.h>
#include <renderengine/RenderEngine.h>
-#include <renderengine/Texture.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <ui/Region.h>
@@ -37,9 +35,6 @@ public:
MOCK_METHOD0(primeCache, std::future<void>());
MOCK_METHOD1(dump, void(std::string&));
- MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
- MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
- MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
MOCK_CONST_METHOD0(isProtected, bool());
@@ -55,7 +50,6 @@ public:
void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&,
const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&,
const bool, base::unique_fd&&));
- MOCK_METHOD0(cleanFramebufferCache, void());
MOCK_METHOD0(getContextPriority, int());
MOCK_METHOD0(supportsBackgroundBlur, bool());
MOCK_METHOD1(onActiveDisplaySizeChanged, void(ui::Size));
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
deleted file mode 100644
index fa6ec10b6e..0000000000
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_
-#define SF_RENDER_ENGINE_DESCRIPTION_H_
-
-#include <renderengine/Texture.h>
-#include <ui/GraphicTypes.h>
-
-namespace android {
-namespace renderengine {
-
-/*
- * This is the structure that holds the state of the rendering engine.
- * This class is used to generate a corresponding GLSL program and set the
- * appropriate uniform.
- */
-struct Description {
- enum class TransferFunction : int {
- LINEAR,
- SRGB,
- ST2084,
- HLG, // Hybrid Log-Gamma for HDR.
- };
-
- static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace);
-
- Description() = default;
- ~Description() = default;
-
- bool hasInputTransformMatrix() const;
- bool hasOutputTransformMatrix() const;
- bool hasColorMatrix() const;
- bool hasDisplayColorMatrix() const;
-
- // whether textures are premultiplied
- bool isPremultipliedAlpha = false;
- // whether this layer is marked as opaque
- bool isOpaque = true;
-
- // corner radius of the layer
- float cornerRadius = 0;
-
- // Size of the rounded rectangle we are cropping to
- half2 cropSize;
-
- // Texture this layer uses
- Texture texture;
- bool textureEnabled = false;
-
- // color used when texturing is disabled or when setting alpha.
- half4 color;
-
- // true if the sampled pixel values are in Y410/BT2020 rather than RGBA
- bool isY410BT2020 = false;
-
- // transfer functions for the input/output
- TransferFunction inputTransferFunction = TransferFunction::LINEAR;
- TransferFunction outputTransferFunction = TransferFunction::LINEAR;
-
- float displayMaxLuminance;
- float maxMasteringLuminance;
- float maxContentLuminance;
-
- // projection matrix
- mat4 projectionMatrix;
-
- // The color matrix will be applied in linear space right before OETF.
- mat4 colorMatrix;
- // The display color matrix will be applied in gamma space after OETF
- mat4 displayColorMatrix;
- mat4 inputTransformMatrix;
- mat4 outputTransformMatrix;
-
- // True if this layer will draw a shadow.
- bool drawShadows = false;
-};
-
-} // namespace renderengine
-} // namespace android
-
-#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/libs/renderengine/mock/Framebuffer.cpp b/libs/renderengine/mock/Framebuffer.cpp
deleted file mode 100644
index fbdcaab697..0000000000
--- a/libs/renderengine/mock/Framebuffer.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 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 <renderengine/mock/Framebuffer.h>
-
-namespace android {
-namespace renderengine {
-namespace mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Framebuffer::Framebuffer() = default;
-Framebuffer::~Framebuffer() = default;
-
-} // namespace mock
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index dad3c1993b..90dcae4369 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -23,7 +23,7 @@
#include <SkImage.h>
#include <include/gpu/ganesh/SkImageGanesh.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
-
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
#include <android/hardware_buffer.h>
#include "ColorSpaces.h"
#include "log/log_main.h"
@@ -40,13 +40,44 @@ AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
- GrBackendFormat backendFormat =
- GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false);
- mBackendTexture =
- GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height,
- &mDeleteProc, &mUpdateProc, &mImageCtx,
- createProtectedImage, backendFormat,
- isOutputBuffer);
+ GrBackendFormat backendFormat;
+
+ GrBackendApi backend = context->backend();
+ if (backend == GrBackendApi::kOpenGL) {
+ backendFormat =
+ GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeGLBackendTexture(context,
+ buffer,
+ desc.width,
+ desc.height,
+ &mDeleteProc,
+ &mUpdateProc,
+ &mImageCtx,
+ createProtectedImage,
+ backendFormat,
+ isOutputBuffer);
+ } else if (backend == GrBackendApi::kVulkan) {
+ backendFormat =
+ GrAHardwareBufferUtils::GetVulkanBackendFormat(context,
+ buffer,
+ desc.format,
+ false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeVulkanBackendTexture(context,
+ buffer,
+ desc.width,
+ desc.height,
+ &mDeleteProc,
+ &mUpdateProc,
+ &mImageCtx,
+ createProtectedImage,
+ backendFormat,
+ isOutputBuffer);
+ } else {
+ LOG_ALWAYS_FATAL("Unexpected backend %d", backend);
+ }
+
mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
@@ -94,7 +125,7 @@ void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace
switch (tex.backend()) {
case GrBackendApi::kOpenGL: {
GrGLTextureInfo textureInfo;
- bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
+ bool retrievedTextureInfo = GrBackendTextures::GetGLTextureInfo(tex, &textureInfo);
LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
"\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
"texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/skia/GLExtensions.cpp
index 3dd534e602..1d4d35fb54 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/skia/GLExtensions.cpp
@@ -23,11 +23,11 @@
#include <stdio.h>
#include <stdlib.h>
-ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions)
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::skia::GLExtensions)
namespace android {
namespace renderengine {
-namespace gl {
+namespace skia {
namespace {
@@ -68,19 +68,19 @@ void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* rende
}
char const* GLExtensions::getVendor() const {
- return mVendor.string();
+ return mVendor.c_str();
}
char const* GLExtensions::getRenderer() const {
- return mRenderer.string();
+ return mRenderer.c_str();
}
char const* GLExtensions::getVersion() const {
- return mVersion.string();
+ return mVersion.c_str();
}
char const* GLExtensions::getExtensions() const {
- return mExtensions.string();
+ return mExtensions.c_str();
}
void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) {
@@ -127,13 +127,13 @@ void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExt
}
char const* GLExtensions::getEGLVersion() const {
- return mEGLVersion.string();
+ return mEGLVersion.c_str();
}
char const* GLExtensions::getEGLExtensions() const {
- return mEGLExtensions.string();
+ return mEGLExtensions.c_str();
}
-} // namespace gl
+} // namespace skia
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/skia/GLExtensions.h
index e415ff304a..0cb1bda0df 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/skia/GLExtensions.h
@@ -29,7 +29,7 @@
namespace android {
namespace renderengine {
-namespace gl {
+namespace skia {
class GLExtensions : public Singleton<GLExtensions> {
public:
@@ -81,7 +81,7 @@ private:
GLExtensions& operator=(const GLExtensions&);
};
-} // namespace gl
+} // namespace skia
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index ff598e7ab5..e253ad596e 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -36,17 +36,26 @@
#include <memory>
#include <numeric>
-#include "../gl/GLExtensions.h"
+#include "GLExtensions.h"
#include "log/log_main.h"
-bool checkGlError(const char* op, int lineNumber);
-
namespace android {
namespace renderengine {
namespace skia {
using base::StringAppendF;
+static bool checkGlError(const char* op, int lineNumber) {
+ bool errorFound = false;
+ GLint error = glGetError();
+ while (error != GL_NO_ERROR) {
+ errorFound = true;
+ error = glGetError();
+ ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
+ }
+ return errorFound;
+}
+
static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
EGLint wanted, EGLConfig* outConfig) {
EGLint numConfigs = -1, n = 0;
@@ -149,7 +158,7 @@ std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(
LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
}
- auto& extensions = gl::GLExtensions::getInstance();
+ auto& extensions = GLExtensions::getInstance();
extensions.initWithEGLStrings(eglVersion, eglExtensions);
// The code assumes that ES2 or later is available if this extension is
@@ -251,14 +260,13 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
- : SkiaRenderEngine(args.renderEngineType,
- static_cast<PixelFormat>(args.pixelFormat),
- args.useColorManagement, args.supportsBackgroundBlur),
+ : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat),
+ args.supportsBackgroundBlur),
mEGLDisplay(display),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
- mProtectedPlaceholderSurface(protectedPlaceholder) { }
+ mProtectedPlaceholderSurface(protectedPlaceholder) {}
SkiaGLRenderEngine::~SkiaGLRenderEngine() {
finishRenderingAndAbandonContext();
@@ -343,8 +351,8 @@ base::unique_fd SkiaGLRenderEngine::flushAndSubmit(GrDirectContext* grContext) {
}
bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) {
- if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
- !gl::GLExtensions::getInstance().hasWaitSync()) {
+ if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+ !GLExtensions::getInstance().hasWaitSync()) {
return false;
}
@@ -379,7 +387,7 @@ bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) {
base::unique_fd SkiaGLRenderEngine::flush() {
ATRACE_CALL();
- if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
+ if (!GLExtensions::getInstance().hasNativeFenceSync()) {
return base::unique_fd();
}
@@ -470,13 +478,13 @@ EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig co
std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority(
const RenderEngineCreationArgs& args) {
- if (!gl::GLExtensions::getInstance().hasContextPriority()) {
+ if (!GLExtensions::getInstance().hasContextPriority()) {
return std::nullopt;
}
switch (args.contextPriority) {
case RenderEngine::ContextPriority::REALTIME:
- if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+ if (GLExtensions::getInstance().hasRealtimePriority()) {
return RenderEngine::ContextPriority::REALTIME;
} else {
ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
@@ -520,7 +528,7 @@ int SkiaGLRenderEngine::getContextPriority() {
}
void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
- const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
+ const GLExtensions& extensions = GLExtensions::getInstance();
StringAppendF(&result, "\n ------------RE GLES------------\n");
StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 871d258ce7..4dd55905fd 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -20,7 +20,6 @@
#include "SkiaRenderEngine.h"
-#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <GrBackendSemaphore.h>
#include <GrContextOptions.h>
#include <SkBlendMode.h>
@@ -55,13 +54,14 @@
#include <android-base/stringprintf.h>
#include <gui/FenceMonitor.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <pthread.h>
#include <src/core/SkTraceEventCommon.h>
#include <sync/sync.h>
#include <ui/BlurRegion.h>
-#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
+#include <ui/HdrRenderTypeUtils.h>
#include <utils/Trace.h>
#include <cmath>
@@ -269,10 +269,8 @@ void SkiaRenderEngine::setEnableTracing(bool tracingEnabled) {
}
SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat,
- bool useColorManagement, bool supportsBackgroundBlur)
- : RenderEngine(type),
- mDefaultPixelFormat(pixelFormat),
- mUseColorManagement(useColorManagement) {
+ bool supportsBackgroundBlur)
+ : RenderEngine(type), mDefaultPixelFormat(pixelFormat) {
if (supportsBackgroundBlur) {
ALOGD("Background Blurs Enabled");
mBlurFilter = new KawaseBlurFilter();
@@ -511,7 +509,8 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader(
auto effect =
shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace,
.outputDataspace = parameters.outputDataSpace,
- .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};
+ .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha,
+ .fakeOutputDataspace = parameters.fakeOutputDataspace};
auto effectIter = mRuntimeEffects.find(effect);
sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
@@ -714,7 +713,9 @@ void SkiaRenderEngine::drawLayersInternal(
SkCanvas* canvas = dstCanvas;
SkiaCapture::OffscreenState offscreenCaptureState;
const LayerSettings* blurCompositionLayer = nullptr;
- if (mBlurFilter) {
+
+ // TODO (b/270314344): Enable blurs in protected context.
+ if (mBlurFilter && !mInProtectedContext) {
bool requiresCompositionLayer = false;
for (const auto& layer : layers) {
// if the layer doesn't have blur or it is not visible then continue
@@ -808,7 +809,8 @@ void SkiaRenderEngine::drawLayersInternal(
const auto [bounds, roundRectClip] =
getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
layer.geometry.roundedCornersRadius);
- if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
+ // TODO (b/270314344): Enable blurs in protected context.
+ if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha) && !mInProtectedContext) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
// if multiple layers have blur, then we need to take a snapshot now because
@@ -816,8 +818,20 @@ void SkiaRenderEngine::drawLayersInternal(
if (!blurInput) {
blurInput = activeSurface->makeImageSnapshot();
}
+
// rect to be blurred in the coordinate space of blurInput
- const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
+ SkRect blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
+
+ // Some layers may be much bigger than the screen. If we used
+ // `blurRect` directly, this would allocate a large buffer with no
+ // benefit. Apply the clip, which already takes the display size
+ // into account. The clipped size will then be used to calculate the
+ // size of the buffer we will create for blurring.
+ if (!blurRect.intersect(SkRect::Make(canvas->getDeviceClipBounds()))) {
+ // This should not happen, but if it did, we would use the full
+ // sized layer, which should still be fine.
+ ALOGW("blur bounds does not intersect display clip!");
+ }
// if the clip needs to be applied then apply it now and make sure
// it is restored before we attempt to draw any shadows.
@@ -892,12 +906,14 @@ void SkiaRenderEngine::drawLayersInternal(
(display.outputDataspace & ui::Dataspace::TRANSFER_MASK) ==
static_cast<int32_t>(ui::Dataspace::TRANSFER_SRGB);
- const ui::Dataspace runtimeEffectDataspace = !dimInLinearSpace && isExtendedHdr
+ const bool useFakeOutputDataspaceForRuntimeEffect = !dimInLinearSpace && isExtendedHdr;
+
+ const ui::Dataspace fakeDataspace = useFakeOutputDataspaceForRuntimeEffect
? static_cast<ui::Dataspace>(
(display.outputDataspace & ui::Dataspace::STANDARD_MASK) |
ui::Dataspace::TRANSFER_GAMMA2_2 |
(display.outputDataspace & ui::Dataspace::RANGE_MASK))
- : display.outputDataspace;
+ : ui::Dataspace::UNKNOWN;
// If the input dataspace is range extended, the output dataspace transfer is sRGB
// and dimmingStage is GAMMA_OETF, dim in linear space instead, and
@@ -908,8 +924,7 @@ void SkiaRenderEngine::drawLayersInternal(
// luminance in linear space, which color pipelines request GAMMA_OETF break
// without a gamma 2.2 fixup.
const bool requiresLinearEffect = layer.colorTransform != mat4() ||
- (mUseColorManagement &&
- needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
+ (needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
(dimInLinearSpace && !equalsWithinMargin(1.f, layerDimmingRatio)) ||
(!dimInLinearSpace && isExtendedHdr);
@@ -920,10 +935,7 @@ void SkiaRenderEngine::drawLayersInternal(
continue;
}
- // If color management is disabled, then mark the source image with the same colorspace as
- // the destination surface so that Skia's color management is a no-op.
- const ui::Dataspace layerDataspace =
- !mUseColorManagement ? display.outputDataspace : layer.sourceDataspace;
+ const ui::Dataspace layerDataspace = layer.sourceDataspace;
SkPaint paint;
if (layer.source.buffer.buffer) {
@@ -1004,7 +1016,8 @@ void SkiaRenderEngine::drawLayersInternal(
.layerDimmingRatio = dimInLinearSpace
? layerDimmingRatio
: 1.f,
- .outputDataSpace = runtimeEffectDataspace}));
+ .outputDataSpace = display.outputDataspace,
+ .fakeOutputDataspace = fakeDataspace}));
// Turn on dithering when dimming beyond this (arbitrary) threshold...
static constexpr float kDimmingThreshold = 0.2f;
@@ -1012,7 +1025,10 @@ void SkiaRenderEngine::drawLayersInternal(
// Most HDR standards require at least 10-bits of color depth for source content, so we
// can just extract the transfer function rather than dig into precise gralloc layout.
// Furthermore, we can assume that the only 8-bit target we support is RGBA8888.
- const bool requiresDownsample = isHdrDataspace(layer.sourceDataspace) &&
+ const bool requiresDownsample =
+ getHdrRenderType(layer.sourceDataspace,
+ std::optional<ui::PixelFormat>(static_cast<ui::PixelFormat>(
+ buffer->getPixelFormat()))) != HdrRenderType::SDR &&
buffer->getPixelFormat() == PIXEL_FORMAT_RGBA_8888;
if (layerDimmingRatio <= kDimmingThreshold || requiresDownsample) {
paint.setDither(true);
@@ -1068,7 +1084,8 @@ void SkiaRenderEngine::drawLayersInternal(
.undoPremultipliedAlpha = false,
.requiresLinearEffect = requiresLinearEffect,
.layerDimmingRatio = layerDimmingRatio,
- .outputDataSpace = runtimeEffectDataspace}));
+ .outputDataSpace = display.outputDataspace,
+ .fakeOutputDataspace = fakeDataspace}));
}
if (layer.disableBlending) {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 6457bfa9eb..d4fc959ba5 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -59,23 +59,17 @@ class BlurFilter;
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaRenderEngine(RenderEngineType type,
- PixelFormat pixelFormat,
- bool useColorManagement,
- bool supportsBackgroundBlur);
+ SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, bool supportsBackgroundBlur);
~SkiaRenderEngine() override;
std::future<void> primeCache() override final;
void cleanupPostRender() override final;
- void cleanFramebufferCache() override final{ }
bool supportsBackgroundBlur() override final {
return mBlurFilter != nullptr;
}
void onActiveDisplaySizeChanged(ui::Size size) override final;
int reportShadersCompiled();
- virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override final{};
- virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override final{};
virtual void setEnableTracing(bool tracingEnabled) override final;
void useProtectedContext(bool useProtectedContext) override;
@@ -157,11 +151,11 @@ private:
bool requiresLinearEffect;
float layerDimmingRatio;
const ui::Dataspace outputDataSpace;
+ const ui::Dataspace fakeOutputDataspace;
};
sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
const PixelFormat mDefaultPixelFormat;
- const bool mUseColorManagement;
// Identifier used for various mappings of layers to various
// textures or shaders
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index c16586bb6b..6ecc6ab362 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -592,7 +592,7 @@ std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create(
SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args)
: SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat),
- args.useColorManagement, args.supportsBackgroundBlur) {}
+ args.supportsBackgroundBlur) {}
SkiaVkRenderEngine::~SkiaVkRenderEngine() {
finishRenderingAndAbandonContext();
diff --git a/libs/renderengine/skia/debug/record.sh b/libs/renderengine/skia/debug/record.sh
index e99b7ae390..c818c40371 100755
--- a/libs/renderengine/skia/debug/record.sh
+++ b/libs/renderengine/skia/debug/record.sh
@@ -16,7 +16,6 @@ elif [ "$1" == "rootandsetup" ]; then
# first time use requires these changes
adb root
adb shell setenforce 0
- adb shell setprop debug.renderengine.backend "skiaglthreaded"
adb shell stop
adb shell start
exit 1;
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
index 0c7335c05d..7bf2b0c16f 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -61,25 +61,7 @@ KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
// Draws the given runtime shader on a GPU (Ganesh) surface and returns the result as an
// SkImage.
-static sk_sp<SkImage> makeImage(GrRecordingContext* context, SkRuntimeShaderBuilder* builder,
- const SkImageInfo& resultInfo) {
- if (resultInfo.alphaType() == kUnpremul_SkAlphaType ||
- resultInfo.alphaType() == kUnknown_SkAlphaType) {
- return nullptr;
- }
- constexpr int kSampleCount = 1;
- constexpr bool kMipmapped = false;
-
- sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context,
- skgpu::Budgeted::kYes,
- resultInfo,
- kSampleCount,
- kTopLeft_GrSurfaceOrigin,
- nullptr,
- kMipmapped);
- if (!surface) {
- return nullptr;
- }
+static sk_sp<SkImage> makeImage(SkSurface* surface, SkRuntimeShaderBuilder* builder) {
sk_sp<SkShader> shader = builder->makeShader(nullptr);
if (!shader) {
return nullptr;
@@ -97,11 +79,16 @@ sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context,
const SkRect& blurRect) const {
LOG_ALWAYS_FATAL_IF(context == nullptr, "%s: Needs GPU context", __func__);
LOG_ALWAYS_FATAL_IF(input == nullptr, "%s: Invalid input image", __func__);
+
+ if (blurRadius == 0) {
+ return input;
+ }
+
// Kawase is an approximation of Gaussian, but it behaves differently from it.
// A radius transformation is required for approximating them, and also to introduce
// non-integer steps, necessary to smoothly interpolate large radii.
float tmpRadius = (float)blurRadius / 2.0f;
- float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+ uint32_t numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
float radiusByPasses = tmpRadius / (float)numberOfPasses;
// create blur surface with the bit depth and colorspace of the original surface
@@ -121,15 +108,33 @@ sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context,
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
- sk_sp<SkImage> tmpBlur = makeImage(context, &blurBuilder, scaledInfo);
-
- // And now we'll build our chain of scaled blur stages
- for (auto i = 1; i < numberOfPasses; i++) {
- LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null for pass %d", __func__, i);
- blurBuilder.child("child") =
- tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
- blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
- tmpBlur = makeImage(context, &blurBuilder, scaledInfo);
+ constexpr int kSampleCount = 1;
+ constexpr bool kMipmapped = false;
+ constexpr SkSurfaceProps* kProps = nullptr;
+ sk_sp<SkSurface> surface =
+ SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, scaledInfo, kSampleCount,
+ kTopLeft_GrSurfaceOrigin, kProps, kMipmapped);
+ LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__);
+ sk_sp<SkImage> tmpBlur = makeImage(surface.get(), &blurBuilder);
+
+ // And now we'll build our chain of scaled blur stages. If there is more than one pass,
+ // create a second surface and ping pong between them.
+ sk_sp<SkSurface> surfaceTwo;
+ if (numberOfPasses <= 1) {
+ LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null", __func__);
+ } else {
+ surfaceTwo = surface->makeSurface(scaledInfo);
+ LOG_ALWAYS_FATAL_IF(!surfaceTwo, "%s: Failed to create second blur surface!", __func__);
+
+ for (auto i = 1; i < numberOfPasses; i++) {
+ LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null for pass %d", __func__, i);
+ blurBuilder.child("child") =
+ tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
+ blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
+ tmpBlur = makeImage(surfaceTwo.get(), &blurBuilder);
+ using std::swap;
+ swap(surface, surfaceTwo);
+ }
}
return tmpBlur;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index f3f2da8a0e..5eec305096 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -109,7 +109,6 @@ public:
virtual renderengine::RenderEngine::RenderEngineType type() = 0;
virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0;
virtual bool typeSupported() = 0;
- virtual bool useColorManagement() const = 0;
};
class SkiaVkRenderEngineFactory : public RenderEngineFactory {
@@ -130,13 +129,11 @@ public:
renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
.setImageCacheSize(1)
- .setUseColorManagerment(false)
.setEnableProtectedContext(false)
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(type())
- .setUseColorManagerment(useColorManagement())
.build();
return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs);
}
@@ -144,14 +141,9 @@ public:
bool typeSupported() override {
return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine();
}
- bool useColorManagement() const override { return false; }
void skip() { GTEST_SKIP(); }
};
-class SkiaVkCMRenderEngineFactory : public SkiaVkRenderEngineFactory {
-public:
- bool useColorManagement() const override { return true; }
-};
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaGLRenderEngineFactory"; }
@@ -170,13 +162,11 @@ public:
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(type())
- .setUseColorManagerment(useColorManagement())
.build();
return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
bool typeSupported() override { return true; }
- bool useColorManagement() const override { return false; }
};
class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
@@ -197,13 +187,11 @@ public:
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(type())
- .setUseColorManagerment(useColorManagement())
.build();
return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
bool typeSupported() override { return true; }
- bool useColorManagement() const override { return true; }
};
class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
@@ -290,9 +278,6 @@ public:
if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
writeBufferToFile("/data/texture_out_");
}
- for (uint32_t texName : mTexNames) {
- mRE->deleteTextures(1, &texName);
- }
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
@@ -635,8 +620,6 @@ public:
std::unique_ptr<renderengine::RenderEngine> mRE;
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
-
- std::vector<uint32_t> mTexNames;
};
void RenderEngineTest::initializeRenderEngine() {
@@ -680,9 +663,6 @@ struct BufferSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
RenderEngineTest* fixture) {
const auto buf = fixture->allocateSourceBuffer(1, 1);
- uint32_t texName;
- fixture->mRE->genTextures(1, &texName);
- fixture->mTexNames.push_back(texName);
uint8_t* pixels;
buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -702,7 +682,6 @@ struct BufferSourceVariant {
buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
- layer.source.buffer.textureName = texName;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
OpaquenessVariant::setOpaqueBit(layer);
}
@@ -1251,9 +1230,6 @@ void RenderEngineTest::fillRedBufferTextureTransform() {
// Here will allocate a checker board texture, but transform texture
// coordinates so that only the upper left is applied.
const auto buf = allocateSourceBuffer(2, 2);
- uint32_t texName;
- RenderEngineTest::mRE->genTextures(1, &texName);
- this->mTexNames.push_back(texName);
uint8_t* pixels;
buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -1274,7 +1250,6 @@ void RenderEngineTest::fillRedBufferTextureTransform() {
buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
- layer.source.buffer.textureName = texName;
// Transform coordinates to only be inside the red quadrant.
layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f));
layer.alpha = 1.0f;
@@ -1300,9 +1275,6 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() {
renderengine::LayerSettings layer;
const auto buf = allocateSourceBuffer(1, 1);
- uint32_t texName;
- RenderEngineTest::mRE->genTextures(1, &texName);
- this->mTexNames.push_back(texName);
uint8_t* pixels;
buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -1314,7 +1286,6 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() {
buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
- layer.source.buffer.textureName = texName;
layer.source.buffer.usePremultipliedAlpha = true;
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -1339,9 +1310,6 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
renderengine::LayerSettings layer;
const auto buf = allocateSourceBuffer(1, 1);
- uint32_t texName;
- RenderEngineTest::mRE->genTextures(1, &texName);
- this->mTexNames.push_back(texName);
uint8_t* pixels;
buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -1353,7 +1321,6 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
- layer.source.buffer.textureName = texName;
layer.source.buffer.usePremultipliedAlpha = false;
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -1559,9 +1526,7 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3
INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
- std::make_shared<SkiaGLESCMRenderEngineFactory>(),
- std::make_shared<SkiaVkRenderEngineFactory>(),
- std::make_shared<SkiaVkCMRenderEngineFactory>()));
+ std::make_shared<SkiaVkRenderEngineFactory>()));
TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
if (!GetParam()->typeSupported()) {
@@ -1745,7 +1710,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -1756,7 +1721,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -1895,7 +1860,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource)
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -1906,7 +1871,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_o
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -2045,7 +2010,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -2056,7 +2021,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_b
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -2592,10 +2557,6 @@ TEST_P(RenderEngineTest, testBorder) {
GTEST_SKIP();
}
- if (!GetParam()->useColorManagement()) {
- GTEST_SKIP();
- }
-
initializeRenderEngine();
const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB;
@@ -3017,15 +2978,11 @@ TEST_P(RenderEngineTest, test_isOpaque) {
std::vector<renderengine::LayerSettings> layers{greenLayer};
invokeDraw(display, layers);
- if (GetParam()->useColorManagement()) {
- expectBufferColor(rect, 117, 251, 76, 255);
- } else {
- expectBufferColor(rect, 0, 255, 0, 255);
- }
+ expectBufferColor(rect, 117, 251, 76, 255);
}
TEST_P(RenderEngineTest, test_tonemapPQMatches) {
- if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) {
+ if (!GetParam()->typeSupported()) {
GTEST_SKIP();
}
@@ -3042,7 +2999,7 @@ TEST_P(RenderEngineTest, test_tonemapPQMatches) {
}
TEST_P(RenderEngineTest, test_tonemapHLGMatches) {
- if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) {
+ if (!GetParam()->typeSupported()) {
GTEST_SKIP();
}
@@ -3262,9 +3219,9 @@ TEST_P(RenderEngineTest, primeShaderCache) {
fut.wait();
}
- const int minimumExpectedShadersCompiled = GetParam()->useColorManagement() ? 60 : 30;
+ static constexpr int kMinimumExpectedShadersCompiled = 60;
ASSERT_GT(static_cast<skia::SkiaGLRenderEngine*>(mRE.get())->reportShadersCompiled(),
- minimumExpectedShadersCompiled);
+ kMinimumExpectedShadersCompiled);
}
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index fe3a16d4bf..281a502b06 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -36,7 +36,7 @@ struct RenderEngineThreadedTest : public ::testing::Test {
void SetUp() override {
mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
[this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); },
- renderengine::RenderEngine::RenderEngineType::THREADED);
+ renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED);
}
std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
@@ -57,18 +57,6 @@ TEST_F(RenderEngineThreadedTest, primeCache) {
mThreadedRE->getContextPriority();
}
-TEST_F(RenderEngineThreadedTest, genTextures) {
- uint32_t texName;
- EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
- mThreadedRE->genTextures(1, &texName);
-}
-
-TEST_F(RenderEngineThreadedTest, deleteTextures) {
- uint32_t texName;
- EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
- mThreadedRE->deleteTextures(1, &texName);
-}
-
TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
size_t size = 20;
EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 6a1561abcd..3bc9e6d7e0 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -27,8 +27,6 @@
#include <processgroup/processgroup.h>
#include <utils/Trace.h>
-#include "gl/GLESRenderEngine.h"
-
using namespace std::chrono_literals;
namespace android {
@@ -175,46 +173,6 @@ void RenderEngineThreaded::dump(std::string& result) {
result.assign(resultFuture.get());
}
-void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
- ATRACE_CALL();
- // This is a no-op in SkiaRenderEngine.
- if (getRenderEngineType() != RenderEngineType::THREADED) {
- return;
- }
- std::promise<void> resultPromise;
- std::future<void> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::genTextures");
- instance.genTextures(count, names);
- resultPromise.set_value();
- });
- }
- mCondition.notify_one();
- resultFuture.wait();
-}
-
-void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
- ATRACE_CALL();
- // This is a no-op in SkiaRenderEngine.
- if (getRenderEngineType() != RenderEngineType::THREADED) {
- return;
- }
- std::promise<void> resultPromise;
- std::future<void> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::deleteTextures");
- instance.deleteTextures(count, names);
- resultPromise.set_value();
- });
- }
- mCondition.notify_one();
- resultFuture.wait();
-}
-
void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) {
ATRACE_CALL();
@@ -313,20 +271,6 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
return resultFuture;
}
-void RenderEngineThreaded::cleanFramebufferCache() {
- ATRACE_CALL();
- // This function is designed so it can run asynchronously, so we do not need to wait
- // for the futures.
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::cleanFramebufferCache");
- instance.cleanFramebufferCache();
- });
- }
- mCondition.notify_one();
-}
-
int RenderEngineThreaded::getContextPriority() {
std::promise<int> resultPromise;
std::future<int> resultFuture = resultPromise.get_future();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 6eb108e064..a7ecab2931 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -46,8 +46,6 @@ public:
void dump(std::string& result) override;
- void genTextures(size_t count, uint32_t* names) override;
- void deleteTextures(size_t count, uint32_t const* names) override;
size_t getMaxTextureSize() const override;
size_t getMaxViewportDims() const override;
@@ -60,7 +58,6 @@ public:
const bool useFramebufferCache,
base::unique_fd&& bufferFence) override;
- void cleanFramebufferCache() override;
int getContextPriority() override;
bool supportsBackgroundBlur() override;
void onActiveDisplaySizeChanged(ui::Size size) override;
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index c85517a976..ef039e5c36 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -168,8 +168,8 @@ void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
void generateOETF(std::string& shader) {
// Only support gamma 2.2 for now
shader.append(R"(
- float OETF(float3 linear) {
- return sign(linear) * pow(abs(linear), (1.0 / 2.2));
+ float3 OETF(float3 linear) {
+ return sign(linear) * pow(abs(linear), float3(1.0 / 2.2));
}
)");
}
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index cc96f83578..4be0a3a9d9 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -115,7 +115,7 @@ sp<Fence> Fence::merge(const char* name, const sp<Fence>& f1,
sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
const sp<Fence>& f2) {
- return merge(name.string(), f1, f2);
+ return merge(name.c_str(), f1, f2);
}
int Fence::dup() const {
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index c3b2d3d808..37ebfc4617 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -31,10 +31,15 @@ using namespace aidl::android::hardware::graphics::allocator;
using namespace aidl::android::hardware::graphics::common;
using namespace ::android::hardware::graphics::mapper;
+using ADataspace = aidl::android::hardware::graphics::common::Dataspace;
+using APixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+
namespace android {
static const auto kIAllocatorServiceName = IAllocator::descriptor + std::string("/default");
static const auto kIAllocatorMinimumVersion = 2;
+constexpr const char* kStandardMetadataName =
+ "android.hardware.graphics.common.StandardMetadataType";
// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
static constexpr uint64_t kRemovedUsageBits = static_cast<uint64_t>((1 << 10) | (1 << 13));
@@ -284,17 +289,205 @@ bool Gralloc5Mapper::isLoaded() const {
return mMapper != nullptr && mMapper->version >= AIMAPPER_VERSION_5;
}
+static bool isStandardMetadata(AIMapper_MetadataType metadataType) {
+ return strcmp(kStandardMetadataName, metadataType.name) == 0;
+}
+
+struct DumpBufferResult {
+ uint64_t bufferId;
+ std::string name;
+ uint64_t width;
+ uint64_t height;
+ uint64_t layerCount;
+ APixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ uint64_t pixelFormatModifier;
+ BufferUsage usage;
+ ADataspace dataspace;
+ uint64_t allocationSize;
+ uint64_t protectedContent;
+ ExtendableType compression;
+ ExtendableType interlaced;
+ ExtendableType chromaSiting;
+ std::vector<ui::PlaneLayout> planeLayouts;
+};
+
+#define DECODE_TO(name, output) \
+ case StandardMetadataType::name: \
+ output = StandardMetadata<StandardMetadataType::name>::value ::decode(value, valueSize) \
+ .value(); \
+ break
+
+static void dumpBufferCommon(DumpBufferResult* outResult, AIMapper_MetadataType metadataType,
+ const void* value, size_t valueSize) {
+ if (!isStandardMetadata(metadataType)) {
+ return;
+ }
+ StandardMetadataType type = (StandardMetadataType)metadataType.value;
+ switch (type) {
+ DECODE_TO(BUFFER_ID, outResult->bufferId);
+ DECODE_TO(NAME, outResult->name);
+ DECODE_TO(WIDTH, outResult->width);
+ DECODE_TO(HEIGHT, outResult->height);
+ DECODE_TO(LAYER_COUNT, outResult->layerCount);
+ DECODE_TO(PIXEL_FORMAT_REQUESTED, outResult->pixelFormatRequested);
+ DECODE_TO(PIXEL_FORMAT_FOURCC, outResult->pixelFormatFourCC);
+ DECODE_TO(PIXEL_FORMAT_MODIFIER, outResult->pixelFormatModifier);
+ DECODE_TO(USAGE, outResult->usage);
+ DECODE_TO(DATASPACE, outResult->dataspace);
+ DECODE_TO(ALLOCATION_SIZE, outResult->allocationSize);
+ DECODE_TO(PROTECTED_CONTENT, outResult->protectedContent);
+ DECODE_TO(COMPRESSION, outResult->compression);
+ DECODE_TO(INTERLACED, outResult->interlaced);
+ DECODE_TO(CHROMA_SITING, outResult->chromaSiting);
+ DECODE_TO(PLANE_LAYOUTS, outResult->planeLayouts);
+ default:
+ break;
+ }
+}
+
+#undef DECODE_TO
+
+template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>{}>>
+constexpr std::underlying_type_t<EnumT> to_underlying(EnumT e) noexcept {
+ return static_cast<std::underlying_type_t<EnumT>>(e);
+}
+
+static void writeDumpToStream(const DumpBufferResult& bufferDump, std::ostream& outDump,
+ bool less) {
+ double allocationSizeKiB = static_cast<double>(bufferDump.allocationSize) / 1024;
+
+ outDump << "+ name:" << bufferDump.name << ", id:" << bufferDump.bufferId
+ << ", size:" << std::fixed << allocationSizeKiB << "KiB, w/h:" << bufferDump.width
+ << "x" << bufferDump.height << ", usage: 0x" << std::hex
+ << to_underlying(bufferDump.usage) << std::dec
+ << ", req fmt:" << to_underlying(bufferDump.pixelFormatRequested)
+ << ", fourcc/mod:" << bufferDump.pixelFormatFourCC << "/"
+ << bufferDump.pixelFormatModifier << ", dataspace: 0x" << std::hex
+ << to_underlying(bufferDump.dataspace) << std::dec << ", compressed: ";
+
+ if (less) {
+ bool isCompressed = !gralloc4::isStandardCompression(bufferDump.compression) ||
+ (gralloc4::getStandardCompressionValue(bufferDump.compression) !=
+ ui::Compression::NONE);
+ outDump << std::boolalpha << isCompressed << "\n";
+ } else {
+ outDump << gralloc4::getCompressionName(bufferDump.compression) << "\n";
+ }
+
+ if (!less) {
+ bool firstPlane = true;
+ for (const auto& planeLayout : bufferDump.planeLayouts) {
+ if (firstPlane) {
+ firstPlane = false;
+ outDump << "\tplanes: ";
+ } else {
+ outDump << "\t ";
+ }
+
+ for (size_t i = 0; i < planeLayout.components.size(); i++) {
+ const auto& planeLayoutComponent = planeLayout.components[i];
+ outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
+ if (i < planeLayout.components.size() - 1) {
+ outDump << "/";
+ } else {
+ outDump << ":\t";
+ }
+ }
+ outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
+ << ", stride:" << planeLayout.strideInBytes
+ << " bytes, size:" << planeLayout.totalSizeInBytes;
+ outDump << ", inc:" << planeLayout.sampleIncrementInBits
+ << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
+ << planeLayout.verticalSubsampling;
+ outDump << "\n";
+ }
+
+ outDump << "\tlayer cnt: " << bufferDump.layerCount
+ << ", protected content: " << bufferDump.protectedContent
+ << ", interlaced: " << gralloc4::getInterlacedName(bufferDump.interlaced)
+ << ", chroma siting:" << gralloc4::getChromaSitingName(bufferDump.chromaSiting)
+ << "\n";
+ }
+}
+
std::string Gralloc5Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
- // TODO(b/261858392): Implement
- (void)bufferHandle;
- (void)less;
- return {};
+ DumpBufferResult bufferInfo;
+ AIMapper_DumpBufferCallback dumpBuffer = [](void* contextPtr,
+ AIMapper_MetadataType metadataType,
+ const void* _Nonnull value, size_t valueSize) {
+ DumpBufferResult* context = reinterpret_cast<DumpBufferResult*>(contextPtr);
+ dumpBufferCommon(context, metadataType, value, valueSize);
+ };
+ AIMapper_Error error = mMapper->v5.dumpBuffer(bufferHandle, dumpBuffer, &bufferInfo);
+ if (error != AIMAPPER_ERROR_NONE) {
+ ALOGE("Error dumping buffer: %d", error);
+ return std::string{};
+ }
+ std::ostringstream stream;
+ stream.precision(2);
+ writeDumpToStream(bufferInfo, stream, less);
+ return stream.str();
}
std::string Gralloc5Mapper::dumpBuffers(bool less) const {
- // TODO(b/261858392): Implement
- (void)less;
- return {};
+ class DumpAllBuffersContext {
+ private:
+ bool mHasPending = false;
+ DumpBufferResult mPending;
+ std::vector<DumpBufferResult> mResults;
+
+ public:
+ DumpAllBuffersContext() { mResults.reserve(10); }
+
+ void commit() {
+ if (mHasPending) {
+ mResults.push_back(mPending);
+ mHasPending = false;
+ }
+ }
+
+ DumpBufferResult* write() {
+ mHasPending = true;
+ return &mPending;
+ }
+
+ const std::vector<DumpBufferResult>& results() {
+ commit();
+ return mResults;
+ }
+ } context;
+
+ AIMapper_BeginDumpBufferCallback beginCallback = [](void* contextPtr) {
+ DumpAllBuffersContext* context = reinterpret_cast<DumpAllBuffersContext*>(contextPtr);
+ context->commit();
+ };
+
+ AIMapper_DumpBufferCallback dumpBuffer = [](void* contextPtr,
+ AIMapper_MetadataType metadataType,
+ const void* _Nonnull value, size_t valueSize) {
+ DumpAllBuffersContext* context = reinterpret_cast<DumpAllBuffersContext*>(contextPtr);
+ dumpBufferCommon(context->write(), metadataType, value, valueSize);
+ };
+
+ AIMapper_Error error = mMapper->v5.dumpAllBuffers(beginCallback, dumpBuffer, &context);
+ if (error != AIMAPPER_ERROR_NONE) {
+ ALOGE("Error dumping buffers: %d", error);
+ return std::string{};
+ }
+ uint64_t totalAllocationSize = 0;
+ std::ostringstream stream;
+ stream.precision(2);
+ stream << "Imported gralloc buffers:\n";
+
+ for (const auto& bufferDump : context.results()) {
+ writeDumpToStream(bufferDump, stream, less);
+ totalAllocationSize += bufferDump.allocationSize;
+ }
+
+ double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
+ stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
+ return stream.str();
}
status_t Gralloc5Mapper::importBuffer(const native_handle_t *rawHandle,
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index c0abec23e0..eb0bd4ed0a 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -89,14 +89,14 @@ void GraphicBufferAllocator::dump(std::string& result, bool less) const {
uint64_t total = 0;
result.append("GraphicBufferAllocator buffers:\n");
const size_t count = list.size();
- StringAppendF(&result, "%10s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
+ StringAppendF(&result, "%14s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
"W (Stride) x H", "Layers", "Format", "Usage", "Requestor");
for (size_t i = 0; i < count; i++) {
const alloc_rec_t& rec(list.valueAt(i));
std::string sizeStr = (rec.size)
? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0)
: "unknown";
- StringAppendF(&result, "%10p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
+ StringAppendF(&result, "%14p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height,
rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
total += rec.size;
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
index cb61e6a320..494272b1a8 100644
--- a/libs/ui/include/ui/FatVector.h
+++ b/libs/ui/include/ui/FatVector.h
@@ -65,6 +65,17 @@ public:
free(p);
}
}
+
+ // The STL checks that this member type is present so that
+ // std::allocator_traits<InlineStdAllocator<T, SIZE>>::rebind_alloc<Other>
+ // works. std::vector won't be able to construct an
+ // InlineStdAllocator<Other, SIZE>, because InlineStdAllocator has no
+ // default constructor, but vector presumably doesn't rebind the allocator
+ // because it doesn't allocate internal node types.
+ template <class Other>
+ struct rebind {
+ typedef InlineStdAllocator<Other, SIZE> other;
+ };
Allocation& mAllocation;
};
diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
new file mode 100644
index 0000000000..b0af878cdb
--- /dev/null
+++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 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 <ui/GraphicTypes.h>
+
+namespace android {
+
+enum class HdrRenderType {
+ SDR, // just render to SDR
+ DISPLAY_HDR, // HDR by extended brightness
+ GENERIC_HDR // tonemapped HDR
+};
+
+/***
+ * A helper function to classify how we treat the result based on params.
+ *
+ * @param dataspace the dataspace
+ * @param pixelFormat optional, in case there is no source buffer.
+ * @param hdrSdrRatio default is 1.f, render engine side doesn't take care of it.
+ * @return HdrRenderType
+ */
+inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace,
+ std::optional<ui::PixelFormat> pixelFormat,
+ float hdrSdrRatio = 1.f) {
+ const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+ const auto range = dataspace & HAL_DATASPACE_RANGE_MASK;
+
+ if (transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG) {
+ return HdrRenderType::GENERIC_HDR;
+ }
+
+ static const auto BT2020_LINEAR_EXT = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_LINEAR |
+ HAL_DATASPACE_RANGE_EXTENDED);
+
+ if ((dataspace == BT2020_LINEAR_EXT || dataspace == ui::Dataspace::V0_SCRGB) &&
+ pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16) {
+ return HdrRenderType::GENERIC_HDR;
+ }
+
+ // Extended range layer with an hdr/sdr ratio of > 1.01f can "self-promote" to HDR.
+ if (range == HAL_DATASPACE_RANGE_EXTENDED && hdrSdrRatio > 1.01f) {
+ return HdrRenderType::DISPLAY_HDR;
+ }
+
+ return HdrRenderType::SDR;
+}
+
+} // namespace android \ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 831b64d877..8ce017d7a3 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -164,9 +164,9 @@ cc_test {
}
cc_test {
- name: "DataspaceUtils_test",
+ name: "HdrRenderTypeUtils_test",
shared_libs: ["libui"],
- srcs: ["DataspaceUtils_test.cpp"],
+ srcs: ["HdrRenderTypeUtils_test.cpp"],
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp
deleted file mode 100644
index 3e0967182b..0000000000
--- a/libs/ui/tests/DataspaceUtils_test.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "DataspaceUtilsTest"
-
-#include <gtest/gtest.h>
-#include <ui/DataspaceUtils.h>
-
-namespace android {
-
-class DataspaceUtilsTest : public testing::Test {};
-
-TEST_F(DataspaceUtilsTest, isHdrDataspace) {
- EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_HLG));
- EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ));
- EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ));
- EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG));
-
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR));
- // scRGB defines a very wide gamut but not an expanded luminance range
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT709));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3_LINEAR));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3_LINEAR));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::ADOBE_RGB));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_LINEAR));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_ITU));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_BT2020));
-}
-
-} // namespace android
diff --git a/libs/ui/tests/HdrRenderTypeUtils_test.cpp b/libs/ui/tests/HdrRenderTypeUtils_test.cpp
new file mode 100644
index 0000000000..efe819db76
--- /dev/null
+++ b/libs/ui/tests/HdrRenderTypeUtils_test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HdrRenderTypeUtilsTest"
+
+#include <gtest/gtest.h>
+#include <ui/HdrRenderTypeUtils.h>
+
+namespace android {
+
+class HdrRenderTypeUtilsTest : public testing::Test {};
+
+TEST_F(HdrRenderTypeUtilsTest, getHdrRenderType) {
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_ITU_HLG, std::nullopt),
+ HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_ITU_PQ, std::nullopt),
+ HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_PQ, std::nullopt), HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_HLG, std::nullopt),
+ HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB,
+ std::optional<ui::PixelFormat>(ui::PixelFormat::RGBA_FP16)),
+ HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB,
+ std::optional<ui::PixelFormat>(ui::PixelFormat::RGBA_8888), 2.f),
+ HdrRenderType::DISPLAY_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB_LINEAR,
+ std::optional<ui::PixelFormat>(ui::PixelFormat::RGBA_8888), 2.f),
+ HdrRenderType::DISPLAY_HDR);
+
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SRGB_LINEAR, std::nullopt), HdrRenderType::SDR);
+ // scRGB defines a very wide gamut but not an expanded luminance range
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB_LINEAR, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SRGB, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_JFIF, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_BT601_625, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_BT601_525, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_BT709, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DCI_P3_LINEAR, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DCI_P3, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DISPLAY_P3_LINEAR, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DISPLAY_P3, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::ADOBE_RGB, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_LINEAR, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_ITU, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DISPLAY_BT2020, std::nullopt), HdrRenderType::SDR);
+}
+
+} // namespace android
diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp
index e3f709b7f5..9deba01dc8 100644
--- a/libs/ultrahdr/Android.bp
+++ b/libs/ultrahdr/Android.bp
@@ -14,11 +14,10 @@
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
+ default_applicable_licenses: [
+ "frameworks_native_license",
+ "adobe_hdr_gain_map_license",
+ ],
}
cc_library {
diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
new file mode 100644
index 0000000000..e999a8bd28
--- /dev/null
+++ b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
@@ -0,0 +1,19 @@
+// Copyright 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+license {
+ name: "adobe_hdr_gain_map_license",
+ license_kinds: ["legacy_by_exception_only"],
+ license_text: ["NOTICE"],
+}
diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE b/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE
new file mode 100644
index 0000000000..3f6c5944c7
--- /dev/null
+++ b/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE
@@ -0,0 +1 @@
+This product includes Gain Map technology under license by Adobe.
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
index ad1d57aaee..f1f403548d 100644
--- a/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
+++ b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
@@ -54,7 +54,7 @@ void UltraHdrDecFuzzer::process() {
std::cout << "input buffer size " << jpegImgR.length << std::endl;
std::cout << "image dimensions " << info.width << " x " << info.width << std::endl;
#endif
- size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_SDR) ? 4 : 8);
+ size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
jpegr_uncompressed_struct decodedJpegR;
auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
decodedJpegR.data = decodedRaw.get();
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
index bbe58e0f2e..2d59e8bb88 100644
--- a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
+++ b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
@@ -23,22 +23,12 @@
// User include files
#include "ultrahdr/gainmapmath.h"
+#include "ultrahdr/jpegdecoderhelper.h"
#include "ultrahdr/jpegencoderhelper.h"
#include "utils/Log.h"
using namespace android::ultrahdr;
-// constants
-const int kMinWidth = 8;
-const int kMaxWidth = 7680;
-
-const int kMinHeight = 8;
-const int kMaxHeight = 4320;
-
-const int kScaleFactor = 4;
-
-const int kJpegBlock = 16;
-
// Color gamuts for image data, sync with ultrahdr.h
const int kCgMin = ULTRAHDR_COLORGAMUT_UNSPECIFIED + 1;
const int kCgMax = ULTRAHDR_COLORGAMUT_MAX;
@@ -60,7 +50,7 @@ public:
UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
void process();
void fillP010Buffer(uint16_t* data, int width, int height, int stride);
- void fill420Buffer(uint8_t* data, int size);
+ void fill420Buffer(uint8_t* data, int width, int height, int stride);
private:
FuzzedDataProvider mFdp;
@@ -70,11 +60,12 @@ void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, in
uint16_t* tmp = data;
std::vector<uint16_t> buffer(16);
for (int i = 0; i < buffer.size(); i++) {
- buffer[i] = mFdp.ConsumeIntegralInRange<int>(0, (1 << 10) - 1);
+ buffer[i] = (mFdp.ConsumeIntegralInRange<int>(0, (1 << 10) - 1)) << 6;
}
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i += buffer.size()) {
- memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (width - i)));
+ memcpy(tmp + i, buffer.data(),
+ std::min((int)buffer.size(), (width - i)) * sizeof(*data));
std::shuffle(buffer.begin(), buffer.end(),
std::default_random_engine(std::random_device{}()));
}
@@ -82,13 +73,18 @@ void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, in
}
}
-void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int size) {
+void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int width, int height, int stride) {
+ uint8_t* tmp = data;
std::vector<uint8_t> buffer(16);
mFdp.ConsumeData(buffer.data(), buffer.size());
- for (int i = 0; i < size; i += buffer.size()) {
- memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (size - i)));
- std::shuffle(buffer.begin(), buffer.end(),
- std::default_random_engine(std::random_device{}()));
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i += buffer.size()) {
+ memcpy(tmp + i, buffer.data(),
+ std::min((int)buffer.size(), (width - i)) * sizeof(*data));
+ std::shuffle(buffer.begin(), buffer.end(),
+ std::default_random_engine(std::random_device{}()));
+ }
+ tmp += stride;
}
}
@@ -129,9 +125,10 @@ void UltraHdrEncFuzzer::process() {
int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
height = (height >> 1) << 1;
- std::unique_ptr<uint16_t[]> bufferY = nullptr;
- std::unique_ptr<uint16_t[]> bufferUV = nullptr;
- std::unique_ptr<uint8_t[]> yuv420ImgRaw = nullptr;
+ std::unique_ptr<uint16_t[]> bufferYHdr = nullptr;
+ std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr;
+ std::unique_ptr<uint8_t[]> bufferYSdr = nullptr;
+ std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr;
std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
if (muxSwitch != 4) {
// init p010 image
@@ -145,30 +142,27 @@ void UltraHdrEncFuzzer::process() {
int bppP010 = 2;
if (isUVContiguous) {
size_t p010Size = yStride * height * 3 / 2;
- bufferY = std::make_unique<uint16_t[]>(p010Size);
- p010Img.data = bufferY.get();
+ bufferYHdr = std::make_unique<uint16_t[]>(p010Size);
+ p010Img.data = bufferYHdr.get();
p010Img.chroma_data = nullptr;
p010Img.chroma_stride = 0;
- fillP010Buffer(bufferY.get(), width, height, yStride);
- fillP010Buffer(bufferY.get() + yStride * height, width, height / 2, yStride);
+ fillP010Buffer(bufferYHdr.get(), width, height, yStride);
+ fillP010Buffer(bufferYHdr.get() + yStride * height, width, height / 2, yStride);
} else {
int uvStride = mFdp.ConsumeIntegralInRange<int>(width, width + 128);
size_t p010YSize = yStride * height;
- bufferY = std::make_unique<uint16_t[]>(p010YSize);
- p010Img.data = bufferY.get();
- fillP010Buffer(bufferY.get(), width, height, yStride);
+ bufferYHdr = std::make_unique<uint16_t[]>(p010YSize);
+ p010Img.data = bufferYHdr.get();
+ fillP010Buffer(bufferYHdr.get(), width, height, yStride);
size_t p010UVSize = uvStride * p010Img.height / 2;
- bufferUV = std::make_unique<uint16_t[]>(p010UVSize);
- p010Img.chroma_data = bufferUV.get();
+ bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize);
+ p010Img.chroma_data = bufferUVHdr.get();
p010Img.chroma_stride = uvStride;
- fillP010Buffer(bufferUV.get(), width, height / 2, uvStride);
+ fillP010Buffer(bufferUVHdr.get(), width, height / 2, uvStride);
}
} else {
- int map_width = width / kScaleFactor;
- int map_height = height / kScaleFactor;
- map_width = static_cast<size_t>(floor((map_width + kJpegBlock - 1) / kJpegBlock)) *
- kJpegBlock;
- map_height = ((map_height + 1) >> 1) << 1;
+ size_t map_width = width / kMapDimensionScaleFactor;
+ size_t map_height = height / kMapDimensionScaleFactor;
// init 400 image
grayImg.width = map_width;
grayImg.height = map_height;
@@ -177,7 +171,7 @@ void UltraHdrEncFuzzer::process() {
const size_t graySize = map_width * map_height;
grayImgRaw = std::make_unique<uint8_t[]>(graySize);
grayImg.data = grayImgRaw.get();
- fill420Buffer(grayImgRaw.get(), graySize);
+ fill420Buffer(grayImgRaw.get(), map_width, map_height, map_width);
grayImg.chroma_data = nullptr;
grayImg.luma_stride = 0;
grayImg.chroma_stride = 0;
@@ -185,17 +179,38 @@ void UltraHdrEncFuzzer::process() {
if (muxSwitch > 0) {
// init 420 image
+ bool isUVContiguous = mFdp.ConsumeBool();
+ bool hasYStride = mFdp.ConsumeBool();
+ int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
yuv420Img.width = width;
yuv420Img.height = height;
yuv420Img.colorGamut = yuv420Cg;
-
- const size_t yuv420Size = (yuv420Img.width * yuv420Img.height * 3) / 2;
- yuv420ImgRaw = std::make_unique<uint8_t[]>(yuv420Size);
- yuv420Img.data = yuv420ImgRaw.get();
- fill420Buffer(yuv420ImgRaw.get(), yuv420Size);
- yuv420Img.chroma_data = nullptr;
- yuv420Img.luma_stride = 0;
- yuv420Img.chroma_stride = 0;
+ yuv420Img.luma_stride = hasYStride ? yStride : 0;
+ if (isUVContiguous) {
+ size_t yuv420Size = yStride * height * 3 / 2;
+ bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size);
+ yuv420Img.data = bufferYSdr.get();
+ yuv420Img.chroma_data = nullptr;
+ yuv420Img.chroma_stride = 0;
+ fill420Buffer(bufferYSdr.get(), width, height, yStride);
+ fill420Buffer(bufferYSdr.get() + yStride * height, width / 2, height / 2,
+ yStride / 2);
+ fill420Buffer(bufferYSdr.get() + yStride * height * 5 / 4, width / 2, height / 2,
+ yStride / 2);
+ } else {
+ int uvStride = mFdp.ConsumeIntegralInRange<int>(width / 2, width / 2 + 128);
+ size_t yuv420YSize = yStride * height;
+ bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize);
+ yuv420Img.data = bufferYSdr.get();
+ fill420Buffer(bufferYSdr.get(), width, height, yStride);
+ size_t yuv420UVSize = uvStride * yuv420Img.height / 2 * 2;
+ bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
+ yuv420Img.chroma_data = bufferUVSdr.get();
+ yuv420Img.chroma_stride = uvStride;
+ fill420Buffer(bufferUVSdr.get(), width / 2, height / 2, uvStride);
+ fill420Buffer(bufferUVSdr.get() + uvStride * height / 2, width / 2, height / 2,
+ uvStride);
+ }
}
// dest
@@ -212,6 +227,8 @@ void UltraHdrEncFuzzer::process() {
std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
+ std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl;
+ std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl;
std::cout << "quality factor " << quality << std::endl;
#endif
@@ -226,8 +243,19 @@ void UltraHdrEncFuzzer::process() {
} else {
// compressed img
JpegEncoderHelper encoder;
- if (encoder.compressImage(yuv420Img.data, yuv420Img.width, yuv420Img.height, quality,
- nullptr, 0)) {
+ struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img;
+ if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width;
+ if (!yuv420ImgCopy.chroma_data) {
+ uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data);
+ yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height;
+ yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1;
+ }
+
+ if (encoder.compressImage(reinterpret_cast<uint8_t*>(yuv420ImgCopy.data),
+ reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data),
+ yuv420ImgCopy.width, yuv420ImgCopy.height,
+ yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride,
+ quality, nullptr, 0)) {
jpegImg.length = encoder.getCompressedImageSize();
jpegImg.maxLength = jpegImg.length;
jpegImg.data = encoder.getCompressedImagePtr();
@@ -242,14 +270,15 @@ void UltraHdrEncFuzzer::process() {
} else if (muxSwitch == 4) { // api 4
jpegImgR.length = 0;
JpegEncoderHelper gainMapEncoder;
- if (gainMapEncoder.compressImage(grayImg.data, grayImg.width, grayImg.height,
- quality, nullptr, 0, true)) {
+ if (gainMapEncoder.compressImage(reinterpret_cast<uint8_t*>(grayImg.data),
+ nullptr, grayImg.width, grayImg.height,
+ grayImg.width, 0, quality, nullptr, 0)) {
jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
jpegGainMap.maxLength = jpegImg.length;
jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
ultrahdr_metadata_struct metadata;
- metadata.version = "1.0";
+ metadata.version = kJpegrVersion;
if (tf == ULTRAHDR_TF_HLG) {
metadata.maxContentBoost = kHlgMaxNits / kSdrWhiteNits;
} else if (tf == ULTRAHDR_TF_PQ) {
@@ -274,7 +303,8 @@ void UltraHdrEncFuzzer::process() {
jpegr_info_struct info{0, 0, &iccData, &exifData};
status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
if (status == android::OK) {
- size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_SDR) ? 4 : 8);
+ size_t outSize =
+ info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
jpegr_uncompressed_struct decodedJpegR;
auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
decodedJpegR.data = decodedRaw.get();
diff --git a/libs/ultrahdr/gainmapmath.cpp b/libs/ultrahdr/gainmapmath.cpp
index ee15363b69..ae9c4ca338 100644
--- a/libs/ultrahdr/gainmapmath.cpp
+++ b/libs/ultrahdr/gainmapmath.cpp
@@ -168,7 +168,7 @@ Color srgbInvOetf(Color e_gamma) {
// See IEC 61966-2-1, Equations F.5 and F.6.
float srgbInvOetfLUT(float e_gamma) {
- uint32_t value = static_cast<uint32_t>(e_gamma * kSrgbInvOETFNumEntries);
+ uint32_t value = static_cast<uint32_t>(e_gamma * (kSrgbInvOETFNumEntries - 1) + 0.5);
//TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kSrgbInvOETFNumEntries - 1);
return kSrgbInvOETF[value];
@@ -288,7 +288,7 @@ Color hlgOetf(Color e) {
}
float hlgOetfLUT(float e) {
- uint32_t value = static_cast<uint32_t>(e * kHlgOETFNumEntries);
+ uint32_t value = static_cast<uint32_t>(e * (kHlgOETFNumEntries - 1) + 0.5);
//TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kHlgOETFNumEntries - 1);
@@ -315,7 +315,7 @@ Color hlgInvOetf(Color e_gamma) {
}
float hlgInvOetfLUT(float e_gamma) {
- uint32_t value = static_cast<uint32_t>(e_gamma * kHlgInvOETFNumEntries);
+ uint32_t value = static_cast<uint32_t>(e_gamma * (kHlgInvOETFNumEntries - 1) + 0.5);
//TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kHlgInvOETFNumEntries - 1);
@@ -344,7 +344,7 @@ Color pqOetf(Color e) {
}
float pqOetfLUT(float e) {
- uint32_t value = static_cast<uint32_t>(e * kPqOETFNumEntries);
+ uint32_t value = static_cast<uint32_t>(e * (kPqOETFNumEntries - 1) + 0.5);
//TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kPqOETFNumEntries - 1);
@@ -376,7 +376,7 @@ Color pqInvOetf(Color e_gamma) {
}
float pqInvOetfLUT(float e_gamma) {
- uint32_t value = static_cast<uint32_t>(e_gamma * kPqInvOETFNumEntries);
+ uint32_t value = static_cast<uint32_t>(e_gamma * (kPqInvOETFNumEntries - 1) + 0.5);
//TODO() : Remove once conversion modules have appropriate clamping in place
value = CLIP3(value, 0, kPqInvOETFNumEntries - 1);
@@ -531,29 +531,29 @@ void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma
Color new_uv = (yuv1 + yuv2 + yuv3 + yuv4) / 4.0f;
- size_t pixel_y1_idx = x_chroma * 2 + y_chroma * 2 * image->width;
- size_t pixel_y2_idx = (x_chroma * 2 + 1) + y_chroma * 2 * image->width;
- size_t pixel_y3_idx = x_chroma * 2 + (y_chroma * 2 + 1) * image->width;
- size_t pixel_y4_idx = (x_chroma * 2 + 1) + (y_chroma * 2 + 1) * image->width;
+ size_t pixel_y1_idx = x_chroma * 2 + y_chroma * 2 * image->luma_stride;
+ size_t pixel_y2_idx = (x_chroma * 2 + 1) + y_chroma * 2 * image->luma_stride;
+ size_t pixel_y3_idx = x_chroma * 2 + (y_chroma * 2 + 1) * image->luma_stride;
+ size_t pixel_y4_idx = (x_chroma * 2 + 1) + (y_chroma * 2 + 1) * image->luma_stride;
uint8_t& y1_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y1_idx];
uint8_t& y2_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y2_idx];
uint8_t& y3_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y3_idx];
uint8_t& y4_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y4_idx];
- size_t pixel_count = image->width * image->height;
- size_t pixel_uv_idx = x_chroma + y_chroma * (image->width / 2);
+ size_t pixel_count = image->chroma_stride * image->height / 2;
+ size_t pixel_uv_idx = x_chroma + y_chroma * (image->chroma_stride);
- uint8_t& u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx];
- uint8_t& v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx];
+ uint8_t& u_uint = reinterpret_cast<uint8_t*>(image->chroma_data)[pixel_uv_idx];
+ uint8_t& v_uint = reinterpret_cast<uint8_t*>(image->chroma_data)[pixel_count + pixel_uv_idx];
- y1_uint = static_cast<uint8_t>(floor(yuv1.y * 255.0f + 0.5f));
- y2_uint = static_cast<uint8_t>(floor(yuv2.y * 255.0f + 0.5f));
- y3_uint = static_cast<uint8_t>(floor(yuv3.y * 255.0f + 0.5f));
- y4_uint = static_cast<uint8_t>(floor(yuv4.y * 255.0f + 0.5f));
+ y1_uint = static_cast<uint8_t>(CLIP3((yuv1.y * 255.0f + 0.5f), 0, 255));
+ y2_uint = static_cast<uint8_t>(CLIP3((yuv2.y * 255.0f + 0.5f), 0, 255));
+ y3_uint = static_cast<uint8_t>(CLIP3((yuv3.y * 255.0f + 0.5f), 0, 255));
+ y4_uint = static_cast<uint8_t>(CLIP3((yuv4.y * 255.0f + 0.5f), 0, 255));
- u_uint = static_cast<uint8_t>(floor(new_uv.u * 255.0f + 128.0f + 0.5f));
- v_uint = static_cast<uint8_t>(floor(new_uv.v * 255.0f + 128.0f + 0.5f));
+ u_uint = static_cast<uint8_t>(CLIP3((new_uv.u * 255.0f + 128.0f + 0.5f), 0, 255));
+ v_uint = static_cast<uint8_t>(CLIP3((new_uv.v * 255.0f + 128.0f + 0.5f), 0, 255));
}
////////////////////////////////////////////////////////////////////////////////
@@ -598,14 +598,18 @@ Color applyGainLUT(Color e, float gain, GainLUT& gainLUT) {
}
Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
- size_t pixel_count = image->width * image->height;
+ uint8_t* luma_data = reinterpret_cast<uint8_t*>(image->data);
+ size_t luma_stride = image->luma_stride;
+ uint8_t* chroma_data = reinterpret_cast<uint8_t*>(image->chroma_data);
+ size_t chroma_stride = image->chroma_stride;
- size_t pixel_y_idx = x + y * image->width;
- size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2);
+ size_t offset_cr = chroma_stride * (image->height / 2);
+ size_t pixel_y_idx = x + y * luma_stride;
+ size_t pixel_chroma_idx = x / 2 + (y / 2) * chroma_stride;
- uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y_idx];
- uint8_t u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx];
- uint8_t v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx];
+ uint8_t y_uint = luma_data[pixel_y_idx];
+ uint8_t u_uint = chroma_data[pixel_chroma_idx];
+ uint8_t v_uint = chroma_data[offset_cr + pixel_chroma_idx];
// 128 bias for UV given we are using jpeglib; see:
// https://github.com/kornelski/libjpeg/blob/master/structure.doc
@@ -615,20 +619,10 @@ Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
}
Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
- size_t luma_stride = image->luma_stride;
- size_t chroma_stride = image->chroma_stride;
uint16_t* luma_data = reinterpret_cast<uint16_t*>(image->data);
+ size_t luma_stride = image->luma_stride == 0 ? image->width : image->luma_stride;
uint16_t* chroma_data = reinterpret_cast<uint16_t*>(image->chroma_data);
-
- if (luma_stride == 0) {
- luma_stride = image->width;
- }
- if (chroma_stride == 0) {
- chroma_stride = luma_stride;
- }
- if (chroma_data == nullptr) {
- chroma_data = &reinterpret_cast<uint16_t*>(image->data)[luma_stride * image->height];
- }
+ size_t chroma_stride = image->chroma_stride;
size_t pixel_y_idx = y * luma_stride + x;
size_t pixel_u_idx = (y >> 1) * chroma_stride + (x & ~0x1);
diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
index 50b4d2fab1..9f1238f718 100644
--- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h
+++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
@@ -172,7 +172,7 @@ struct GainLUT {
}
float getGainFactor(float gain) {
- uint32_t idx = static_cast<uint32_t>(gain * (kGainFactorNumEntries - 1));
+ uint32_t idx = static_cast<uint32_t>(gain * (kGainFactorNumEntries - 1) + 0.5);
//TODO() : Remove once conversion modules have appropriate clamping in place
idx = CLIP3(idx, 0, kGainFactorNumEntries - 1);
return mGainTable[idx];
diff --git a/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h
index 2c6778e299..9d06415cb3 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h
@@ -19,6 +19,7 @@
// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
#include <cstdio>
+#include <vector>
extern "C" {
#include <jerror.h>
@@ -26,10 +27,11 @@ extern "C" {
}
#include <utils/Errors.h>
-#include <vector>
namespace android::ultrahdr {
+#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m))
+
/*
* Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
* This class is not thread-safe.
@@ -46,8 +48,9 @@ public:
* ICC segment which will be added to the compressed image.
* Returns false if errors occur during compression.
*/
- bool compressImage(const void* image, int width, int height, int quality,
- const void* iccBuffer, unsigned int iccSize, bool isSingleChannel = false);
+ bool compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
+ int lumaStride, int chromaStride, int quality, const void* iccBuffer,
+ unsigned int iccSize);
/*
* Returns the compressed JPEG buffer pointer. This method must be called only after calling
@@ -66,6 +69,7 @@ public:
* We must pass at least 16 scanlines according to libjpeg documentation.
*/
static const int kCompressBatchSize = 16;
+
private:
// initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
// passed into jpeg library.
@@ -75,15 +79,16 @@ private:
static void outputErrorMessage(j_common_ptr cinfo);
// Returns false if errors occur.
- bool encode(const void* inYuv, int width, int height, int jpegQuality,
- const void* iccBuffer, unsigned int iccSize, bool isSingleChannel);
+ bool encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
+ int lumaStride, int chromaStride, int quality, const void* iccBuffer,
+ unsigned int iccSize);
void setJpegDestination(jpeg_compress_struct* cinfo);
void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo,
bool isSingleChannel);
// Returns false if errors occur.
- bool compress(jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel);
- bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv);
- bool compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image);
+ bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, const uint8_t* uvBuffer,
+ int lumaStride, int chromaStride);
+ bool compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, int lumaStride);
// The block size for encoded jpeg image buffer.
static const int kBlockSize = 16384;
diff --git a/libs/ultrahdr/include/ultrahdr/jpegr.h b/libs/ultrahdr/include/ultrahdr/jpegr.h
index f80496a758..850cb32e75 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegr.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegr.h
@@ -17,9 +17,13 @@
#ifndef ANDROID_ULTRAHDR_JPEGR_H
#define ANDROID_ULTRAHDR_JPEGR_H
-#include "jpegencoderhelper.h"
-#include "jpegrerrorcode.h"
-#include "ultrahdr.h"
+#include <cstdint>
+#include <vector>
+
+#include "ultrahdr/jpegdecoderhelper.h"
+#include "ultrahdr/jpegencoderhelper.h"
+#include "ultrahdr/jpegrerrorcode.h"
+#include "ultrahdr/ultrahdr.h"
#ifndef FLT_MAX
#define FLT_MAX 0x1.fffffep127f
@@ -27,6 +31,27 @@
namespace android::ultrahdr {
+// The current JPEGR version that we encode to
+static const char* const kJpegrVersion = "1.0";
+
+// Map is quarter res / sixteenth size
+static const size_t kMapDimensionScaleFactor = 4;
+
+// Gain Map width is (image_width / kMapDimensionScaleFactor). If we were to
+// compress 420 GainMap in jpeg, then we need at least 2 samples. For Grayscale
+// 1 sample is sufficient. We are using 2 here anyways
+static const int kMinWidth = 2 * kMapDimensionScaleFactor;
+static const int kMinHeight = 2 * kMapDimensionScaleFactor;
+
+// Minimum Codec Unit(MCU) for 420 sub-sampling is decided by JPEG encoder by parameter
+// JpegEncoderHelper::kCompressBatchSize.
+// The width and height of image under compression is expected to be a multiple of MCU size.
+// If this criteria is not satisfied, padding is done.
+static const size_t kJpegBlock = JpegEncoderHelper::kCompressBatchSize;
+
+/*
+ * Holds information of jpegr image
+ */
struct jpegr_info_struct {
size_t width;
size_t height;
@@ -49,16 +74,19 @@ struct jpegr_uncompressed_struct {
// Values below are optional
// Pointer to chroma data, if it's NULL, chroma plane is considered to be immediately
- // following after the luma plane.
- // Note: currently this feature is only supported for P010 image (HDR input).
+ // after the luma plane.
void* chroma_data = nullptr;
- // Strides of Y plane in number of pixels, using 0 to present uninitialized, must be
- // larger than or equal to luma width.
- // Note: currently this feature is only supported for P010 image (HDR input).
+ // Stride of Y plane in number of pixels. 0 indicates the member is uninitialized. If
+ // non-zero this value must be larger than or equal to luma width. If stride is
+ // uninitialized then it is assumed to be equal to luma width.
int luma_stride = 0;
- // Strides of UV plane in number of pixels, using 0 to present uninitialized, must be
- // larger than or equal to chroma width.
- // Note: currently this feature is only supported for P010 image (HDR input).
+ // Stride of UV plane in number of pixels.
+ // 1. If this handle points to P010 image then this value must be larger than
+ // or equal to luma width.
+ // 2. If this handle points to 420 image then this value must be larger than
+ // or equal to (luma width / 2).
+ // NOTE: if chroma_data is nullptr, chroma_stride is irrelevant. Just as the way,
+ // chroma_data is derived from luma ptr, chroma stride is derived from luma stride.
int chroma_stride = 0;
};
@@ -102,10 +130,10 @@ public:
* Tonemap the HDR input to a SDR image, generate gain map from the HDR and SDR images,
* compress SDR YUV to 8-bit JPEG and append the gain map to the end of the compressed
* JPEG.
- * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
* @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
+ * represents the maximum available size of the destination buffer, and it must be
* set before calling this method. If the encoded JPEGR size exceeds
* {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
* @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
@@ -113,11 +141,8 @@ public:
* @param exif pointer to the exif metadata.
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
- status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality,
- jr_exif_ptr exif);
+ status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
+ jr_compressed_ptr dest, int quality, jr_exif_ptr exif);
/*
* Encode API-1
@@ -126,8 +151,8 @@ public:
* Generate gain map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
* the gain map to the end of the compressed JPEG. HDR and SDR inputs must be the same
* resolution. SDR input is assumed to use the sRGB transfer function.
- * @param uncompressed_p010_image uncompressed HDR image in P010 color format
- * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
* @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
* represents the maximum available size of the desitination buffer, and it must be
@@ -138,11 +163,8 @@ public:
* @param exif pointer to the exif metadata.
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
- status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality,
+ status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_uncompressed_ptr yuv420_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest, int quality,
jr_exif_ptr exif);
/*
@@ -155,11 +177,11 @@ public:
* compressed JPEG. Adds an ICC profile if one isn't present in the input JPEG image. HDR and
* SDR inputs must be the same resolution and color space. SDR image is assumed to use the sRGB
* transfer function.
- * @param uncompressed_p010_image uncompressed HDR image in P010 color format
- * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
- * Note: the SDR image must be the decoded version of the JPEG
- * input
- * @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
+ * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
+ * Note: the compressed SDR image must be the compressed
+ * yuv420_image_ptr image in JPEG format.
* @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
* represents the maximum available size of the desitination buffer, and it must be
@@ -167,10 +189,8 @@ public:
* {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
- status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_compressed_ptr compressed_jpeg_image,
- ultrahdr_transfer_function hdr_tf,
+ status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_uncompressed_ptr yuv420_image_ptr,
+ jr_compressed_ptr yuv420jpg_image_ptr, ultrahdr_transfer_function hdr_tf,
jr_compressed_ptr dest);
/*
@@ -183,8 +203,8 @@ public:
* and the decoded SDR result, append the gain map to the end of the compressed JPEG. Adds an
* ICC profile if one isn't present in the input JPEG image. HDR and SDR inputs must be the same
* resolution. JPEG image is assumed to use the sRGB transfer function.
- * @param uncompressed_p010_image uncompressed HDR image in P010 color format
- * @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
* @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
* represents the maximum available size of the desitination buffer, and it must be
@@ -192,10 +212,8 @@ public:
* {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
- status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_compressed_ptr compressed_jpeg_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest);
+ status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_compressed_ptr yuv420jpg_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest);
/*
* Encode API-4
@@ -203,8 +221,8 @@ public:
*
* Assemble the primary JPEG image, the gain map and the metadata to JPEG/R format. Adds an ICC
* profile if one isn't present in the input JPEG image.
- * @param compressed_jpeg_image compressed 8-bit JPEG image
- * @param compressed_gainmap compressed 8-bit JPEG single channel image
+ * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
+ * @param gainmapjpg_image_ptr gain map image compressed in jpeg format
* @param metadata metadata to be written in XMP of the primary jpeg
* @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
* represents the maximum available size of the desitination buffer, and it must be
@@ -212,9 +230,8 @@ public:
* {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
- status_t encodeJPEGR(jr_compressed_ptr compressed_jpeg_image,
- jr_compressed_ptr compressed_gainmap,
- ultrahdr_metadata_ptr metadata,
+ status_t encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
+ jr_compressed_ptr gainmapjpg_image_ptr, ultrahdr_metadata_ptr metadata,
jr_compressed_ptr dest);
/*
@@ -227,8 +244,7 @@ public:
*
* This method only supports single gain map metadata values for fields that allow multi-channel
* metadata values.
- *
- * @param compressed_jpegr_image compressed JPEGR image.
+ * @param jpegr_image_ptr compressed JPEGR image.
* @param dest destination of the uncompressed JPEGR image.
* @param max_display_boost (optional) the maximum available boost supported by a display,
* the value must be greater than or equal to 1.0.
@@ -248,57 +264,55 @@ public:
----------------------------------------------------------------------
| JPEGR_OUTPUT_HDR_HLG | RGBA_1010102 HLG |
----------------------------------------------------------------------
- * @param gain_map destination of the decoded gain map. The default value is NULL where
- the decoder will do nothing about it. If configured not NULL the decoder
- will write the decoded gain_map data into this structure. The format
- is defined in {@code jpegr_uncompressed_struct}.
+ * @param gainmap_image_ptr destination of the decoded gain map. The default value is NULL
+ where the decoder will do nothing about it. If configured not NULL
+ the decoder will write the decoded gain_map data into this
+ structure. The format is defined in
+ {@code jpegr_uncompressed_struct}.
* @param metadata destination of the decoded metadata. The default value is NULL where the
decoder will do nothing about it. If configured not NULL the decoder will
write metadata into this structure. the format of metadata is defined in
{@code ultrahdr_metadata_struct}.
* @return NO_ERROR if decoding succeeds, error code if error occurs.
*/
- status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
- jr_uncompressed_ptr dest,
- float max_display_boost = FLT_MAX,
- jr_exif_ptr exif = nullptr,
+ status_t decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_ptr dest,
+ float max_display_boost = FLT_MAX, jr_exif_ptr exif = nullptr,
ultrahdr_output_format output_format = ULTRAHDR_OUTPUT_HDR_LINEAR,
- jr_uncompressed_ptr gain_map = nullptr,
+ jr_uncompressed_ptr gainmap_image_ptr = nullptr,
ultrahdr_metadata_ptr metadata = nullptr);
/*
- * Gets Info from JPEGR file without decoding it.
- *
- * This method only supports single gain map metadata values for fields that allow multi-channel
- * metadata values.
- *
- * The output is filled jpegr_info structure
- * @param compressed_jpegr_image compressed JPEGR image
- * @param jpegr_info pointer to output JPEGR info. Members of jpegr_info
- * are owned by the caller
- * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
- */
- status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
- jr_info_ptr jpegr_info);
+ * Gets Info from JPEGR file without decoding it.
+ *
+ * This method only supports single gain map metadata values for fields that allow multi-channel
+ * metadata values.
+ *
+ * The output is filled jpegr_info structure
+ * @param jpegr_image_ptr compressed JPEGR image
+ * @param jpeg_image_info_ptr pointer to jpegr info struct. Members of jpegr_info
+ * are owned by the caller
+ * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
+ */
+ status_t getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg_image_info_ptr);
+
protected:
/*
* This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
* 10-bit yuv images as input, and calculate the uncompressed gain map. The input images
* must be the same resolution. The SDR input is assumed to use the sRGB transfer function.
*
- * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
- * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
* @param hdr_tf transfer function of the HDR image
- * @param dest gain map; caller responsible for memory of data
- * @param metadata max_content_boost is filled in
+ * @param metadata everything but "version" is filled in this struct
+ * @param dest location at which gain map image is stored (caller responsible for memory
+ of data).
* @param sdr_is_601 if true, then use BT.601 decoding of YUV regardless of SDR image gamut
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- status_t generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_uncompressed_ptr uncompressed_p010_image,
- ultrahdr_transfer_function hdr_tf,
- ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr dest,
+ status_t generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
+ jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
+ ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest,
bool sdr_is_601 = false);
/*
@@ -309,8 +323,8 @@ protected:
* The SDR image is assumed to use the sRGB transfer function. The SDR image is also assumed to
* be a decoded JPEG for the purpose of YUV interpration.
*
- * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
- * @param uncompressed_gain_map uncompressed gain map
+ * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
+ * @param gainmap_image_ptr pointer to uncompressed gain map image struct.
* @param metadata JPEG/R metadata extracted from XMP.
* @param output_format flag for setting output color format. if set to
* {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
@@ -319,35 +333,33 @@ protected:
* @param dest reconstructed HDR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- status_t applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_uncompressed_ptr uncompressed_gain_map,
- ultrahdr_metadata_ptr metadata,
- ultrahdr_output_format output_format,
- float max_display_boost,
+ status_t applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
+ jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata,
+ ultrahdr_output_format output_format, float max_display_boost,
jr_uncompressed_ptr dest);
private:
/*
* This method is called in the encoding pipeline. It will encode the gain map.
*
- * @param uncompressed_gain_map uncompressed gain map
- * @param resource to compress gain map
+ * @param gainmap_image_ptr pointer to uncompressed gain map image struct
+ * @param jpeg_enc_obj_ptr helper resource to compress gain map
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
- status_t compressGainMap(jr_uncompressed_ptr uncompressed_gain_map,
- JpegEncoderHelper* jpeg_encoder);
+ status_t compressGainMap(jr_uncompressed_ptr gainmap_image_ptr,
+ JpegEncoderHelper* jpeg_enc_obj_ptr);
/*
- * This methoud is called to separate primary image and gain map image from JPEGR
+ * This method is called to separate primary image and gain map image from JPEGR
*
- * @param compressed_jpegr_image compressed JPEGR image
- * @param primary_image destination of primary image
- * @param gain_map destination of compressed gain map
+ * @param jpegr_image_ptr pointer to compressed JPEGR image.
+ * @param primary_jpg_image_ptr destination of primary image
+ * @param gainmap_jpg_image_ptr destination of compressed gain map image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr primary_image,
- jr_compressed_ptr gain_map);
+ */
+ status_t extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
+ jr_compressed_ptr primary_jpg_image_ptr,
+ jr_compressed_ptr gainmap_jpg_image_ptr);
/*
* This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
@@ -358,8 +370,9 @@ private:
* API-3 this parameter is null, but the primary image in JPEG/R may still have EXIF as long as
* the input JPEG has EXIF.
*
- * @param compressed_jpeg_image compressed 8-bit JPEG image
- * @param compress_gain_map compressed recover map
+
+ * @param primary_jpg_image_ptr destination of primary image
+ * @param gainmap_jpg_image_ptr destination of compressed gain map image
* @param (nullable) exif EXIF package
* @param (nullable) icc ICC package
* @param icc_size length in bytes of ICC package
@@ -367,22 +380,18 @@ private:
* @param dest compressed JPEGR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- status_t appendGainMap(jr_compressed_ptr compressed_jpeg_image,
- jr_compressed_ptr compressed_gain_map,
- jr_exif_ptr exif,
- void* icc, size_t icc_size,
- ultrahdr_metadata_ptr metadata,
- jr_compressed_ptr dest);
+ status_t appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
+ jr_compressed_ptr gainmap_jpg_image_ptr, jr_exif_ptr exif, void* icc,
+ size_t icc_size, ultrahdr_metadata_ptr metadata, jr_compressed_ptr dest);
/*
* This method will tone map a HDR image to an SDR image.
*
- * @param src (input) uncompressed P010 image
- * @param dest (output) tone mapping result as a YUV_420 image
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ * @param src pointer to uncompressed HDR image struct. HDR image is expected to be
+ * in p010 color format
+ * @param dest pointer to store tonemapped SDR image
*/
- status_t toneMap(jr_uncompressed_ptr src,
- jr_uncompressed_ptr dest);
+ status_t toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest);
/*
* This method will convert a YUV420 image from one YUV encoding to another in-place (eg.
@@ -396,15 +405,15 @@ private:
* @param dest_encoding output YUV encoding
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- status_t convertYuv(jr_uncompressed_ptr image,
- ultrahdr_color_gamut src_encoding,
+ status_t convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_encoding,
ultrahdr_color_gamut dest_encoding);
/*
* This method will check the validity of the input arguments.
*
- * @param uncompressed_p010_image uncompressed HDR image in P010 color format
- * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
+ * be in 420p color format
* @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
* represents the maximum available size of the desitination buffer, and it must be
@@ -412,32 +421,30 @@ private:
* {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
* @return NO_ERROR if the input args are valid, error code is not valid.
*/
- status_t areInputArgumentsValid(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest);
+ status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
+ jr_uncompressed_ptr yuv420_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest_ptr);
/*
* This method will check the validity of the input arguments.
*
- * @param uncompressed_p010_image uncompressed HDR image in P010 color format
- * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param p010_image_ptr uncompressed HDR image in P010 color format
+ * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
+ * be in 420p color format
* @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
+ * represents the maximum available size of the destination buffer, and it must be
* set before calling this method. If the encoded JPEGR size exceeds
* {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
* @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
* the highest quality
* @return NO_ERROR if the input args are valid, error code is not valid.
*/
- status_t areInputArgumentsValid(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality);
+ status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
+ jr_uncompressed_ptr yuv420_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest,
+ int quality);
};
-
} // namespace android::ultrahdr
#endif // ANDROID_ULTRAHDR_JPEGR_H
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
index 17cc97173c..66f70889fb 100644
--- a/libs/ultrahdr/include/ultrahdr/ultrahdr.h
+++ b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_ULTRAHDR_ULTRAHDR_H
#define ANDROID_ULTRAHDR_ULTRAHDR_H
+#include <string>
+
namespace android::ultrahdr {
// Color gamuts for image data
typedef enum {
diff --git a/libs/ultrahdr/jpegdecoderhelper.cpp b/libs/ultrahdr/jpegdecoderhelper.cpp
index fef544452a..33bf9ef3d4 100644
--- a/libs/ultrahdr/jpegdecoderhelper.cpp
+++ b/libs/ultrahdr/jpegdecoderhelper.cpp
@@ -227,10 +227,20 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
mHeight = cinfo.image_height;
if (decodeToRGBA) {
- if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
- // We don't intend to support decoding grayscale to RGBA
- status = false;
- ALOGE("%s: decoding grayscale to RGBA is unsupported", __func__);
+ // The primary image is expected to be yuv420 sampling
+ if (cinfo.jpeg_color_space != JCS_YCbCr) {
+ status = false;
+ ALOGE("%s: decodeToRGBA unexpected jpeg color space ", __func__);
+ goto CleanUp;
+ }
+ if (cinfo.comp_info[0].h_samp_factor != 2 ||
+ cinfo.comp_info[1].h_samp_factor != 1 ||
+ cinfo.comp_info[2].h_samp_factor != 1 ||
+ cinfo.comp_info[0].v_samp_factor != 2 ||
+ cinfo.comp_info[1].v_samp_factor != 1 ||
+ cinfo.comp_info[2].v_samp_factor != 1 ) {
+ status = false;
+ ALOGE("%s: decodeToRGBA unexpected primary image sub-sampling", __func__);
goto CleanUp;
}
// 4 bytes per pixel
@@ -251,12 +261,16 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
} else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
+ } else {
+ status = false;
+ ALOGE("%s: decodeToYUV unexpected jpeg color space", __func__);
+ goto CleanUp;
}
cinfo.out_color_space = cinfo.jpeg_color_space;
cinfo.raw_data_out = TRUE;
}
- cinfo.dct_method = JDCT_IFAST;
+ cinfo.dct_method = JDCT_ISLOW;
jpeg_start_decompress(&cinfo);
diff --git a/libs/ultrahdr/jpegencoderhelper.cpp b/libs/ultrahdr/jpegencoderhelper.cpp
index a03547b538..13ae7424d5 100644
--- a/libs/ultrahdr/jpegencoderhelper.cpp
+++ b/libs/ultrahdr/jpegencoderhelper.cpp
@@ -14,38 +14,35 @@
* limitations under the License.
*/
-#include <ultrahdr/jpegencoderhelper.h>
+#include <cstring>
+#include <memory>
+#include <vector>
+#include <ultrahdr/jpegencoderhelper.h>
#include <utils/Log.h>
-#include <errno.h>
-
namespace android::ultrahdr {
-#define ALIGNM(x, m) ((((x) + ((m) - 1)) / (m)) * (m))
-
// The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
struct destination_mgr {
-public:
struct jpeg_destination_mgr mgr;
JpegEncoderHelper* encoder;
};
-JpegEncoderHelper::JpegEncoderHelper() {
-}
+JpegEncoderHelper::JpegEncoderHelper() {}
-JpegEncoderHelper::~JpegEncoderHelper() {
-}
+JpegEncoderHelper::~JpegEncoderHelper() {}
-bool JpegEncoderHelper::compressImage(const void* image, int width, int height, int quality,
- const void* iccBuffer, unsigned int iccSize,
- bool isSingleChannel) {
+bool JpegEncoderHelper::compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
+ int height, int lumaStride, int chromaStride, int quality,
+ const void* iccBuffer, unsigned int iccSize) {
mResultBuffer.clear();
- if (!encode(image, width, height, quality, iccBuffer, iccSize, isSingleChannel)) {
+ if (!encode(yBuffer, uvBuffer, width, height, lumaStride, chromaStride, quality, iccBuffer,
+ iccSize)) {
return false;
}
- ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes",
- (width * height * 12) / 8, width, height, mResultBuffer.size());
+ ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes", (width * height * 12) / 8, width, height,
+ mResultBuffer.size());
return true;
}
@@ -85,29 +82,28 @@ void JpegEncoderHelper::outputErrorMessage(j_common_ptr cinfo) {
char buffer[JMSG_LENGTH_MAX];
/* Create the message */
- (*cinfo->err->format_message) (cinfo, buffer);
+ (*cinfo->err->format_message)(cinfo, buffer);
ALOGE("%s\n", buffer);
}
-bool JpegEncoderHelper::encode(const void* image, int width, int height, int jpegQuality,
- const void* iccBuffer, unsigned int iccSize, bool isSingleChannel) {
+bool JpegEncoderHelper::encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
+ int height, int lumaStride, int chromaStride, int quality,
+ const void* iccBuffer, unsigned int iccSize) {
jpeg_compress_struct cinfo;
jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
- // Override output_message() to print error log with ALOGE().
cinfo.err->output_message = &outputErrorMessage;
jpeg_create_compress(&cinfo);
setJpegDestination(&cinfo);
-
- setJpegCompressStruct(width, height, jpegQuality, &cinfo, isSingleChannel);
+ setJpegCompressStruct(width, height, quality, &cinfo, uvBuffer == nullptr);
jpeg_start_compress(&cinfo, TRUE);
-
if (iccBuffer != nullptr && iccSize > 0) {
jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
}
-
- bool status = compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel);
+ bool status = cinfo.num_components == 1
+ ? compressY(&cinfo, yBuffer, lumaStride)
+ : compressYuv(&cinfo, yBuffer, uvBuffer, lumaStride, chromaStride);
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
@@ -115,8 +111,9 @@ bool JpegEncoderHelper::encode(const void* image, int width, int height, int jpe
}
void JpegEncoderHelper::setJpegDestination(jpeg_compress_struct* cinfo) {
- destination_mgr* dest = static_cast<struct destination_mgr *>((*cinfo->mem->alloc_small) (
- (j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(destination_mgr)));
+ destination_mgr* dest = static_cast<struct destination_mgr*>(
+ (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT,
+ sizeof(destination_mgr)));
dest->encoder = this;
dest->mgr.init_destination = &initDestination;
dest->mgr.empty_output_buffer = &emptyOutputBuffer;
@@ -125,59 +122,40 @@ void JpegEncoderHelper::setJpegDestination(jpeg_compress_struct* cinfo) {
}
void JpegEncoderHelper::setJpegCompressStruct(int width, int height, int quality,
- jpeg_compress_struct* cinfo, bool isSingleChannel) {
+ jpeg_compress_struct* cinfo, bool isSingleChannel) {
cinfo->image_width = width;
cinfo->image_height = height;
- if (isSingleChannel) {
- cinfo->input_components = 1;
- cinfo->in_color_space = JCS_GRAYSCALE;
- } else {
- cinfo->input_components = 3;
- cinfo->in_color_space = JCS_YCbCr;
- }
+ cinfo->input_components = isSingleChannel ? 1 : 3;
+ cinfo->in_color_space = isSingleChannel ? JCS_GRAYSCALE : JCS_YCbCr;
jpeg_set_defaults(cinfo);
-
jpeg_set_quality(cinfo, quality, TRUE);
- jpeg_set_colorspace(cinfo, isSingleChannel ? JCS_GRAYSCALE : JCS_YCbCr);
cinfo->raw_data_in = TRUE;
- cinfo->dct_method = JDCT_IFAST;
-
- if (!isSingleChannel) {
- // Configure sampling factors. The sampling factor is JPEG subsampling 420 because the
- // source format is YUV420.
- cinfo->comp_info[0].h_samp_factor = 2;
- cinfo->comp_info[0].v_samp_factor = 2;
- cinfo->comp_info[1].h_samp_factor = 1;
- cinfo->comp_info[1].v_samp_factor = 1;
- cinfo->comp_info[2].h_samp_factor = 1;
- cinfo->comp_info[2].v_samp_factor = 1;
- }
-}
-
-bool JpegEncoderHelper::compress(
- jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel) {
- if (isSingleChannel) {
- return compressSingleChannel(cinfo, image);
+ cinfo->dct_method = JDCT_ISLOW;
+ cinfo->comp_info[0].h_samp_factor = cinfo->in_color_space == JCS_GRAYSCALE ? 1 : 2;
+ cinfo->comp_info[0].v_samp_factor = cinfo->in_color_space == JCS_GRAYSCALE ? 1 : 2;
+ for (int i = 1; i < cinfo->num_components; i++) {
+ cinfo->comp_info[i].h_samp_factor = 1;
+ cinfo->comp_info[i].v_samp_factor = 1;
}
- return compressYuv(cinfo, image);
}
-bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv) {
+bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
+ const uint8_t* uvBuffer, int lumaStride, int chromaStride) {
JSAMPROW y[kCompressBatchSize];
JSAMPROW cb[kCompressBatchSize / 2];
JSAMPROW cr[kCompressBatchSize / 2];
- JSAMPARRAY planes[3] {y, cb, cr};
+ JSAMPARRAY planes[3]{y, cb, cr};
- size_t y_plane_size = cinfo->image_width * cinfo->image_height;
- size_t uv_plane_size = y_plane_size / 4;
- uint8_t* y_plane = const_cast<uint8_t*>(yuv);
- uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size);
- uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size);
+ size_t y_plane_size = lumaStride * cinfo->image_height;
+ size_t u_plane_size = chromaStride * cinfo->image_height / 2;
+ uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
+ uint8_t* u_plane = const_cast<uint8_t*>(uvBuffer);
+ uint8_t* v_plane = const_cast<uint8_t*>(u_plane + u_plane_size);
std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
memset(empty.get(), 0, cinfo->image_width);
const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- const bool is_width_aligned = (aligned_width == cinfo->image_width);
+ const bool need_padding = (lumaStride < aligned_width);
std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
uint8_t* y_plane_intrm = nullptr;
uint8_t* u_plane_intrm = nullptr;
@@ -186,7 +164,7 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t*
JSAMPROW cb_intrm[kCompressBatchSize / 2];
JSAMPROW cr_intrm[kCompressBatchSize / 2];
JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
- if (!is_width_aligned) {
+ if (need_padding) {
size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
y_plane_intrm = buffer_intrm.get();
@@ -211,11 +189,11 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t*
for (int i = 0; i < kCompressBatchSize; ++i) {
size_t scanline = cinfo->next_scanline + i;
if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * cinfo->image_width;
+ y[i] = y_plane + scanline * lumaStride;
} else {
y[i] = empty.get();
}
- if (!is_width_aligned) {
+ if (need_padding) {
memcpy(y_intrm[i], y[i], cinfo->image_width);
}
}
@@ -223,18 +201,18 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t*
for (int i = 0; i < kCompressBatchSize / 2; ++i) {
size_t scanline = cinfo->next_scanline / 2 + i;
if (scanline < cinfo->image_height / 2) {
- int offset = scanline * (cinfo->image_width / 2);
+ int offset = scanline * chromaStride;
cb[i] = u_plane + offset;
cr[i] = v_plane + offset;
} else {
cb[i] = cr[i] = empty.get();
}
- if (!is_width_aligned) {
+ if (need_padding) {
memcpy(cb_intrm[i], cb[i], cinfo->image_width / 2);
memcpy(cr_intrm[i], cr[i], cinfo->image_width / 2);
}
}
- int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
+ int processed = jpeg_write_raw_data(cinfo, need_padding ? planes_intrm : planes,
kCompressBatchSize);
if (processed != kCompressBatchSize) {
ALOGE("Number of processed lines does not equal input lines.");
@@ -244,22 +222,23 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t*
return true;
}
-bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image) {
+bool JpegEncoderHelper::compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
+ int lumaStride) {
JSAMPROW y[kCompressBatchSize];
- JSAMPARRAY planes[1] {y};
+ JSAMPARRAY planes[1]{y};
- uint8_t* y_plane = const_cast<uint8_t*>(image);
+ uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
memset(empty.get(), 0, cinfo->image_width);
const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- bool is_width_aligned = (aligned_width == cinfo->image_width);
+ const bool need_padding = (lumaStride < aligned_width);
std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
uint8_t* y_plane_intrm = nullptr;
uint8_t* u_plane_intrm = nullptr;
JSAMPROW y_intrm[kCompressBatchSize];
JSAMPARRAY planes_intrm[]{y_intrm};
- if (!is_width_aligned) {
+ if (need_padding) {
size_t mcu_row_size = aligned_width * kCompressBatchSize;
buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
y_plane_intrm = buffer_intrm.get();
@@ -273,15 +252,15 @@ bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const
for (int i = 0; i < kCompressBatchSize; ++i) {
size_t scanline = cinfo->next_scanline + i;
if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * cinfo->image_width;
+ y[i] = y_plane + scanline * lumaStride;
} else {
y[i] = empty.get();
}
- if (!is_width_aligned) {
+ if (need_padding) {
memcpy(y_intrm[i], y[i], cinfo->image_width);
}
}
- int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
+ int processed = jpeg_write_raw_data(cinfo, need_padding ? planes_intrm : planes,
kCompressBatchSize);
if (processed != kCompressBatchSize / 2) {
ALOGE("Number of processed lines does not equal input lines.");
@@ -291,4 +270,4 @@ bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const
return true;
}
-} // namespace ultrahdr
+} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index 5a601bd6b9..ae8184547a 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -14,31 +14,26 @@
* limitations under the License.
*/
-#include <ultrahdr/jpegr.h>
-#include <ultrahdr/jpegencoderhelper.h>
-#include <ultrahdr/jpegdecoderhelper.h>
+#include <cmath>
+#include <condition_variable>
+#include <deque>
+#include <memory>
+#include <mutex>
+#include <thread>
+
#include <ultrahdr/gainmapmath.h>
+#include <ultrahdr/icc.h>
+#include <ultrahdr/jpegr.h>
#include <ultrahdr/jpegrutils.h>
#include <ultrahdr/multipictureformat.h>
-#include <ultrahdr/icc.h>
-#include <image_io/jpeg/jpeg_marker.h>
+#include <image_io/base/data_segment_data_source.h>
#include <image_io/jpeg/jpeg_info.h>
-#include <image_io/jpeg/jpeg_scanner.h>
#include <image_io/jpeg/jpeg_info_builder.h>
-#include <image_io/base/data_segment_data_source.h>
-#include <utils/Log.h>
+#include <image_io/jpeg/jpeg_marker.h>
+#include <image_io/jpeg/jpeg_scanner.h>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <cmath>
-#include <condition_variable>
-#include <deque>
-#include <mutex>
-#include <thread>
-#include <unistd.h>
+#include <utils/Log.h>
using namespace std;
using namespace photos_editing_formats::image_io;
@@ -60,25 +55,6 @@ namespace android::ultrahdr {
} \
}
-// The current JPEGR version that we encode to
-static const char* const kJpegrVersion = "1.0";
-
-// Map is quarter res / sixteenth size
-static const size_t kMapDimensionScaleFactor = 4;
-
-// Gain Map width is (image_width / kMapDimensionScaleFactor). If we were to
-// compress 420 GainMap in jpeg, then we need at least 2 samples. For Grayscale
-// 1 sample is sufficient. We are using 2 here anyways
-static const int kMinWidth = 2 * kMapDimensionScaleFactor;
-static const int kMinHeight = 2 * kMapDimensionScaleFactor;
-
-// JPEG block size.
-// JPEG encoding / decoding will require block based DCT transform 16 x 16 for luma,
-// and 8 x 8 for chroma.
-// Width must be 16 dividable for luma, and 8 dividable for chroma.
-// If this criteria is not facilitated, we will pad zeros based to each line on the
-// required block size.
-static const size_t kJpegBlock = JpegEncoderHelper::kCompressBatchSize;
// JPEG compress quality (0 ~ 100) for gain map
static const int kMapCompressQuality = 85;
@@ -96,184 +72,176 @@ int GetCPUCoreCount() {
return cpuCoreCount;
}
-status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
+status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
+ jr_uncompressed_ptr yuv420_image_ptr,
ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest) {
- if (uncompressed_p010_image == nullptr || uncompressed_p010_image->data == nullptr) {
- ALOGE("received nullptr for uncompressed p010 image");
+ jr_compressed_ptr dest_ptr) {
+ if (p010_image_ptr == nullptr || p010_image_ptr->data == nullptr) {
+ ALOGE("Received nullptr for input p010 image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (uncompressed_p010_image->width % 2 != 0
- || uncompressed_p010_image->height % 2 != 0) {
- ALOGE("Image dimensions cannot be odd, image dimensions %dx%d",
- uncompressed_p010_image->width, uncompressed_p010_image->height);
+ if (p010_image_ptr->width % 2 != 0 || p010_image_ptr->height % 2 != 0) {
+ ALOGE("Image dimensions cannot be odd, image dimensions %dx%d", p010_image_ptr->width,
+ p010_image_ptr->height);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (uncompressed_p010_image->width < kMinWidth
- || uncompressed_p010_image->height < kMinHeight) {
- ALOGE("Image dimensions cannot be less than %dx%d, image dimensions %dx%d",
- kMinWidth, kMinHeight, uncompressed_p010_image->width, uncompressed_p010_image->height);
+ if (p010_image_ptr->width < kMinWidth || p010_image_ptr->height < kMinHeight) {
+ ALOGE("Image dimensions cannot be less than %dx%d, image dimensions %dx%d", kMinWidth,
+ kMinHeight, p010_image_ptr->width, p010_image_ptr->height);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (uncompressed_p010_image->width > kMaxWidth
- || uncompressed_p010_image->height > kMaxHeight) {
- ALOGE("Image dimensions cannot be larger than %dx%d, image dimensions %dx%d",
- kMaxWidth, kMaxHeight, uncompressed_p010_image->width, uncompressed_p010_image->height);
+ if (p010_image_ptr->width > kMaxWidth || p010_image_ptr->height > kMaxHeight) {
+ ALOGE("Image dimensions cannot be larger than %dx%d, image dimensions %dx%d", kMaxWidth,
+ kMaxHeight, p010_image_ptr->width, p010_image_ptr->height);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (uncompressed_p010_image->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED
- || uncompressed_p010_image->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
- ALOGE("Unrecognized p010 color gamut %d", uncompressed_p010_image->colorGamut);
+ if (p010_image_ptr->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
+ p010_image_ptr->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
+ ALOGE("Unrecognized p010 color gamut %d", p010_image_ptr->colorGamut);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (uncompressed_p010_image->luma_stride != 0
- && uncompressed_p010_image->luma_stride < uncompressed_p010_image->width) {
- ALOGE("Luma stride can not be smaller than width, stride=%d, width=%d",
- uncompressed_p010_image->luma_stride, uncompressed_p010_image->width);
+ if (p010_image_ptr->luma_stride != 0 && p010_image_ptr->luma_stride < p010_image_ptr->width) {
+ ALOGE("Luma stride must not be smaller than width, stride=%d, width=%d",
+ p010_image_ptr->luma_stride, p010_image_ptr->width);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (uncompressed_p010_image->chroma_data != nullptr
- && uncompressed_p010_image->chroma_stride < uncompressed_p010_image->width) {
- ALOGE("Chroma stride can not be smaller than width, stride=%d, width=%d",
- uncompressed_p010_image->chroma_stride,
- uncompressed_p010_image->width);
+ if (p010_image_ptr->chroma_data != nullptr &&
+ p010_image_ptr->chroma_stride < p010_image_ptr->width) {
+ ALOGE("Chroma stride must not be smaller than width, stride=%d, width=%d",
+ p010_image_ptr->chroma_stride, p010_image_ptr->width);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (dest == nullptr || dest->data == nullptr) {
- ALOGE("received nullptr for destination");
+ if (dest_ptr == nullptr || dest_ptr->data == nullptr) {
+ ALOGE("Received nullptr for destination");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX
- || hdr_tf == ULTRAHDR_TF_SRGB) {
+ if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX || hdr_tf == ULTRAHDR_TF_SRGB) {
ALOGE("Invalid hdr transfer function %d", hdr_tf);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (uncompressed_yuv_420_image == nullptr) {
+ if (yuv420_image_ptr == nullptr) {
return NO_ERROR;
}
-
- if (uncompressed_yuv_420_image->data == nullptr) {
- ALOGE("received nullptr for uncompressed 420 image");
+ if (yuv420_image_ptr->data == nullptr) {
+ ALOGE("Received nullptr for uncompressed 420 image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (uncompressed_yuv_420_image->luma_stride != 0) {
- ALOGE("Stride is not supported for YUV420 image");
- return ERROR_JPEGR_UNSUPPORTED_FEATURE;
+ if (yuv420_image_ptr->luma_stride != 0 &&
+ yuv420_image_ptr->luma_stride < yuv420_image_ptr->width) {
+ ALOGE("Luma stride must not be smaller than width, stride=%d, width=%d",
+ yuv420_image_ptr->luma_stride, yuv420_image_ptr->width);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (uncompressed_yuv_420_image->chroma_data != nullptr) {
- ALOGE("Pointer to chroma plane is not supported for YUV420 image, chroma data must"
- "be immediately after the luma data.");
- return ERROR_JPEGR_UNSUPPORTED_FEATURE;
+ if (yuv420_image_ptr->chroma_data != nullptr &&
+ yuv420_image_ptr->chroma_stride < yuv420_image_ptr->width / 2) {
+ ALOGE("Chroma stride must not be smaller than (width / 2), stride=%d, width=%d",
+ yuv420_image_ptr->chroma_stride, yuv420_image_ptr->width);
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
- || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
- ALOGE("Image resolutions mismatch: P010: %dx%d, YUV420: %dx%d",
- uncompressed_p010_image->width,
- uncompressed_p010_image->height,
- uncompressed_yuv_420_image->width,
- uncompressed_yuv_420_image->height);
+ if (p010_image_ptr->width != yuv420_image_ptr->width ||
+ p010_image_ptr->height != yuv420_image_ptr->height) {
+ ALOGE("Image resolutions mismatch: P010: %dx%d, YUV420: %dx%d", p010_image_ptr->width,
+ p010_image_ptr->height, yuv420_image_ptr->width, yuv420_image_ptr->height);
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
-
- if (uncompressed_yuv_420_image->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED
- || uncompressed_yuv_420_image->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
- ALOGE("Unrecognized 420 color gamut %d", uncompressed_yuv_420_image->colorGamut);
+ if (yuv420_image_ptr->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
+ yuv420_image_ptr->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
+ ALOGE("Unrecognized 420 color gamut %d", yuv420_image_ptr->colorGamut);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
return NO_ERROR;
}
-status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
+status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
+ jr_uncompressed_ptr yuv420_image_ptr,
ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality) {
- if (status_t ret = areInputArgumentsValid(
- uncompressed_p010_image, uncompressed_yuv_420_image, hdr_tf, dest) != NO_ERROR) {
- return ret;
- }
-
+ jr_compressed_ptr dest_ptr, int quality) {
if (quality < 0 || quality > 100) {
ALOGE("quality factor is out side range [0-100], quality factor : %d", quality);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
- return NO_ERROR;
+ return areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest_ptr);
}
/* Encode API-0 */
-status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality,
- jr_exif_ptr exif) {
- if (status_t ret = areInputArgumentsValid(
- uncompressed_p010_image, /* uncompressed_yuv_420_image */ nullptr,
- hdr_tf, dest, quality) != NO_ERROR) {
+status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
+ jr_compressed_ptr dest, int quality, jr_exif_ptr exif) {
+ // validate input arguments
+ if (auto ret = areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest, quality);
+ ret != NO_ERROR) {
return ret;
}
-
if (exif != nullptr && exif->data == nullptr) {
ALOGE("received nullptr for exif metadata");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- ultrahdr_metadata_struct metadata;
- metadata.version = kJpegrVersion;
-
- jpegr_uncompressed_struct uncompressed_yuv_420_image;
- unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
- uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
- uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
- JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
-
- jpegr_uncompressed_struct map;
- JPEGR_CHECK(generateGainMap(
- &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
+ // clean up input structure for later usage
+ jpegr_uncompressed_struct p010_image = *p010_image_ptr;
+ if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
+ if (!p010_image.chroma_data) {
+ uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
+ p010_image.chroma_data = data + p010_image.luma_stride * p010_image.height;
+ p010_image.chroma_stride = p010_image.luma_stride;
+ }
+
+ const int yu420_luma_stride = ALIGNM(p010_image.width, kJpegBlock);
+ unique_ptr<uint8_t[]> yuv420_image_data =
+ make_unique<uint8_t[]>(yu420_luma_stride * p010_image.height * 3 / 2);
+ jpegr_uncompressed_struct yuv420_image = {.data = yuv420_image_data.get(),
+ .width = p010_image.width,
+ .height = p010_image.height,
+ .colorGamut = p010_image.colorGamut,
+ .luma_stride = yu420_luma_stride,
+ .chroma_data = nullptr,
+ .chroma_stride = yu420_luma_stride >> 1};
+ uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
+ yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height;
+
+ // tone map
+ JPEGR_CHECK(toneMap(&p010_image, &yuv420_image));
+
+ // gain map
+ ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
+ jpegr_uncompressed_struct gainmap_image;
+ JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(map.data));
-
- JpegEncoderHelper jpeg_encoder_gainmap;
- JPEGR_CHECK(compressGainMap(&map, &jpeg_encoder_gainmap));
- jpegr_compressed_struct compressed_map;
- compressed_map.maxLength = jpeg_encoder_gainmap.getCompressedImageSize();
- compressed_map.length = compressed_map.maxLength;
- compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
- compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
-
- sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
- uncompressed_yuv_420_image.colorGamut);
-
- // Convert to Bt601 YUV encoding for JPEG encode
- JPEGR_CHECK(convertYuv(&uncompressed_yuv_420_image, uncompressed_yuv_420_image.colorGamut,
- ULTRAHDR_COLORGAMUT_P3));
-
- JpegEncoderHelper jpeg_encoder;
- if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
- uncompressed_yuv_420_image.width,
- uncompressed_yuv_420_image.height, quality,
- icc->getData(), icc->getLength())) {
+ map_data.reset(reinterpret_cast<uint8_t*>(gainmap_image.data));
+
+ // compress gain map
+ JpegEncoderHelper jpeg_enc_obj_gm;
+ JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
+ jpegr_compressed_struct compressed_map = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
+ .length = static_cast<int>(
+ jpeg_enc_obj_gm.getCompressedImageSize()),
+ .maxLength = static_cast<int>(
+ jpeg_enc_obj_gm.getCompressedImageSize()),
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+
+ sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420_image.colorGamut);
+
+ // convert to Bt601 YUV encoding for JPEG encode
+ if (yuv420_image.colorGamut != ULTRAHDR_COLORGAMUT_P3) {
+ JPEGR_CHECK(convertYuv(&yuv420_image, yuv420_image.colorGamut, ULTRAHDR_COLORGAMUT_P3));
+ }
+
+ // compress 420 image
+ JpegEncoderHelper jpeg_enc_obj_yuv420;
+ if (!jpeg_enc_obj_yuv420.compressImage(reinterpret_cast<uint8_t*>(yuv420_image.data),
+ reinterpret_cast<uint8_t*>(yuv420_image.chroma_data),
+ yuv420_image.width, yuv420_image.height,
+ yuv420_image.luma_stride, yuv420_image.chroma_stride,
+ quality, icc->getData(), icc->getLength())) {
return ERROR_JPEGR_ENCODE_ERROR;
}
- jpegr_compressed_struct jpeg;
- jpeg.data = jpeg_encoder.getCompressedImagePtr();
- jpeg.length = jpeg_encoder.getCompressedImageSize();
+ jpegr_compressed_struct jpeg = {.data = jpeg_enc_obj_yuv420.getCompressedImagePtr(),
+ .length = static_cast<int>(
+ jpeg_enc_obj_yuv420.getCompressedImageSize()),
+ .maxLength = static_cast<int>(
+ jpeg_enc_obj_yuv420.getCompressedImageSize()),
+ .colorGamut = yuv420_image.colorGamut};
- // No ICC since JPEG encode already did it
+ // append gain map, no ICC since JPEG encode already did it
JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
&metadata, dest));
@@ -281,226 +249,277 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
}
/* Encode API-1 */
-status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality,
- jr_exif_ptr exif) {
- if (uncompressed_yuv_420_image == nullptr) {
+status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
+ jr_uncompressed_ptr yuv420_image_ptr, ultrahdr_transfer_function hdr_tf,
+ jr_compressed_ptr dest, int quality, jr_exif_ptr exif) {
+ // validate input arguments
+ if (yuv420_image_ptr == nullptr) {
ALOGE("received nullptr for uncompressed 420 image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
if (exif != nullptr && exif->data == nullptr) {
ALOGE("received nullptr for exif metadata");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (status_t ret = areInputArgumentsValid(
- uncompressed_p010_image, uncompressed_yuv_420_image, hdr_tf,
- dest, quality) != NO_ERROR) {
+ if (auto ret = areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest, quality);
+ ret != NO_ERROR) {
return ret;
}
- ultrahdr_metadata_struct metadata;
- metadata.version = kJpegrVersion;
+ // clean up input structure for later usage
+ jpegr_uncompressed_struct p010_image = *p010_image_ptr;
+ if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
+ if (!p010_image.chroma_data) {
+ uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
+ p010_image.chroma_data = data + p010_image.luma_stride * p010_image.height;
+ p010_image.chroma_stride = p010_image.luma_stride;
+ }
+ jpegr_uncompressed_struct yuv420_image = *yuv420_image_ptr;
+ if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width;
+ if (!yuv420_image.chroma_data) {
+ uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
+ yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height;
+ yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1;
+ }
- jpegr_uncompressed_struct map;
- JPEGR_CHECK(generateGainMap(
- uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
+ // gain map
+ ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
+ jpegr_uncompressed_struct gainmap_image;
+ JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(map.data));
-
- JpegEncoderHelper jpeg_encoder_gainmap;
- JPEGR_CHECK(compressGainMap(&map, &jpeg_encoder_gainmap));
- jpegr_compressed_struct compressed_map;
- compressed_map.maxLength = jpeg_encoder_gainmap.getCompressedImageSize();
- compressed_map.length = compressed_map.maxLength;
- compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
- compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
-
- sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
- uncompressed_yuv_420_image->colorGamut);
-
- // Convert to Bt601 YUV encoding for JPEG encode; make a copy so as to no clobber client data
- unique_ptr<uint8_t[]> yuv_420_bt601_data = make_unique<uint8_t[]>(
- uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 3 / 2);
- memcpy(yuv_420_bt601_data.get(), uncompressed_yuv_420_image->data,
- uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 3 / 2);
-
- jpegr_uncompressed_struct yuv_420_bt601_image = {
- yuv_420_bt601_data.get(), uncompressed_yuv_420_image->width, uncompressed_yuv_420_image->height,
- uncompressed_yuv_420_image->colorGamut };
- JPEGR_CHECK(convertYuv(&yuv_420_bt601_image, yuv_420_bt601_image.colorGamut,
- ULTRAHDR_COLORGAMUT_P3));
-
- JpegEncoderHelper jpeg_encoder;
- if (!jpeg_encoder.compressImage(yuv_420_bt601_image.data,
- yuv_420_bt601_image.width,
- yuv_420_bt601_image.height, quality,
- icc->getData(), icc->getLength())) {
+ map_data.reset(reinterpret_cast<uint8_t*>(gainmap_image.data));
+
+ // compress gain map
+ JpegEncoderHelper jpeg_enc_obj_gm;
+ JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
+ jpegr_compressed_struct compressed_map = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
+ .length = static_cast<int>(
+ jpeg_enc_obj_gm.getCompressedImageSize()),
+ .maxLength = static_cast<int>(
+ jpeg_enc_obj_gm.getCompressedImageSize()),
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+
+ sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420_image.colorGamut);
+
+ jpegr_uncompressed_struct yuv420_bt601_image = yuv420_image;
+ unique_ptr<uint8_t[]> yuv_420_bt601_data;
+ // Convert to bt601 YUV encoding for JPEG encode
+ if (yuv420_image.colorGamut != ULTRAHDR_COLORGAMUT_P3) {
+ const int yuv_420_bt601_luma_stride = ALIGNM(yuv420_image.width, kJpegBlock);
+ yuv_420_bt601_data =
+ make_unique<uint8_t[]>(yuv_420_bt601_luma_stride * yuv420_image.height * 3 / 2);
+ yuv420_bt601_image.data = yuv_420_bt601_data.get();
+ yuv420_bt601_image.colorGamut = yuv420_image.colorGamut;
+ yuv420_bt601_image.luma_stride = yuv_420_bt601_luma_stride;
+ uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_bt601_image.data);
+ yuv420_bt601_image.chroma_data = data + yuv_420_bt601_luma_stride * yuv420_image.height;
+ yuv420_bt601_image.chroma_stride = yuv_420_bt601_luma_stride >> 1;
+
+ {
+ // copy luma
+ uint8_t* y_dst = reinterpret_cast<uint8_t*>(yuv420_bt601_image.data);
+ uint8_t* y_src = reinterpret_cast<uint8_t*>(yuv420_image.data);
+ if (yuv420_bt601_image.luma_stride == yuv420_image.luma_stride) {
+ memcpy(y_dst, y_src, yuv420_bt601_image.luma_stride * yuv420_image.height);
+ } else {
+ for (size_t i = 0; i < yuv420_image.height; i++) {
+ memcpy(y_dst, y_src, yuv420_image.width);
+ if (yuv420_image.width != yuv420_bt601_image.luma_stride) {
+ memset(y_dst + yuv420_image.width, 0,
+ yuv420_bt601_image.luma_stride - yuv420_image.width);
+ }
+ y_dst += yuv420_bt601_image.luma_stride;
+ y_src += yuv420_image.luma_stride;
+ }
+ }
+ }
+
+ if (yuv420_bt601_image.chroma_stride == yuv420_image.chroma_stride) {
+ // copy luma
+ uint8_t* ch_dst = reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data);
+ uint8_t* ch_src = reinterpret_cast<uint8_t*>(yuv420_image.chroma_data);
+ memcpy(ch_dst, ch_src, yuv420_bt601_image.chroma_stride * yuv420_image.height);
+ } else {
+ // copy cb & cr
+ uint8_t* cb_dst = reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data);
+ uint8_t* cb_src = reinterpret_cast<uint8_t*>(yuv420_image.chroma_data);
+ uint8_t* cr_dst = cb_dst + (yuv420_bt601_image.chroma_stride * yuv420_bt601_image.height / 2);
+ uint8_t* cr_src = cb_src + (yuv420_image.chroma_stride * yuv420_image.height / 2);
+ for (size_t i = 0; i < yuv420_image.height / 2; i++) {
+ memcpy(cb_dst, cb_src, yuv420_image.width / 2);
+ memcpy(cr_dst, cr_src, yuv420_image.width / 2);
+ if (yuv420_bt601_image.width / 2 != yuv420_bt601_image.chroma_stride) {
+ memset(cb_dst + yuv420_image.width / 2, 0,
+ yuv420_bt601_image.chroma_stride - yuv420_image.width / 2);
+ memset(cr_dst + yuv420_image.width / 2, 0,
+ yuv420_bt601_image.chroma_stride - yuv420_image.width / 2);
+ }
+ cb_dst += yuv420_bt601_image.chroma_stride;
+ cb_src += yuv420_image.chroma_stride;
+ cr_dst += yuv420_bt601_image.chroma_stride;
+ cr_src += yuv420_image.chroma_stride;
+ }
+ }
+ JPEGR_CHECK(convertYuv(&yuv420_bt601_image, yuv420_image.colorGamut, ULTRAHDR_COLORGAMUT_P3));
+ }
+
+ // compress 420 image
+ JpegEncoderHelper jpeg_enc_obj_yuv420;
+ if (!jpeg_enc_obj_yuv420.compressImage(reinterpret_cast<uint8_t*>(yuv420_bt601_image.data),
+ reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data),
+ yuv420_bt601_image.width, yuv420_bt601_image.height,
+ yuv420_bt601_image.luma_stride,
+ yuv420_bt601_image.chroma_stride, quality, icc->getData(),
+ icc->getLength())) {
return ERROR_JPEGR_ENCODE_ERROR;
}
- jpegr_compressed_struct jpeg;
- jpeg.data = jpeg_encoder.getCompressedImagePtr();
- jpeg.length = jpeg_encoder.getCompressedImageSize();
- // No ICC since jpeg encode already did it
+ jpegr_compressed_struct jpeg = {.data = jpeg_enc_obj_yuv420.getCompressedImagePtr(),
+ .length = static_cast<int>(
+ jpeg_enc_obj_yuv420.getCompressedImageSize()),
+ .maxLength = static_cast<int>(
+ jpeg_enc_obj_yuv420.getCompressedImageSize()),
+ .colorGamut = yuv420_image.colorGamut};
+
+ // append gain map, no ICC since JPEG encode already did it
JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
&metadata, dest));
-
return NO_ERROR;
}
/* Encode API-2 */
-status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_compressed_ptr compressed_jpeg_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest) {
- if (uncompressed_yuv_420_image == nullptr) {
+status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
+ jr_uncompressed_ptr yuv420_image_ptr,
+ jr_compressed_ptr yuv420jpg_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest) {
+ // validate input arguments
+ if (yuv420_image_ptr == nullptr) {
ALOGE("received nullptr for uncompressed 420 image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (compressed_jpeg_image == nullptr || compressed_jpeg_image->data == nullptr) {
+ if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpeg image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (status_t ret = areInputArgumentsValid(
- uncompressed_p010_image, uncompressed_yuv_420_image, hdr_tf, dest) != NO_ERROR) {
+ if (auto ret = areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest);
+ ret != NO_ERROR) {
return ret;
}
- ultrahdr_metadata_struct metadata;
- metadata.version = kJpegrVersion;
-
- jpegr_uncompressed_struct map;
- JPEGR_CHECK(generateGainMap(
- uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
- std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(map.data));
-
- JpegEncoderHelper jpeg_encoder_gainmap;
- JPEGR_CHECK(compressGainMap(&map, &jpeg_encoder_gainmap));
- jpegr_compressed_struct compressed_map;
- compressed_map.maxLength = jpeg_encoder_gainmap.getCompressedImageSize();
- compressed_map.length = compressed_map.maxLength;
- compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
- compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
-
- // We just want to check if ICC is present, so don't do a full decode. Note,
- // this doesn't verify that the ICC is valid.
- JpegDecoderHelper decoder;
- std::vector<uint8_t> icc;
- decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
- /* pWidth */ nullptr, /* pHeight */ nullptr,
- &icc, /* exifData */ nullptr);
-
- // Add ICC if not already present.
- if (icc.size() > 0) {
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
- /* icc */ nullptr, /* icc size */ 0, &metadata, dest));
- } else {
- sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
- uncompressed_yuv_420_image->colorGamut);
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
- newIcc->getData(), newIcc->getLength(), &metadata, dest));
+ // clean up input structure for later usage
+ jpegr_uncompressed_struct p010_image = *p010_image_ptr;
+ if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
+ if (!p010_image.chroma_data) {
+ uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
+ p010_image.chroma_data = data + p010_image.luma_stride * p010_image.height;
+ p010_image.chroma_stride = p010_image.luma_stride;
+ }
+ jpegr_uncompressed_struct yuv420_image = *yuv420_image_ptr;
+ if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width;
+ if (!yuv420_image.chroma_data) {
+ uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
+ yuv420_image.chroma_data = data + yuv420_image.luma_stride * p010_image.height;
+ yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1;
}
- return NO_ERROR;
+ // gain map
+ ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
+ jpegr_uncompressed_struct gainmap_image;
+ JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(gainmap_image.data));
+
+ // compress gain map
+ JpegEncoderHelper jpeg_enc_obj_gm;
+ JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
+ jpegr_compressed_struct gainmapjpg_image = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
+ .length = static_cast<int>(
+ jpeg_enc_obj_gm.getCompressedImageSize()),
+ .maxLength = static_cast<int>(
+ jpeg_enc_obj_gm.getCompressedImageSize()),
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+
+ return encodeJPEGR(yuv420jpg_image_ptr, &gainmapjpg_image, &metadata, dest);
}
/* Encode API-3 */
-status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_compressed_ptr compressed_jpeg_image,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest) {
- if (compressed_jpeg_image == nullptr || compressed_jpeg_image->data == nullptr) {
+status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
+ jr_compressed_ptr yuv420jpg_image_ptr,
+ ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest) {
+ // validate input arguments
+ if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpeg image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (status_t ret = areInputArgumentsValid(
- uncompressed_p010_image, /* uncompressed_yuv_420_image */ nullptr,
- hdr_tf, dest) != NO_ERROR) {
+ if (auto ret = areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest); ret != NO_ERROR) {
return ret;
}
- // Note: output is Bt.601 YUV encoded regardless of gamut, due to jpeg decode.
- JpegDecoderHelper jpeg_decoder;
- if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
+ // clean up input structure for later usage
+ jpegr_uncompressed_struct p010_image = *p010_image_ptr;
+ if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
+ if (!p010_image.chroma_data) {
+ uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
+ p010_image.chroma_data = data + p010_image.luma_stride * p010_image.height;
+ p010_image.chroma_stride = p010_image.luma_stride;
+ }
+
+ // decode input jpeg, gamut is going to be bt601.
+ JpegDecoderHelper jpeg_dec_obj_yuv420;
+ if (!jpeg_dec_obj_yuv420.decompressImage(yuv420jpg_image_ptr->data,
+ yuv420jpg_image_ptr->length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
- jpegr_uncompressed_struct uncompressed_yuv_420_image;
- uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
- uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
- uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
- uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
+ jpegr_uncompressed_struct yuv420_image{};
+ yuv420_image.data = jpeg_dec_obj_yuv420.getDecompressedImagePtr();
+ yuv420_image.width = jpeg_dec_obj_yuv420.getDecompressedImageWidth();
+ yuv420_image.height = jpeg_dec_obj_yuv420.getDecompressedImageHeight();
+ yuv420_image.colorGamut = yuv420jpg_image_ptr->colorGamut;
+ if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width;
+ if (!yuv420_image.chroma_data) {
+ uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
+ yuv420_image.chroma_data = data + yuv420_image.luma_stride * p010_image.height;
+ yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1;
+ }
- if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
- || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
+ if (p010_image_ptr->width != yuv420_image.width ||
+ p010_image_ptr->height != yuv420_image.height) {
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
- ultrahdr_metadata_struct metadata;
- metadata.version = kJpegrVersion;
-
- jpegr_uncompressed_struct map;
- // Indicate that the SDR image is Bt.601 YUV encoded.
- JPEGR_CHECK(generateGainMap(
- &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map,
- true /* sdr_is_601 */ ));
+ // gain map
+ ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
+ jpegr_uncompressed_struct gainmap_image;
+ JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image,
+ true /* sdr_is_601 */));
std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(map.data));
-
- JpegEncoderHelper jpeg_encoder_gainmap;
- JPEGR_CHECK(compressGainMap(&map, &jpeg_encoder_gainmap));
- jpegr_compressed_struct compressed_map;
- compressed_map.maxLength = jpeg_encoder_gainmap.getCompressedImageSize();
- compressed_map.length = compressed_map.maxLength;
- compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
- compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
-
- // We just want to check if ICC is present, so don't do a full decode. Note,
- // this doesn't verify that the ICC is valid.
- JpegDecoderHelper decoder;
- std::vector<uint8_t> icc;
- decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
- /* pWidth */ nullptr, /* pHeight */ nullptr,
- &icc, /* exifData */ nullptr);
-
- // Add ICC if not already present.
- if (icc.size() > 0) {
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
- /* icc */ nullptr, /* icc size */ 0, &metadata, dest));
- } else {
- sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
- uncompressed_yuv_420_image.colorGamut);
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
- newIcc->getData(), newIcc->getLength(), &metadata, dest));
- }
-
- return NO_ERROR;
+ map_data.reset(reinterpret_cast<uint8_t*>(gainmap_image.data));
+
+ // compress gain map
+ JpegEncoderHelper jpeg_enc_obj_gm;
+ JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
+ jpegr_compressed_struct gainmapjpg_image = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
+ .length = static_cast<int>(
+ jpeg_enc_obj_gm.getCompressedImageSize()),
+ .maxLength = static_cast<int>(
+ jpeg_enc_obj_gm.getCompressedImageSize()),
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+
+ return encodeJPEGR(yuv420jpg_image_ptr, &gainmapjpg_image, &metadata, dest);
}
/* Encode API-4 */
-status_t JpegR::encodeJPEGR(jr_compressed_ptr compressed_jpeg_image,
- jr_compressed_ptr compressed_gainmap,
- ultrahdr_metadata_ptr metadata,
+status_t JpegR::encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
+ jr_compressed_ptr gainmapjpg_image_ptr, ultrahdr_metadata_ptr metadata,
jr_compressed_ptr dest) {
- if (compressed_jpeg_image == nullptr || compressed_jpeg_image->data == nullptr) {
+ if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpeg image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (compressed_gainmap == nullptr || compressed_gainmap->data == nullptr) {
+ if (gainmapjpg_image_ptr == nullptr || gainmapjpg_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed gain map");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
if (dest == nullptr || dest->data == nullptr) {
ALOGE("received nullptr for destination");
return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -510,46 +529,46 @@ status_t JpegR::encodeJPEGR(jr_compressed_ptr compressed_jpeg_image,
// this doesn't verify that the ICC is valid.
JpegDecoderHelper decoder;
std::vector<uint8_t> icc;
- decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
- /* pWidth */ nullptr, /* pHeight */ nullptr,
- &icc, /* exifData */ nullptr);
+ decoder.getCompressedImageParameters(yuv420jpg_image_ptr->data, yuv420jpg_image_ptr->length,
+ /* pWidth */ nullptr, /* pHeight */ nullptr, &icc,
+ /* exifData */ nullptr);
// Add ICC if not already present.
if (icc.size() > 0) {
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, compressed_gainmap, /* exif */ nullptr,
- /* icc */ nullptr, /* icc size */ 0, metadata, dest));
+ JPEGR_CHECK(appendGainMap(yuv420jpg_image_ptr, gainmapjpg_image_ptr, /* exif */ nullptr,
+ /* icc */ nullptr, /* icc size */ 0, metadata, dest));
} else {
- sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
- compressed_jpeg_image->colorGamut);
- JPEGR_CHECK(appendGainMap(compressed_jpeg_image, compressed_gainmap, /* exif */ nullptr,
- newIcc->getData(), newIcc->getLength(), metadata, dest));
+ sp<DataStruct> newIcc =
+ IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420jpg_image_ptr->colorGamut);
+ JPEGR_CHECK(appendGainMap(yuv420jpg_image_ptr, gainmapjpg_image_ptr, /* exif */ nullptr,
+ newIcc->getData(), newIcc->getLength(), metadata, dest));
}
return NO_ERROR;
}
-status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_ptr jpegr_info) {
- if (compressed_jpegr_image == nullptr || compressed_jpegr_image->data == nullptr) {
+status_t JpegR::getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg_image_info_ptr) {
+ if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpegr image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (jpegr_info == nullptr) {
+ if (jpeg_image_info_ptr == nullptr) {
ALOGE("received nullptr for compressed jpegr info struct");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
jpegr_compressed_struct primary_image, gainmap_image;
- status_t status =
- extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image);
+ status_t status = extractPrimaryImageAndGainMap(jpegr_image_ptr, &primary_image, &gainmap_image);
if (status != NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
return status;
}
- JpegDecoderHelper jpeg_decoder;
- if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
- &jpegr_info->width, &jpegr_info->height,
- jpegr_info->iccData, jpegr_info->exifData)) {
+ JpegDecoderHelper jpeg_dec_obj_hdr;
+ if (!jpeg_dec_obj_hdr.getCompressedImageParameters(primary_image.data, primary_image.length,
+ &jpeg_image_info_ptr->width,
+ &jpeg_image_info_ptr->height,
+ jpeg_image_info_ptr->iccData,
+ jpeg_image_info_ptr->exifData)) {
return ERROR_JPEGR_DECODE_ERROR;
}
@@ -557,41 +576,34 @@ status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_p
}
/* Decode API */
-status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
- jr_uncompressed_ptr dest,
- float max_display_boost,
- jr_exif_ptr exif,
+status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_ptr dest,
+ float max_display_boost, jr_exif_ptr exif,
ultrahdr_output_format output_format,
- jr_uncompressed_ptr gain_map,
- ultrahdr_metadata_ptr metadata) {
- if (compressed_jpegr_image == nullptr || compressed_jpegr_image->data == nullptr) {
+ jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata) {
+ if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) {
ALOGE("received nullptr for compressed jpegr image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
if (dest == nullptr || dest->data == nullptr) {
ALOGE("received nullptr for dest image");
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
if (max_display_boost < 1.0f) {
ALOGE("received bad value for max_display_boost %f", max_display_boost);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
if (exif != nullptr && exif->data == nullptr) {
ALOGE("received nullptr address for exif data");
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
if (output_format <= ULTRAHDR_OUTPUT_UNSPECIFIED || output_format > ULTRAHDR_OUTPUT_MAX) {
ALOGE("received bad value for output format %d", output_format);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- jpegr_compressed_struct primary_image, gainmap_image;
+ jpegr_compressed_struct primary_jpeg_image, gainmap_jpeg_image;
status_t status =
- extractPrimaryImageAndGainMap(compressed_jpegr_image, &primary_image, &gainmap_image);
+ extractPrimaryImageAndGainMap(jpegr_image_ptr, &primary_jpeg_image, &gainmap_jpeg_image);
if (status != NO_ERROR) {
if (output_format != ULTRAHDR_OUTPUT_SDR || status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
ALOGE("received invalid compressed jpegr image");
@@ -599,22 +611,22 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
}
}
- JpegDecoderHelper jpeg_decoder;
- if (!jpeg_decoder.decompressImage(primary_image.data, primary_image.length,
- (output_format == ULTRAHDR_OUTPUT_SDR))) {
+ JpegDecoderHelper jpeg_dec_obj_yuv420;
+ if (!jpeg_dec_obj_yuv420.decompressImage(primary_jpeg_image.data, primary_jpeg_image.length,
+ (output_format == ULTRAHDR_OUTPUT_SDR))) {
return ERROR_JPEGR_DECODE_ERROR;
}
if (output_format == ULTRAHDR_OUTPUT_SDR) {
- if ((jpeg_decoder.getDecompressedImageWidth() *
- jpeg_decoder.getDecompressedImageHeight() * 4) >
- jpeg_decoder.getDecompressedImageSize()) {
+ if ((jpeg_dec_obj_yuv420.getDecompressedImageWidth() *
+ jpeg_dec_obj_yuv420.getDecompressedImageHeight() * 4) >
+ jpeg_dec_obj_yuv420.getDecompressedImageSize()) {
return ERROR_JPEGR_CALCULATION_ERROR;
}
} else {
- if ((jpeg_decoder.getDecompressedImageWidth() *
- jpeg_decoder.getDecompressedImageHeight() * 3 / 2) >
- jpeg_decoder.getDecompressedImageSize()) {
+ if ((jpeg_dec_obj_yuv420.getDecompressedImageWidth() *
+ jpeg_dec_obj_yuv420.getDecompressedImageHeight() * 3 / 2) >
+ jpeg_dec_obj_yuv420.getDecompressedImageSize()) {
return ERROR_JPEGR_CALCULATION_ERROR;
}
}
@@ -623,46 +635,46 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
if (exif->data == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- if (exif->length < jpeg_decoder.getEXIFSize()) {
+ if (exif->length < jpeg_dec_obj_yuv420.getEXIFSize()) {
return ERROR_JPEGR_BUFFER_TOO_SMALL;
}
- memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize());
- exif->length = jpeg_decoder.getEXIFSize();
+ memcpy(exif->data, jpeg_dec_obj_yuv420.getEXIFPtr(), jpeg_dec_obj_yuv420.getEXIFSize());
+ exif->length = jpeg_dec_obj_yuv420.getEXIFSize();
}
if (output_format == ULTRAHDR_OUTPUT_SDR) {
- dest->width = jpeg_decoder.getDecompressedImageWidth();
- dest->height = jpeg_decoder.getDecompressedImageHeight();
- memcpy(dest->data, jpeg_decoder.getDecompressedImagePtr(), dest->width * dest->height * 4);
+ dest->width = jpeg_dec_obj_yuv420.getDecompressedImageWidth();
+ dest->height = jpeg_dec_obj_yuv420.getDecompressedImageHeight();
+ memcpy(dest->data, jpeg_dec_obj_yuv420.getDecompressedImagePtr(),
+ dest->width * dest->height * 4);
return NO_ERROR;
}
- JpegDecoderHelper gain_map_decoder;
- if (!gain_map_decoder.decompressImage(gainmap_image.data, gainmap_image.length)) {
+ JpegDecoderHelper jpeg_dec_obj_gm;
+ if (!jpeg_dec_obj_gm.decompressImage(gainmap_jpeg_image.data, gainmap_jpeg_image.length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
- if ((gain_map_decoder.getDecompressedImageWidth() *
- gain_map_decoder.getDecompressedImageHeight()) >
- gain_map_decoder.getDecompressedImageSize()) {
+ if ((jpeg_dec_obj_gm.getDecompressedImageWidth() * jpeg_dec_obj_gm.getDecompressedImageHeight()) >
+ jpeg_dec_obj_gm.getDecompressedImageSize()) {
return ERROR_JPEGR_CALCULATION_ERROR;
}
- jpegr_uncompressed_struct map;
- map.data = gain_map_decoder.getDecompressedImagePtr();
- map.width = gain_map_decoder.getDecompressedImageWidth();
- map.height = gain_map_decoder.getDecompressedImageHeight();
+ jpegr_uncompressed_struct gainmap_image;
+ gainmap_image.data = jpeg_dec_obj_gm.getDecompressedImagePtr();
+ gainmap_image.width = jpeg_dec_obj_gm.getDecompressedImageWidth();
+ gainmap_image.height = jpeg_dec_obj_gm.getDecompressedImageHeight();
- if (gain_map != nullptr) {
- gain_map->width = map.width;
- gain_map->height = map.height;
- int size = gain_map->width * gain_map->height;
- gain_map->data = malloc(size);
- memcpy(gain_map->data, map.data, size);
+ if (gainmap_image_ptr != nullptr) {
+ gainmap_image_ptr->width = gainmap_image.width;
+ gainmap_image_ptr->height = gainmap_image.height;
+ int size = gainmap_image_ptr->width * gainmap_image_ptr->height;
+ gainmap_image_ptr->data = malloc(size);
+ memcpy(gainmap_image_ptr->data, gainmap_image.data, size);
}
ultrahdr_metadata_struct uhdr_metadata;
- if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
- gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
+ if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_dec_obj_gm.getXMPPtr()),
+ jpeg_dec_obj_gm.getXMPSize(), &uhdr_metadata)) {
return ERROR_JPEGR_INVALID_METADATA;
}
@@ -677,32 +689,33 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax;
}
- jpegr_uncompressed_struct uncompressed_yuv_420_image;
- uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
- uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
- uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
- uncompressed_yuv_420_image.colorGamut = IccHelper::readIccColorGamut(
- jpeg_decoder.getICCPtr(), jpeg_decoder.getICCSize());
+ jpegr_uncompressed_struct yuv420_image;
+ yuv420_image.data = jpeg_dec_obj_yuv420.getDecompressedImagePtr();
+ yuv420_image.width = jpeg_dec_obj_yuv420.getDecompressedImageWidth();
+ yuv420_image.height = jpeg_dec_obj_yuv420.getDecompressedImageHeight();
+ yuv420_image.colorGamut = IccHelper::readIccColorGamut(jpeg_dec_obj_yuv420.getICCPtr(),
+ jpeg_dec_obj_yuv420.getICCSize());
+ yuv420_image.luma_stride = yuv420_image.width;
+ uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
+ yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height;
+ yuv420_image.chroma_stride = yuv420_image.width >> 1;
- JPEGR_CHECK(applyGainMap(&uncompressed_yuv_420_image, &map, &uhdr_metadata, output_format,
+ JPEGR_CHECK(applyGainMap(&yuv420_image, &gainmap_image, &uhdr_metadata, output_format,
max_display_boost, dest));
return NO_ERROR;
}
-status_t JpegR::compressGainMap(jr_uncompressed_ptr uncompressed_gain_map,
- JpegEncoderHelper* jpeg_encoder) {
- if (uncompressed_gain_map == nullptr || jpeg_encoder == nullptr) {
+status_t JpegR::compressGainMap(jr_uncompressed_ptr gainmap_image_ptr,
+ JpegEncoderHelper* jpeg_enc_obj_ptr) {
+ if (gainmap_image_ptr == nullptr || jpeg_enc_obj_ptr == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
// Don't need to convert YUV to Bt601 since single channel
- if (!jpeg_encoder->compressImage(uncompressed_gain_map->data,
- uncompressed_gain_map->width,
- uncompressed_gain_map->height,
- kMapCompressQuality,
- nullptr,
- 0,
- true /* isSingleChannel */)) {
+ if (!jpeg_enc_obj_ptr->compressImage(reinterpret_cast<uint8_t*>(gainmap_image_ptr->data), nullptr,
+ gainmap_image_ptr->width, gainmap_image_ptr->height,
+ gainmap_image_ptr->luma_stride, 0, kMapCompressQuality,
+ nullptr, 0)) {
return ERROR_JPEGR_ENCODE_ERROR;
}
@@ -714,13 +727,13 @@ static_assert(kJobSzInRows > 0 && kJobSzInRows % kMapDimensionScaleFactor == 0,
"align job size to kMapDimensionScaleFactor");
class JobQueue {
- public:
+public:
bool dequeueJob(size_t& rowStart, size_t& rowEnd);
void enqueueJob(size_t rowStart, size_t rowEnd);
void markQueueForEnd();
void reset();
- private:
+private:
bool mQueuedAllJobs = false;
std::deque<std::tuple<size_t, size_t>> mJobs;
std::mutex mMutex;
@@ -767,41 +780,37 @@ void JobQueue::reset() {
mQueuedAllJobs = false;
}
-status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_uncompressed_ptr uncompressed_p010_image,
- ultrahdr_transfer_function hdr_tf,
- ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr dest,
- bool sdr_is_601) {
- if (uncompressed_yuv_420_image == nullptr
- || uncompressed_p010_image == nullptr
- || metadata == nullptr
- || dest == nullptr) {
+status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
+ jr_uncompressed_ptr p010_image_ptr,
+ ultrahdr_transfer_function hdr_tf, ultrahdr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest, bool sdr_is_601) {
+ if (yuv420_image_ptr == nullptr || p010_image_ptr == nullptr || metadata == nullptr ||
+ dest == nullptr || yuv420_image_ptr->data == nullptr ||
+ yuv420_image_ptr->chroma_data == nullptr || p010_image_ptr->data == nullptr ||
+ p010_image_ptr->chroma_data == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width
- || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) {
+ if (yuv420_image_ptr->width != p010_image_ptr->width ||
+ yuv420_image_ptr->height != p010_image_ptr->height) {
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
-
- if (uncompressed_yuv_420_image->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED
- || uncompressed_p010_image->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
+ if (yuv420_image_ptr->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
+ p010_image_ptr->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
return ERROR_JPEGR_INVALID_COLORGAMUT;
}
- size_t image_width = uncompressed_yuv_420_image->width;
- size_t image_height = uncompressed_yuv_420_image->height;
+ size_t image_width = yuv420_image_ptr->width;
+ size_t image_height = yuv420_image_ptr->height;
size_t map_width = image_width / kMapDimensionScaleFactor;
size_t map_height = image_height / kMapDimensionScaleFactor;
- size_t map_stride = static_cast<size_t>(
- floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock;
- size_t map_height_aligned = ((map_height + 1) >> 1) << 1;
- dest->width = map_stride;
- dest->height = map_height_aligned;
+ dest->data = new uint8_t[map_width * map_height];
+ dest->width = map_width;
+ dest->height = map_height;
dest->colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- dest->data = new uint8_t[map_stride * map_height_aligned];
+ dest->luma_stride = map_width;
+ dest->chroma_data = nullptr;
+ dest->chroma_stride = 0;
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
@@ -843,12 +852,12 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
float log2MinBoost = log2(metadata->minContentBoost);
float log2MaxBoost = log2(metadata->maxContentBoost);
- ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
- uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
+ ColorTransformFn hdrGamutConversionFn =
+ getHdrConversionFn(yuv420_image_ptr->colorGamut, p010_image_ptr->colorGamut);
ColorCalculationFn luminanceFn = nullptr;
ColorTransformFn sdrYuvToRgbFn = nullptr;
- switch (uncompressed_yuv_420_image->colorGamut) {
+ switch (yuv420_image_ptr->colorGamut) {
case ULTRAHDR_COLORGAMUT_BT709:
luminanceFn = srgbLuminance;
sdrYuvToRgbFn = srgbYuvToRgb;
@@ -870,7 +879,7 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
}
ColorTransformFn hdrYuvToRgbFn = nullptr;
- switch (uncompressed_p010_image->colorGamut) {
+ switch (p010_image_ptr->colorGamut) {
case ULTRAHDR_COLORGAMUT_BT709:
hdrYuvToRgbFn = srgbYuvToRgb;
break;
@@ -890,18 +899,15 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
size_t rowStep = threads == 1 ? image_height : kJobSzInRows;
JobQueue jobQueue;
- std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image,
- metadata, dest, hdrInvOetf, hdrGamutConversionFn,
- luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, hdr_white_nits,
- log2MinBoost, log2MaxBoost, &jobQueue]() -> void {
+ std::function<void()> generateMap = [yuv420_image_ptr, p010_image_ptr, metadata, dest, hdrInvOetf,
+ hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn,
+ hdrYuvToRgbFn, hdr_white_nits, log2MinBoost, log2MaxBoost,
+ &jobQueue]() -> void {
size_t rowStart, rowEnd;
- size_t dest_map_width = uncompressed_yuv_420_image->width / kMapDimensionScaleFactor;
- size_t dest_map_stride = dest->width;
while (jobQueue.dequeueJob(rowStart, rowEnd)) {
for (size_t y = rowStart; y < rowEnd; ++y) {
- for (size_t x = 0; x < dest_map_width; ++x) {
- Color sdr_yuv_gamma =
- sampleYuv420(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y);
+ for (size_t x = 0; x < dest->width; ++x) {
+ Color sdr_yuv_gamma = sampleYuv420(yuv420_image_ptr, kMapDimensionScaleFactor, x, y);
Color sdr_rgb_gamma = sdrYuvToRgbFn(sdr_yuv_gamma);
// We are assuming the SDR input is always sRGB transfer.
#if USE_SRGB_INVOETF_LUT
@@ -911,15 +917,15 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
#endif
float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
- Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
+ Color hdr_yuv_gamma = sampleP010(p010_image_ptr, kMapDimensionScaleFactor, x, y);
Color hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
hdr_rgb = hdrGamutConversionFn(hdr_rgb);
float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
- size_t pixel_idx = x + y * dest_map_stride;
+ size_t pixel_idx = x + y * dest->width;
reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
- encodeGain(sdr_y_nits, hdr_y_nits, metadata, log2MinBoost, log2MaxBoost);
+ encodeGain(sdr_y_nits, hdr_y_nits, metadata, log2MinBoost, log2MaxBoost);
}
}
}
@@ -945,71 +951,64 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
return NO_ERROR;
}
-status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_uncompressed_ptr uncompressed_gain_map,
- ultrahdr_metadata_ptr metadata,
- ultrahdr_output_format output_format,
- float max_display_boost,
+status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
+ jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata,
+ ultrahdr_output_format output_format, float max_display_boost,
jr_uncompressed_ptr dest) {
- if (uncompressed_yuv_420_image == nullptr
- || uncompressed_gain_map == nullptr
- || metadata == nullptr
- || dest == nullptr) {
+ if (yuv420_image_ptr == nullptr || gainmap_image_ptr == nullptr || metadata == nullptr ||
+ dest == nullptr || yuv420_image_ptr->data == nullptr ||
+ yuv420_image_ptr->chroma_data == nullptr || gainmap_image_ptr->data == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (metadata->version.compare("1.0")) {
- ALOGE("Unsupported metadata version: %s", metadata->version.c_str());
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ if (metadata->version.compare(kJpegrVersion)) {
+ ALOGE("Unsupported metadata version: %s", metadata->version.c_str());
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
}
if (metadata->gamma != 1.0f) {
- ALOGE("Unsupported metadata gamma: %f", metadata->gamma);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ ALOGE("Unsupported metadata gamma: %f", metadata->gamma);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
}
if (metadata->offsetSdr != 0.0f || metadata->offsetHdr != 0.0f) {
- ALOGE("Unsupported metadata offset sdr, hdr: %f, %f", metadata->offsetSdr,
- metadata->offsetHdr);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ ALOGE("Unsupported metadata offset sdr, hdr: %f, %f", metadata->offsetSdr, metadata->offsetHdr);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
}
- if (metadata->hdrCapacityMin != metadata->minContentBoost
- || metadata->hdrCapacityMax != metadata->maxContentBoost) {
- ALOGE("Unsupported metadata hdr capacity min, max: %f, %f", metadata->hdrCapacityMin,
- metadata->hdrCapacityMax);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
+ if (metadata->hdrCapacityMin != metadata->minContentBoost ||
+ metadata->hdrCapacityMax != metadata->maxContentBoost) {
+ ALOGE("Unsupported metadata hdr capacity min, max: %f, %f", metadata->hdrCapacityMin,
+ metadata->hdrCapacityMax);
+ return ERROR_JPEGR_UNSUPPORTED_METADATA;
}
// TODO: remove once map scaling factor is computed based on actual map dims
- size_t image_width = uncompressed_yuv_420_image->width;
- size_t image_height = uncompressed_yuv_420_image->height;
+ size_t image_width = yuv420_image_ptr->width;
+ size_t image_height = yuv420_image_ptr->height;
size_t map_width = image_width / kMapDimensionScaleFactor;
size_t map_height = image_height / kMapDimensionScaleFactor;
- map_width = static_cast<size_t>(
- floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock;
- map_height = ((map_height + 1) >> 1) << 1;
- if (map_width != uncompressed_gain_map->width
- || map_height != uncompressed_gain_map->height) {
- ALOGE("gain map dimensions and primary image dimensions are not to scale");
+ if (map_width != gainmap_image_ptr->width || map_height != gainmap_image_ptr->height) {
+ ALOGE("gain map dimensions and primary image dimensions are not to scale, computed gain map "
+ "resolution is %dx%d, received gain map resolution is %dx%d",
+ (int)map_width, (int)map_height, gainmap_image_ptr->width, gainmap_image_ptr->height);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- dest->width = uncompressed_yuv_420_image->width;
- dest->height = uncompressed_yuv_420_image->height;
+ dest->width = yuv420_image_ptr->width;
+ dest->height = yuv420_image_ptr->height;
ShepardsIDW idwTable(kMapDimensionScaleFactor);
float display_boost = std::min(max_display_boost, metadata->maxContentBoost);
GainLUT gainLUT(metadata, display_boost);
JobQueue jobQueue;
- std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_gain_map,
- metadata, dest, &jobQueue, &idwTable, output_format,
- &gainLUT, display_boost]() -> void {
- size_t width = uncompressed_yuv_420_image->width;
- size_t height = uncompressed_yuv_420_image->height;
+ std::function<void()> applyRecMap = [yuv420_image_ptr, gainmap_image_ptr, metadata, dest,
+ &jobQueue, &idwTable, output_format, &gainLUT,
+ display_boost]() -> void {
+ size_t width = yuv420_image_ptr->width;
+ size_t height = yuv420_image_ptr->height;
size_t rowStart, rowEnd;
while (jobQueue.dequeueJob(rowStart, rowEnd)) {
for (size_t y = rowStart; y < rowEnd; ++y) {
for (size_t x = 0; x < width; ++x) {
- Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
+ Color yuv_gamma_sdr = getYuv420Pixel(yuv420_image_ptr, x, y);
// Assuming the sdr image is a decoded JPEG, we should always use Rec.601 YUV coefficients
Color rgb_gamma_sdr = p3YuvToRgb(yuv_gamma_sdr);
// We are assuming the SDR base image is always sRGB transfer.
@@ -1025,9 +1024,9 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
// Currently map_scale_factor is of type size_t, but it could be changed to a float
// later.
if (map_scale_factor != floorf(map_scale_factor)) {
- gain = sampleMap(uncompressed_gain_map, map_scale_factor, x, y);
+ gain = sampleMap(gainmap_image_ptr, map_scale_factor, x, y);
} else {
- gain = sampleMap(uncompressed_gain_map, map_scale_factor, x, y, idwTable);
+ gain = sampleMap(gainmap_image_ptr, map_scale_factor, x, y, idwTable);
}
#if USE_APPLY_GAIN_LUT
@@ -1039,14 +1038,12 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
size_t pixel_idx = x + y * width;
switch (output_format) {
- case ULTRAHDR_OUTPUT_HDR_LINEAR:
- {
+ case ULTRAHDR_OUTPUT_HDR_LINEAR: {
uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16;
break;
}
- case ULTRAHDR_OUTPUT_HDR_HLG:
- {
+ case ULTRAHDR_OUTPUT_HDR_HLG: {
#if USE_HLG_OETF_LUT
ColorTransformFn hdrOetf = hlgOetfLUT;
#else
@@ -1057,8 +1054,7 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
break;
}
- case ULTRAHDR_OUTPUT_HDR_PQ:
- {
+ case ULTRAHDR_OUTPUT_HDR_PQ: {
#if USE_PQ_OETF_LUT
ColorTransformFn hdrOetf = pqOetfLUT;
#else
@@ -1069,8 +1065,8 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
break;
}
- default:
- {}
+ default: {
+ }
// Should be impossible to hit after input validation.
}
}
@@ -1083,9 +1079,9 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
for (int th = 0; th < threads - 1; th++) {
workers.push_back(std::thread(applyRecMap));
}
- const int rowStep = threads == 1 ? uncompressed_yuv_420_image->height : kJobSzInRows;
- for (int rowStart = 0; rowStart < uncompressed_yuv_420_image->height;) {
- int rowEnd = std::min(rowStart + rowStep, uncompressed_yuv_420_image->height);
+ const int rowStep = threads == 1 ? yuv420_image_ptr->height : kJobSzInRows;
+ for (int rowStart = 0; rowStart < yuv420_image_ptr->height;) {
+ int rowEnd = std::min(rowStart + rowStep, yuv420_image_ptr->height);
jobQueue.enqueueJob(rowStart, rowEnd);
rowStart = rowEnd;
}
@@ -1095,18 +1091,18 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
return NO_ERROR;
}
-status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr primary_image,
- jr_compressed_ptr gain_map) {
- if (compressed_jpegr_image == nullptr) {
+status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
+ jr_compressed_ptr primary_jpg_image_ptr,
+ jr_compressed_ptr gainmap_jpg_image_ptr) {
+ if (jpegr_image_ptr == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
MessageHandler msg_handler;
std::shared_ptr<DataSegment> seg =
- DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
- static_cast<const uint8_t*>(compressed_jpegr_image->data),
- DataSegment::BufferDispositionPolicy::kDontDelete);
+ DataSegment::Create(DataRange(0, jpegr_image_ptr->length),
+ static_cast<const uint8_t*>(jpegr_image_ptr->data),
+ DataSegment::BufferDispositionPolicy::kDontDelete);
DataSegmentDataSource data_source(seg);
JpegInfoBuilder jpeg_info_builder;
jpeg_info_builder.SetImageLimit(2);
@@ -1125,20 +1121,20 @@ status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- if (primary_image != nullptr) {
- primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
- image_ranges[0].GetBegin();
- primary_image->length = image_ranges[0].GetLength();
+ if (primary_jpg_image_ptr != nullptr) {
+ primary_jpg_image_ptr->data =
+ static_cast<uint8_t*>(jpegr_image_ptr->data) + image_ranges[0].GetBegin();
+ primary_jpg_image_ptr->length = image_ranges[0].GetLength();
}
if (image_ranges.size() == 1) {
return ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND;
}
- if (gain_map != nullptr) {
- gain_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
- image_ranges[1].GetBegin();
- gain_map->length = image_ranges[1].GetLength();
+ if (gainmap_jpg_image_ptr != nullptr) {
+ gainmap_jpg_image_ptr->data =
+ static_cast<uint8_t*>(jpegr_image_ptr->data) + image_ranges[1].GetBegin();
+ gainmap_jpg_image_ptr->length = image_ranges[1].GetLength();
}
// TODO: choose primary image and gain map image carefully
@@ -1183,58 +1179,48 @@ status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr
// Exif 2.2 spec for EXIF marker
// Adobe XMP spec part 3 for XMP marker
// ICC v4.3 spec for ICC
-status_t JpegR::appendGainMap(jr_compressed_ptr compressed_jpeg_image,
- jr_compressed_ptr compressed_gain_map,
- jr_exif_ptr exif,
- void* icc, size_t icc_size,
- ultrahdr_metadata_ptr metadata,
+status_t JpegR::appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
+ jr_compressed_ptr gainmap_jpg_image_ptr, jr_exif_ptr exif, void* icc,
+ size_t icc_size, ultrahdr_metadata_ptr metadata,
jr_compressed_ptr dest) {
- if (compressed_jpeg_image == nullptr
- || compressed_gain_map == nullptr
- || metadata == nullptr
- || dest == nullptr) {
+ if (primary_jpg_image_ptr == nullptr || gainmap_jpg_image_ptr == nullptr || metadata == nullptr ||
+ dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
if (metadata->version.compare("1.0")) {
ALOGE("received bad value for version: %s", metadata->version.c_str());
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
if (metadata->maxContentBoost < metadata->minContentBoost) {
ALOGE("received bad value for content boost min %f, max %f", metadata->minContentBoost,
- metadata->maxContentBoost);
+ metadata->maxContentBoost);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
if (metadata->hdrCapacityMax < metadata->hdrCapacityMin || metadata->hdrCapacityMin < 1.0f) {
ALOGE("received bad value for hdr capacity min %f, max %f", metadata->hdrCapacityMin,
- metadata->hdrCapacityMax);
+ metadata->hdrCapacityMax);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
if (metadata->offsetSdr < 0.0f || metadata->offsetHdr < 0.0f) {
- ALOGE("received bad value for offset sdr %f, hdr %f", metadata->offsetSdr,
- metadata->offsetHdr);
+ ALOGE("received bad value for offset sdr %f, hdr %f", metadata->offsetSdr, metadata->offsetHdr);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
-
if (metadata->gamma <= 0.0f) {
ALOGE("received bad value for gamma %f", metadata->gamma);
return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
const string nameSpace = "http://ns.adobe.com/xap/1.0/";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
// calculate secondary image length first, because the length will be written into the primary
// image xmp
const string xmp_secondary = generateXmpForSecondaryImage(*metadata);
const int xmp_secondary_length = 2 /* 2 bytes representing the length of the package */
- + nameSpaceLength /* 29 bytes length of name space including \0 */
- + xmp_secondary.size(); /* length of xmp packet */
+ + nameSpaceLength /* 29 bytes length of name space including \0 */
+ + xmp_secondary.size(); /* length of xmp packet */
const int secondary_image_size = 2 /* 2 bytes length of APP1 sign */
- + xmp_secondary_length
- + compressed_gain_map->length;
+ + xmp_secondary_length + gainmap_jpg_image_ptr->length;
// primary image
const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size, *metadata);
// same as primary
@@ -1273,41 +1259,39 @@ status_t JpegR::appendGainMap(jr_compressed_ptr compressed_jpeg_image,
// Write ICC
if (icc != nullptr && icc_size > 0) {
- const int length = icc_size + 2;
- const uint8_t lengthH = ((length >> 8) & 0xff);
- const uint8_t lengthL = (length & 0xff);
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
- JPEGR_CHECK(Write(dest, icc, icc_size, pos));
+ const int length = icc_size + 2;
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, icc, icc_size, pos));
}
// Prepare and write MPF
{
- const int length = 2 + calculateMpfSize();
- const uint8_t lengthH = ((length >> 8) & 0xff);
- const uint8_t lengthL = (length & 0xff);
- int primary_image_size = pos + length + compressed_jpeg_image->length;
- // between APP2 + package size + signature
- // ff e2 00 58 4d 50 46 00
- // 2 + 2 + 4 = 8 (bytes)
- // and ff d8 sign of the secondary image
- int secondary_image_offset = primary_image_size - pos - 8;
- sp<DataStruct> mpf = generateMpf(primary_image_size,
- 0, /* primary_image_offset */
- secondary_image_size,
- secondary_image_offset);
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
- JPEGR_CHECK(Write(dest, (void*)mpf->getData(), mpf->getLength(), pos));
+ const int length = 2 + calculateMpfSize();
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+ int primary_image_size = pos + length + primary_jpg_image_ptr->length;
+ // between APP2 + package size + signature
+ // ff e2 00 58 4d 50 46 00
+ // 2 + 2 + 4 = 8 (bytes)
+ // and ff d8 sign of the secondary image
+ int secondary_image_offset = primary_image_size - pos - 8;
+ sp<DataStruct> mpf = generateMpf(primary_image_size, 0, /* primary_image_offset */
+ secondary_image_size, secondary_image_offset);
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, (void*)mpf->getData(), mpf->getLength(), pos));
}
// Write primary image
- JPEGR_CHECK(Write(dest,
- (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
+ JPEGR_CHECK(Write(dest, (uint8_t*)primary_jpg_image_ptr->data + 2,
+ primary_jpg_image_ptr->length - 2, pos));
// Finish primary image
// Begin secondary image (gain map)
@@ -1329,8 +1313,8 @@ status_t JpegR::appendGainMap(jr_compressed_ptr compressed_jpeg_image,
}
// Write secondary image
- JPEGR_CHECK(Write(dest,
- (uint8_t*)compressed_gain_map->data + 2, compressed_gain_map->length - 2, pos));
+ JPEGR_CHECK(Write(dest, (uint8_t*)gainmap_jpg_image_ptr->data + 2,
+ gainmap_jpg_image_ptr->length - 2, pos));
// Set back length
dest->length = pos;
@@ -1343,62 +1327,52 @@ status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) {
if (src == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- uint16_t* src_luma_data = reinterpret_cast<uint16_t*>(src->data);
- size_t src_luma_stride = src->luma_stride == 0 ? src->width : src->luma_stride;
-
- uint16_t* src_chroma_data;
- size_t src_chroma_stride;
- if (src->chroma_data == nullptr) {
- src_chroma_stride = src_luma_stride;
- src_chroma_data = &reinterpret_cast<uint16_t*>(src->data)[src_luma_stride * src->height];
- } else {
- src_chroma_stride = src->chroma_stride;
- src_chroma_data = reinterpret_cast<uint16_t*>(src->chroma_data);
+ if (src->width != dest->width || src->height != dest->height) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
}
- dest->width = src->width;
- dest->height = src->height;
-
- size_t dest_luma_pixel_count = dest->width * dest->height;
-
+ uint16_t* src_y_data = reinterpret_cast<uint16_t*>(src->data);
+ uint8_t* dst_y_data = reinterpret_cast<uint8_t*>(dest->data);
for (size_t y = 0; y < src->height; ++y) {
+ uint16_t* src_y_row = src_y_data + y * src->luma_stride;
+ uint8_t* dst_y_row = dst_y_data + y * dest->luma_stride;
for (size_t x = 0; x < src->width; ++x) {
- size_t src_y_idx = y * src_luma_stride + x;
- size_t src_u_idx = (y >> 1) * src_chroma_stride + (x & ~0x1);
- size_t src_v_idx = src_u_idx + 1;
-
- uint16_t y_uint = src_luma_data[src_y_idx] >> 6;
- uint16_t u_uint = src_chroma_data[src_u_idx] >> 6;
- uint16_t v_uint = src_chroma_data[src_v_idx] >> 6;
-
- size_t dest_y_idx = x + y * dest->width;
- size_t dest_uv_idx = x / 2 + (y / 2) * (dest->width / 2);
-
- uint8_t* y = &reinterpret_cast<uint8_t*>(dest->data)[dest_y_idx];
- uint8_t* u = &reinterpret_cast<uint8_t*>(dest->data)[dest_luma_pixel_count + dest_uv_idx];
- uint8_t* v = &reinterpret_cast<uint8_t*>(
- dest->data)[dest_luma_pixel_count * 5 / 4 + dest_uv_idx];
-
- *y = static_cast<uint8_t>((y_uint >> 2) & 0xff);
- *u = static_cast<uint8_t>((u_uint >> 2) & 0xff);
- *v = static_cast<uint8_t>((v_uint >> 2) & 0xff);
+ uint16_t y_uint = src_y_row[x] >> 6;
+ dst_y_row[x] = static_cast<uint8_t>((y_uint >> 2) & 0xff);
+ }
+ if (dest->width != dest->luma_stride) {
+ memset(dst_y_row + dest->width, 0, dest->luma_stride - dest->width);
+ }
+ }
+ uint16_t* src_uv_data = reinterpret_cast<uint16_t*>(src->chroma_data);
+ uint8_t* dst_u_data = reinterpret_cast<uint8_t*>(dest->chroma_data);
+ size_t dst_v_offset = (dest->chroma_stride * dest->height / 2);
+ uint8_t* dst_v_data = dst_u_data + dst_v_offset;
+ for (size_t y = 0; y < src->height / 2; ++y) {
+ uint16_t* src_uv_row = src_uv_data + y * src->chroma_stride;
+ uint8_t* dst_u_row = dst_u_data + y * dest->chroma_stride;
+ uint8_t* dst_v_row = dst_v_data + y * dest->chroma_stride;
+ for (size_t x = 0; x < src->width / 2; ++x) {
+ uint16_t u_uint = src_uv_row[x << 1] >> 6;
+ uint16_t v_uint = src_uv_row[(x << 1) + 1] >> 6;
+ dst_u_row[x] = static_cast<uint8_t>((u_uint >> 2) & 0xff);
+ dst_v_row[x] = static_cast<uint8_t>((v_uint >> 2) & 0xff);
+ }
+ if (dest->width / 2 != dest->chroma_stride) {
+ memset(dst_u_row + dest->width / 2, 0, dest->chroma_stride - dest->width / 2);
+ memset(dst_v_row + dest->width / 2, 0, dest->chroma_stride - dest->width / 2);
}
}
-
dest->colorGamut = src->colorGamut;
-
return NO_ERROR;
}
-status_t JpegR::convertYuv(jr_uncompressed_ptr image,
- ultrahdr_color_gamut src_encoding,
+status_t JpegR::convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_encoding,
ultrahdr_color_gamut dest_encoding) {
if (image == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
-
- if (src_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED
- || dest_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
+ if (src_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
+ dest_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
return ERROR_JPEGR_INVALID_COLORGAMUT;
}
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
index 594413018c..004a582fd6 100644
--- a/libs/ultrahdr/tests/Android.bp
+++ b/libs/ultrahdr/tests/Android.bp
@@ -22,7 +22,7 @@ package {
}
cc_test {
- name: "libultrahdr_test",
+ name: "ultrahdr_unit_test",
test_suites: ["device-tests"],
srcs: [
"gainmapmath_test.cpp",
@@ -45,7 +45,7 @@ cc_test {
}
cc_test {
- name: "libjpegencoderhelper_test",
+ name: "jpegencoderhelper_test",
test_suites: ["device-tests"],
srcs: [
"jpegencoderhelper_test.cpp",
@@ -61,7 +61,7 @@ cc_test {
}
cc_test {
- name: "libjpegdecoderhelper_test",
+ name: "jpegdecoderhelper_test",
test_suites: ["device-tests"],
srcs: [
"jpegdecoderhelper_test.cpp",
diff --git a/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010 b/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010
deleted file mode 100644
index e7a5dc84dc..0000000000
--- a/libs/ultrahdr/tests/data/raw_p010_image_with_stride.p010
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/gainmapmath_test.cpp b/libs/ultrahdr/tests/gainmapmath_test.cpp
index af90365e56..7c2d076992 100644
--- a/libs/ultrahdr/tests/gainmapmath_test.cpp
+++ b/libs/ultrahdr/tests/gainmapmath_test.cpp
@@ -120,7 +120,7 @@ public:
0xB0, 0xB1,
0xB2, 0xB3,
};
- return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709 };
+ return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 2 };
}
Color (*Yuv420Colors())[4] {
@@ -153,7 +153,7 @@ public:
0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
};
- return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709 };
+ return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 4 };
}
Color (*P010Colors())[4] {
@@ -636,6 +636,9 @@ TEST_F(GainMapMathTest, TransformYuv420) {
memcpy(out_buf.get(), input.data, out_buf_size);
jpegr_uncompressed_struct output = Yuv420Image();
output.data = out_buf.get();
+ output.chroma_data = out_buf.get() + input.width * input.height;
+ output.luma_stride = input.width;
+ output.chroma_stride = input.width / 2;
transformYuv420(&output, 1, 1, transform);
diff --git a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
index f0e1fa4968..33cb9f658f 100644
--- a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
+++ b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
@@ -42,6 +42,7 @@ public:
};
JpegEncoderHelperTest();
~JpegEncoderHelperTest();
+
protected:
virtual void SetUp();
virtual void TearDown();
@@ -103,24 +104,32 @@ void JpegEncoderHelperTest::TearDown() {}
TEST_F(JpegEncoderHelperTest, encodeAlignedImage) {
JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mAlignedImage.buffer.get(), mAlignedImage.width,
- mAlignedImage.height, JPEG_QUALITY, NULL, 0));
+ EXPECT_TRUE(encoder.compressImage(mAlignedImage.buffer.get(),
+ mAlignedImage.buffer.get() +
+ mAlignedImage.width * mAlignedImage.height,
+ mAlignedImage.width, mAlignedImage.height,
+ mAlignedImage.width, mAlignedImage.width / 2, JPEG_QUALITY,
+ NULL, 0));
ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
}
TEST_F(JpegEncoderHelperTest, encodeUnalignedImage) {
JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(), mUnalignedImage.width,
- mUnalignedImage.height, JPEG_QUALITY, NULL, 0));
+ EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(),
+ mUnalignedImage.buffer.get() +
+ mUnalignedImage.width * mUnalignedImage.height,
+ mUnalignedImage.width, mUnalignedImage.height,
+ mUnalignedImage.width, mUnalignedImage.width / 2,
+ JPEG_QUALITY, NULL, 0));
ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
}
TEST_F(JpegEncoderHelperTest, encodeSingleChannelImage) {
JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), mSingleChannelImage.width,
- mSingleChannelImage.height, JPEG_QUALITY, NULL, 0, true));
+ EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), nullptr,
+ mSingleChannelImage.width, mSingleChannelImage.height,
+ mSingleChannelImage.width, 0, JPEG_QUALITY, NULL, 0));
ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
}
-} // namespace android::ultrahdr
-
+} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
index 41d55ec497..a75086755a 100644
--- a/libs/ultrahdr/tests/jpegr_test.cpp
+++ b/libs/ultrahdr/tests/jpegr_test.cpp
@@ -14,810 +14,1232 @@
* limitations under the License.
*/
+#include <sys/time.h>
+#include <fstream>
+#include <iostream>
+
+#include <ultrahdr/gainmapmath.h>
#include <ultrahdr/jpegr.h>
#include <ultrahdr/jpegrutils.h>
-#include <ultrahdr/gainmapmath.h>
-#include <fcntl.h>
-#include <fstream>
+
#include <gtest/gtest.h>
-#include <sys/time.h>
#include <utils/Log.h>
-#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
-#define RAW_P010_IMAGE_WITH_STRIDE "/sdcard/Documents/raw_p010_image_with_stride.p010"
-#define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420"
-#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
-#define TEST_IMAGE_WIDTH 1280
-#define TEST_IMAGE_HEIGHT 720
-#define TEST_IMAGE_STRIDE 1288
-#define DEFAULT_JPEG_QUALITY 90
-
-#define SAVE_ENCODING_RESULT true
-#define SAVE_DECODING_RESULT true
-#define SAVE_INPUT_RGBA true
+//#define DUMP_OUTPUT
namespace android::ultrahdr {
-struct Timer {
- struct timeval StartingTime;
- struct timeval EndingTime;
- struct timeval ElapsedMicroseconds;
+// resources used by unit tests
+const char* kYCbCrP010FileName = "raw_p010_image.p010";
+const char* kYCbCr420FileName = "raw_yuv420_image.yuv420";
+const char* kSdrJpgFileName = "jpeg_image.jpg";
+const int kImageWidth = 1280;
+const int kImageHeight = 720;
+const int kQuality = 90;
+
+// Wrapper to describe the input type
+typedef enum {
+ YCbCr_p010 = 0,
+ YCbCr_420 = 1,
+} UhdrInputFormat;
+
+/**
+ * Wrapper class for raw resource
+ * Sample usage:
+ * UhdrUnCompressedStructWrapper rawImg(width, height, YCbCr_p010);
+ * rawImg.setImageColorGamut(colorGamut));
+ * rawImg.setImageStride(strideLuma, strideChroma); // optional
+ * rawImg.setChromaMode(false); // optional
+ * rawImg.allocateMemory();
+ * rawImg.loadRawResource(kYCbCrP010FileName);
+ */
+class UhdrUnCompressedStructWrapper {
+public:
+ UhdrUnCompressedStructWrapper(uint32_t width, uint32_t height, UhdrInputFormat format);
+ ~UhdrUnCompressedStructWrapper() = default;
+
+ bool setChromaMode(bool isChromaContiguous);
+ bool setImageStride(int lumaStride, int chromaStride);
+ bool setImageColorGamut(ultrahdr_color_gamut colorGamut);
+ bool allocateMemory();
+ bool loadRawResource(const char* fileName);
+ jr_uncompressed_ptr getImageHandle();
+
+private:
+ std::unique_ptr<uint8_t[]> mLumaData;
+ std::unique_ptr<uint8_t[]> mChromaData;
+ jpegr_uncompressed_struct mImg;
+ UhdrInputFormat mFormat;
+ bool mIsChromaContiguous;
};
-void timerStart(Timer *t) {
- gettimeofday(&t->StartingTime, nullptr);
-}
+/**
+ * Wrapper class for compressed resource
+ * Sample usage:
+ * UhdrCompressedStructWrapper jpgImg(width, height);
+ * rawImg.allocateMemory();
+ */
+class UhdrCompressedStructWrapper {
+public:
+ UhdrCompressedStructWrapper(uint32_t width, uint32_t height);
+ ~UhdrCompressedStructWrapper() = default;
-void timerStop(Timer *t) {
- gettimeofday(&t->EndingTime, nullptr);
-}
+ bool allocateMemory();
+ jr_compressed_ptr getImageHandle();
-int64_t elapsedTime(Timer *t) {
- t->ElapsedMicroseconds.tv_sec = t->EndingTime.tv_sec - t->StartingTime.tv_sec;
- t->ElapsedMicroseconds.tv_usec = t->EndingTime.tv_usec - t->StartingTime.tv_usec;
- return t->ElapsedMicroseconds.tv_sec * 1000000 + t->ElapsedMicroseconds.tv_usec;
-}
+private:
+ std::unique_ptr<uint8_t[]> mData;
+ jpegr_compressed_struct mImg{};
+ uint32_t mWidth;
+ uint32_t mHeight;
+};
-static size_t getFileSize(int fd) {
- struct stat st;
- if (fstat(fd, &st) < 0) {
- ALOGW("%s : fstat failed", __func__);
- return 0;
- }
- return st.st_size; // bytes
+UhdrUnCompressedStructWrapper::UhdrUnCompressedStructWrapper(uint32_t width, uint32_t height,
+ UhdrInputFormat format) {
+ mImg.data = nullptr;
+ mImg.width = width;
+ mImg.height = height;
+ mImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ mImg.chroma_data = nullptr;
+ mImg.luma_stride = 0;
+ mImg.chroma_stride = 0;
+ mFormat = format;
+ mIsChromaContiguous = true;
}
-static bool loadFile(const char filename[], void*& result, int* fileLength) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
+bool UhdrUnCompressedStructWrapper::setChromaMode(bool isChromaContiguous) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
return false;
}
- int length = getFileSize(fd);
- if (length == 0) {
- close(fd);
+ mIsChromaContiguous = isChromaContiguous;
+ return true;
+}
+
+bool UhdrUnCompressedStructWrapper::setImageStride(int lumaStride, int chromaStride) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
return false;
}
- if (fileLength != nullptr) {
- *fileLength = length;
+ if (lumaStride != 0) {
+ if (lumaStride < mImg.width) {
+ std::cerr << "Bad luma stride received" << std::endl;
+ return false;
+ }
+ mImg.luma_stride = lumaStride;
}
- result = malloc(length);
- if (read(fd, result, length) != static_cast<ssize_t>(length)) {
- close(fd);
- return false;
+ if (chromaStride != 0) {
+ if (mFormat == YCbCr_p010 && chromaStride < mImg.width) {
+ std::cerr << "Bad chroma stride received for format YCbCrP010" << std::endl;
+ return false;
+ }
+ if (mFormat == YCbCr_420 && chromaStride < (mImg.width >> 1)) {
+ std::cerr << "Bad chroma stride received for format YCbCr420" << std::endl;
+ return false;
+ }
+ mImg.chroma_stride = chromaStride;
}
- close(fd);
return true;
}
-static bool loadP010Image(const char *filename, jr_uncompressed_ptr img,
- bool isUVContiguous) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
+bool UhdrUnCompressedStructWrapper::setImageColorGamut(ultrahdr_color_gamut colorGamut) {
+ if (mLumaData.get() != nullptr) {
+ std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
return false;
}
- const int bpp = 2;
- int lumaStride = img->luma_stride == 0 ? img->width : img->luma_stride;
- int lumaSize = bpp * lumaStride * img->height;
- int chromaSize = bpp * (img->height / 2) *
- (isUVContiguous ? lumaStride : img->chroma_stride);
- img->data = malloc(lumaSize + (isUVContiguous ? chromaSize : 0));
- if (img->data == nullptr) {
- ALOGE("loadP010Image(): failed to allocate memory for luma data.");
+ mImg.colorGamut = colorGamut;
+ return true;
+}
+
+bool UhdrUnCompressedStructWrapper::allocateMemory() {
+ if (mImg.width == 0 || (mImg.width % 2 != 0) || mImg.height == 0 || (mImg.height % 2 != 0) ||
+ (mFormat != YCbCr_p010 && mFormat != YCbCr_420)) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
return false;
}
- uint8_t *mem = static_cast<uint8_t *>(img->data);
- for (int i = 0; i < img->height; i++) {
- if (read(fd, mem, img->width * bpp) != img->width * bpp) {
- close(fd);
+ int lumaStride = mImg.luma_stride == 0 ? mImg.width : mImg.luma_stride;
+ int lumaSize = lumaStride * mImg.height * (mFormat == YCbCr_p010 ? 2 : 1);
+ int chromaSize = (mImg.height >> 1) * (mFormat == YCbCr_p010 ? 2 : 1);
+ if (mIsChromaContiguous) {
+ chromaSize *= lumaStride;
+ } else {
+ if (mImg.chroma_stride == 0) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
return false;
}
- mem += lumaStride * bpp;
- }
- int chromaStride = lumaStride;
- if (!isUVContiguous) {
- img->chroma_data = malloc(chromaSize);
- if (img->chroma_data == nullptr) {
- ALOGE("loadP010Image(): failed to allocate memory for chroma data.");
- return false;
+ if (mFormat == YCbCr_p010) {
+ chromaSize *= mImg.chroma_stride;
+ } else {
+ chromaSize *= (mImg.chroma_stride * 2);
}
- mem = static_cast<uint8_t *>(img->chroma_data);
- chromaStride = img->chroma_stride;
}
- for (int i = 0; i < img->height / 2; i++) {
- if (read(fd, mem, img->width * bpp) != img->width * bpp) {
- close(fd);
- return false;
- }
- mem += chromaStride * bpp;
+ if (mIsChromaContiguous) {
+ mLumaData = std::make_unique<uint8_t[]>(lumaSize + chromaSize);
+ mImg.data = mLumaData.get();
+ mImg.chroma_data = nullptr;
+ } else {
+ mLumaData = std::make_unique<uint8_t[]>(lumaSize);
+ mImg.data = mLumaData.get();
+ mChromaData = std::make_unique<uint8_t[]>(chromaSize);
+ mImg.chroma_data = mChromaData.get();
}
- close(fd);
return true;
}
-class JpegRTest : public testing::Test {
-public:
- JpegRTest();
- ~JpegRTest();
-
-protected:
- virtual void SetUp();
- virtual void TearDown();
-
- struct jpegr_uncompressed_struct mRawP010Image{};
- struct jpegr_uncompressed_struct mRawP010ImageWithStride{};
- struct jpegr_uncompressed_struct mRawP010ImageWithChromaData{};
- struct jpegr_uncompressed_struct mRawYuv420Image{};
- struct jpegr_compressed_struct mJpegImage{};
-};
-
-JpegRTest::JpegRTest() {}
-JpegRTest::~JpegRTest() {}
-
-void JpegRTest::SetUp() {}
-void JpegRTest::TearDown() {
- free(mRawP010Image.data);
- free(mRawP010Image.chroma_data);
- free(mRawP010ImageWithStride.data);
- free(mRawP010ImageWithStride.chroma_data);
- free(mRawP010ImageWithChromaData.data);
- free(mRawP010ImageWithChromaData.chroma_data);
- free(mRawYuv420Image.data);
- free(mJpegImage.data);
+bool UhdrUnCompressedStructWrapper::loadRawResource(const char* fileName) {
+ if (!mImg.data) {
+ std::cerr << "memory is not allocated, read not possible" << std::endl;
+ return false;
+ }
+ std::ifstream ifd(fileName, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ int bpp = mFormat == YCbCr_p010 ? 2 : 1;
+ int size = ifd.tellg();
+ int length = mImg.width * mImg.height * bpp * 3 / 2; // 2x2 subsampling
+ if (size < length) {
+ std::cerr << "requested to read " << length << " bytes from file : " << fileName
+ << ", file contains only " << length << " bytes" << std::endl;
+ return false;
+ }
+ ifd.seekg(0, std::ios::beg);
+ int lumaStride = mImg.luma_stride == 0 ? mImg.width : mImg.luma_stride;
+ char* mem = static_cast<char*>(mImg.data);
+ for (int i = 0; i < mImg.height; i++) {
+ ifd.read(mem, mImg.width * bpp);
+ mem += lumaStride * bpp;
+ }
+ if (!mIsChromaContiguous) {
+ mem = static_cast<char*>(mImg.chroma_data);
+ }
+ int chromaStride;
+ if (mIsChromaContiguous) {
+ chromaStride = mFormat == YCbCr_p010 ? lumaStride : lumaStride / 2;
+ } else {
+ if (mFormat == YCbCr_p010) {
+ chromaStride = mImg.chroma_stride == 0 ? lumaStride : mImg.chroma_stride;
+ } else {
+ chromaStride = mImg.chroma_stride == 0 ? (lumaStride / 2) : mImg.chroma_stride;
+ }
+ }
+ if (mFormat == YCbCr_p010) {
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, mImg.width * 2);
+ mem += chromaStride * 2;
+ }
+ } else {
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, (mImg.width / 2));
+ mem += chromaStride;
+ }
+ for (int i = 0; i < mImg.height / 2; i++) {
+ ifd.read(mem, (mImg.width / 2));
+ mem += chromaStride;
+ }
+ }
+ return true;
+ }
+ std::cerr << "unable to open file : " << fileName << std::endl;
+ return false;
}
-class JpegRBenchmark : public JpegR {
-public:
- void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
- void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
-private:
- const int kProfileCount = 10;
-};
-
-void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
- jr_uncompressed_ptr p010Image,
- ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr map) {
- ASSERT_EQ(yuv420Image->width, p010Image->width);
- ASSERT_EQ(yuv420Image->height, p010Image->height);
+jr_uncompressed_ptr UhdrUnCompressedStructWrapper::getImageHandle() {
+ return &mImg;
+}
- Timer genRecMapTime;
+UhdrCompressedStructWrapper::UhdrCompressedStructWrapper(uint32_t width, uint32_t height) {
+ mWidth = width;
+ mHeight = height;
+}
- timerStart(&genRecMapTime);
- for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK, generateGainMap(
- yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, metadata, map));
- if (i != kProfileCount - 1) delete[] static_cast<uint8_t *>(map->data);
+bool UhdrCompressedStructWrapper::allocateMemory() {
+ if (mWidth == 0 || (mWidth % 2 != 0) || mHeight == 0 || (mHeight % 2 != 0)) {
+ std::cerr << "Object in bad state, mem alloc failed" << std::endl;
+ return false;
}
- timerStop(&genRecMapTime);
-
- ALOGE("Generate Gain Map:- Res = %i x %i, time = %f ms",
- yuv420Image->width, yuv420Image->height,
- elapsedTime(&genRecMapTime) / (kProfileCount * 1000.f));
-
+ int maxLength = std::max(8 * 1024 /* min size 8kb */, (int)(mWidth * mHeight * 3 * 2));
+ mData = std::make_unique<uint8_t[]>(maxLength);
+ mImg.data = mData.get();
+ mImg.length = 0;
+ mImg.maxLength = maxLength;
+ return true;
}
-void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image,
- jr_uncompressed_ptr map,
- ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr dest) {
- Timer applyRecMapTime;
+jr_compressed_ptr UhdrCompressedStructWrapper::getImageHandle() {
+ return &mImg;
+}
- timerStart(&applyRecMapTime);
- for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK, applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
- metadata->maxContentBoost /* displayBoost */, dest));
+static bool writeFile(const char* filename, void*& result, int length) {
+ std::ofstream ofd(filename, std::ios::binary);
+ if (ofd.is_open()) {
+ ofd.write(static_cast<char*>(result), length);
+ return true;
}
- timerStop(&applyRecMapTime);
-
- ALOGE("Apply Gain Map:- Res = %i x %i, time = %f ms",
- yuv420Image->width, yuv420Image->height,
- elapsedTime(&applyRecMapTime) / (kProfileCount * 1000.f));
+ std::cerr << "unable to write to file : " << filename << std::endl;
+ return false;
}
-TEST_F(JpegRTest, build) {
- // Force all of the gain map lib to be linked by calling all public functions.
- JpegR jpegRCodec;
- jpegRCodec.encodeJPEGR(nullptr, static_cast<ultrahdr_transfer_function>(0), nullptr, 0, nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0),
- nullptr, 0, nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0),
- nullptr);
- jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<ultrahdr_transfer_function>(0), nullptr);
- jpegRCodec.decodeJPEGR(nullptr, nullptr);
+static bool readFile(const char* fileName, void*& result, int maxLength, int& length) {
+ std::ifstream ifd(fileName, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ length = ifd.tellg();
+ if (length > maxLength) {
+ std::cerr << "not enough space to read file" << std::endl;
+ return false;
+ }
+ ifd.seekg(0, std::ios::beg);
+ ifd.read(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to read file : " << fileName << std::endl;
+ return false;
}
-/* Test Encode API-0 invalid arguments */
-TEST_F(JpegRTest, encodeAPI0ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // test quality factor
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- -1, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- 101, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
+void decodeJpegRImg(jr_compressed_ptr img, [[maybe_unused]] const char* outFileName) {
+ std::vector<uint8_t> iccData(0);
+ std::vector<uint8_t> exifData(0);
+ jpegr_info_struct info{0, 0, &iccData, &exifData};
+ JpegR jpegHdr;
+ ASSERT_EQ(OK, jpegHdr.getJPEGRInfo(img, &info));
+ ASSERT_EQ(kImageWidth, info.width);
+ ASSERT_EQ(kImageHeight, info.height);
+ size_t outSize = info.width * info.height * 8;
+ std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
+ jpegr_uncompressed_struct destImage{};
+ destImage.data = data.get();
+ ASSERT_EQ(OK, jpegHdr.decodeJPEGR(img, &destImage));
+ ASSERT_EQ(kImageWidth, destImage.width);
+ ASSERT_EQ(kImageHeight, destImage.height);
+#ifdef DUMP_OUTPUT
+ if (!writeFile(outFileName, destImage.data, outSize)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+}
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
+// ============================================================================
+// Unit Tests
+// ============================================================================
+
+// Test Encode API-0 invalid arguments
+TEST(JpegRTest, EncodeAPI0WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), -1, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), 101, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr dest";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
+ nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
// test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr p010 image";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride";
-
- mRawP010ImageWithStride.chroma_data = nullptr;
-
- free(jpegR.data);
+ {
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
+
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1)));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+ }
+
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
}
/* Test Encode API-1 invalid arguments */
-TEST_F(JpegRTest, encodeAPI1ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawYuv420Image.data = malloc(16);
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- // test quality factor
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, -1, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, 101, nullptr)) << "fail, API allows bad jpeg quality factor";
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function";
+TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), -1, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), 101, nullptr),
+ OK)
+ << "fail, API allows bad jpeg quality factor";
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- nullptr, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr dest";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
+ nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
// test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr p010 image";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride";
+ {
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
+
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
// test 420 input
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = nullptr;
- mRawP010ImageWithStride.chroma_stride = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr for 420 image";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 image width";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH - 2;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 image height";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = TEST_IMAGE_STRIDE;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride for 420";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = mRawYuv420Image.data;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows chroma pointer for 420";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = nullptr;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 color gamut";
-
- mRawYuv420Image.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 color gamut";
-
- free(jpegR.data);
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr 420 image";
+
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows nullptr 420 image";
+ }
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad 420 color gamut";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad 420 color gamut";
+
+ rawImg420->width = kWidth - 1;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = 0;
+ rawImg420->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image width for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad luma stride for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = 0;
+ rawImg420->chroma_data = rawImgP010->data;
+ rawImg420->chroma_stride = kWidth / 2 - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK)
+ << "fail, API allows bad chroma stride for 420";
+ }
}
/* Test Encode API-2 invalid arguments */
-TEST_F(JpegRTest, encodeAPI2ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawYuv420Image.data = malloc(16);
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- static_cast<ultrahdr_transfer_function>(-10),
- &jpegR)) << "fail, API allows bad hdr transfer function";
+TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr)) << "fail, API allows nullptr dest";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
+
+ // test compressed image
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ }
// test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr p010 image";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad luma stride";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad chroma stride";
+ {
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
+
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
// test 420 input
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = nullptr;
- mRawP010ImageWithStride.chroma_stride = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr for 420 image";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 image width";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH - 2;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 image height";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = TEST_IMAGE_STRIDE;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad luma stride for 420";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = mRawYuv420Image.data;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows chroma pointer for 420";
-
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.luma_stride = 0;
- mRawYuv420Image.chroma_data = nullptr;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
-
- mRawYuv420Image.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
-
- // bad compressed image
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &mRawYuv420Image, nullptr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
-
- free(jpegR.data);
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr 420 image";
+
+ UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
+ jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr 420 image";
+ }
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+ UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ auto rawImg420 = rawImg2.getImageHandle();
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad 420 color gamut";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad 420 color gamut";
+
+ rawImg420->width = kWidth - 1;
+ rawImg420->height = kHeight;
+ rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = 0;
+ rawImg420->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad luma stride for 420";
+
+ rawImg420->width = kWidth;
+ rawImg420->height = kHeight;
+ rawImg420->luma_stride = 0;
+ rawImg420->chroma_data = rawImgP010->data;
+ rawImg420->chroma_stride = kWidth / 2 - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma stride for 420";
+ }
}
/* Test Encode API-3 invalid arguments */
-TEST_F(JpegRTest, encodeAPI3ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- mRawP010ImageWithStride.data = malloc(16);
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- // test hdr transfer function
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR,
- static_cast<ultrahdr_transfer_function>(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- &jpegR)) << "fail, API allows bad hdr transfer function";
-
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, static_cast<ultrahdr_transfer_function>(-10),
- &jpegR)) << "fail, API allows bad hdr transfer function";
+TEST(JpegRTest, EncodeAPI3WithInvalidArgs) {
+ JpegR uHdrLib;
+
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+
+ // test quality factor and transfer function
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(
+ ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ static_cast<ultrahdr_transfer_function>(-10),
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad hdr transfer function";
+ }
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- nullptr)) << "fail, API allows nullptr dest";
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
+ }
+
+ // test compressed image
+ {
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr for compressed image";
+ }
// test p010 input
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows nullptr p010 image";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = static_cast<ultrahdr_color_gamut>(
- ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad p010 color gamut";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = 0;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image width";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = 0;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad image height";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad luma stride";
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data;
- mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad chroma stride";
- mRawP010ImageWithStride.chroma_data = nullptr;
-
- // bad compressed image
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR)) << "fail, API allows bad 420 color gamut";
-
- free(jpegR.data);
+ {
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+
+ UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr p010 image";
+ }
+
+ {
+ const int kWidth = 32, kHeight = 32;
+ UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ auto rawImgP010 = rawImg.getImageHandle();
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut =
+ static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad p010 color gamut";
+
+ rawImgP010->width = kWidth - 1;
+ rawImgP010->height = kHeight;
+ rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight - 1;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = 0;
+ rawImgP010->height = kHeight;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image width";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = 0;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad image height";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad luma stride";
+
+ rawImgP010->width = kWidth;
+ rawImgP010->height = kHeight;
+ rawImgP010->luma_stride = kWidth + 64;
+ rawImgP010->chroma_data = rawImgP010->data;
+ rawImgP010->chroma_stride = kWidth - 2;
+ ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad chroma stride";
+ }
}
/* Test Encode API-4 invalid arguments */
-TEST_F(JpegRTest, encodeAPI4ForInvalidArgs) {
- int ret;
-
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- JpegR jpegRCodec;
+TEST(JpegRTest, EncodeAPI4WithInvalidArgs) {
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgImg2(16, 16);
+ JpegR uHdrLib;
// test dest
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, &jpegR, nullptr, nullptr)) << "fail, API allows nullptr dest";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr, nullptr),
+ OK)
+ << "fail, API allows nullptr dest";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr,
+ jpgImg2.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr dest";
// test primary image
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- nullptr, &jpegR, nullptr, &jpegR)) << "fail, API allows nullptr primary image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(), nullptr, jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr primary image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg2.getImageHandle(), jpgImg.getImageHandle(), nullptr,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr primary image";
// test gain map
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, nullptr, &jpegR)) << "fail, API allows nullptr gainmap image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), nullptr, nullptr, jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr gain map image";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg2.getImageHandle(), nullptr,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows nullptr gain map image";
// test metadata
ultrahdr_metadata_struct good_metadata;
@@ -832,82 +1254,95 @@ TEST_F(JpegRTest, encodeAPI4ForInvalidArgs) {
ultrahdr_metadata_struct metadata = good_metadata;
metadata.version = "1.1";
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata version";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata version";
metadata = good_metadata;
metadata.minContentBoost = 3.0f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata content boost";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata content boost";
metadata = good_metadata;
metadata.gamma = -0.1f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata gamma";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata gamma";
metadata = good_metadata;
metadata.offsetSdr = -0.1f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata offset sdr";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata offset sdr";
metadata = good_metadata;
metadata.offsetHdr = -0.1f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata offset hdr";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata offset hdr";
metadata = good_metadata;
metadata.hdrCapacityMax = 0.5f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata hdr capacity max";
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata hdr capacity max";
metadata = good_metadata;
metadata.hdrCapacityMin = 0.5f;
- EXPECT_NE(OK, jpegRCodec.encodeJPEGR(
- &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata hdr capacity min";
-
- free(jpegR.data);
+ ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
+ jpgImg.getImageHandle()),
+ OK)
+ << "fail, API allows bad metadata hdr capacity min";
}
/* Test Decode API invalid arguments */
-TEST_F(JpegRTest, decodeAPIForInvalidArgs) {
- int ret;
+TEST(JpegRTest, DecodeAPIWithInvalidArgs) {
+ JpegR uHdrLib;
- // we are not really compressing anything so lets keep allocs to a minimum
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = 16 * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
-
- // we are not really decoding anything so lets keep allocs to a minimum
- mRawP010Image.data = malloc(16);
-
- JpegR jpegRCodec;
+ UhdrCompressedStructWrapper jpgImg(16, 16);
+ jpegr_uncompressed_struct destImage{};
+ size_t outSize = 16 * 16 * 8;
+ std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
+ destImage.data = data.get();
// test jpegr image
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- nullptr, &mRawP010Image)) << "fail, API allows nullptr for jpegr img";
+ ASSERT_NE(uHdrLib.decodeJPEGR(nullptr, &destImage), OK)
+ << "fail, API allows nullptr for jpegr img";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
+ << "fail, API allows nullptr for jpegr img";
+ ASSERT_TRUE(jpgImg.allocateMemory());
// test dest image
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, nullptr)) << "fail, API allows nullptr for dest";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), nullptr), OK)
+ << "fail, API allows nullptr for dest";
+ destImage.data = nullptr;
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
+ << "fail, API allows nullptr for dest";
+ destImage.data = data.get();
// test max display boost
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5)) << "fail, API allows invalid max display boost";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, 0.5), OK)
+ << "fail, API allows invalid max display boost";
// test output format
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5, nullptr,
- static_cast<ultrahdr_output_format>(-1))) << "fail, API allows invalid output format";
-
- EXPECT_NE(OK, jpegRCodec.decodeJPEGR(
- &jpegR, &mRawP010Image, 0.5, nullptr,
- static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)))
- << "fail, API allows invalid output format";
-
- free(jpegR.data);
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
+ static_cast<ultrahdr_output_format>(-1)),
+ OK)
+ << "fail, API allows invalid output format";
+ ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
+ static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)),
+ OK)
+ << "fail, API allows invalid output format";
}
-TEST_F(JpegRTest, writeXmpThenRead) {
+TEST(JpegRTest, writeXmpThenRead) {
ultrahdr_metadata_struct metadata_expected;
metadata_expected.version = "1.0";
metadata_expected.maxContentBoost = 1.25f;
@@ -918,16 +1353,16 @@ TEST_F(JpegRTest, writeXmpThenRead) {
metadata_expected.hdrCapacityMin = 1.0f;
metadata_expected.hdrCapacityMax = metadata_expected.maxContentBoost;
const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
std::string xmp = generateXmpForSecondaryImage(metadata_expected);
std::vector<uint8_t> xmpData;
xmpData.reserve(nameSpaceLength + xmp.size());
xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(nameSpace.c_str()),
- reinterpret_cast<const uint8_t*>(nameSpace.c_str()) + nameSpaceLength);
+ reinterpret_cast<const uint8_t*>(nameSpace.c_str()) + nameSpaceLength);
xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
- reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
+ reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
ultrahdr_metadata_struct metadata_read;
EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
@@ -940,436 +1375,661 @@ TEST_F(JpegRTest, writeXmpThenRead) {
EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMax, metadata_read.hdrCapacityMax);
}
-/* Test Encode API-0 */
-TEST_F(JpegRTest, encodeFromP010) {
- int ret;
-
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010Image, true)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
-
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH + 128;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithStride, true)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
-
- jpegr_compressed_struct jpegRWithStride;
- jpegRWithStride.maxLength = jpegR.length;
- jpegRWithStride.data = malloc(jpegRWithStride.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegRWithStride,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- ASSERT_EQ(jpegR.length, jpegRWithStride.length)
- << "Same input is yielding different output";
- ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithStride.data, jpegR.length))
- << "Same input is yielding different output";
-
- mRawP010ImageWithChromaData.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithChromaData.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithChromaData.luma_stride = TEST_IMAGE_WIDTH + 64;
- mRawP010ImageWithChromaData.chroma_stride = TEST_IMAGE_WIDTH + 256;
- mRawP010ImageWithChromaData.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- // Load input files.
- if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithChromaData, false)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- jpegr_compressed_struct jpegRWithChromaData;
- jpegRWithChromaData.maxLength = jpegR.length;
- jpegRWithChromaData.data = malloc(jpegRWithChromaData.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithChromaData, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegRWithChromaData, DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- ASSERT_EQ(jpegR.length, jpegRWithChromaData.length)
- << "Same input is yielding different output";
- ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithChromaData.data, jpegR.length))
- << "Same input is yielding different output";
-
- free(jpegR.data);
- free(jpegRWithStride.data);
- free(jpegRWithChromaData.data);
-}
+class JpegRAPIEncodeAndDecodeTest
+ : public ::testing::TestWithParam<std::tuple<ultrahdr_color_gamut, ultrahdr_color_gamut>> {
+public:
+ JpegRAPIEncodeAndDecodeTest()
+ : mP010ColorGamut(std::get<0>(GetParam())), mYuv420ColorGamut(std::get<1>(GetParam())){};
-/* Test Encode API-0 and decode */
-TEST_F(JpegRTest, encodeFromP010ThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY,
- nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ const ultrahdr_color_gamut mP010ColorGamut;
+ const ultrahdr_color_gamut mYuv420ColorGamut;
+};
+
+/* Test Encode API-0 and Decode */
+TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
+ // reference encode
+ UhdrUnCompressedStructWrapper rawImg(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg.allocateMemory());
+ ASSERT_TRUE(rawImg.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 18, 0));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 18, kImageWidth + 28));
+ ASSERT_TRUE(rawImg2.setChromaMode(false));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2.setImageStride(0, kImageWidth + 34));
+ ASSERT_TRUE(rawImg2.setChromaMode(false));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set but no chroma ptr
+ {
+ UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2.setImageStride(kImageWidth, kImageWidth + 38));
+ ASSERT_TRUE(rawImg2.allocateMemory());
+ ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- free(jpegR.data);
- free(decodedJpegR.data);
+ auto jpg1 = jpgImg.getImageHandle();
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api0_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
+ }
+#endif
+
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api0_output.rgb"));
}
-/* Test Encode API-0 (with stride) and decode */
-TEST_F(JpegRTest, encodeFromP010WithStrideThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE_WITH_STRIDE, mRawP010ImageWithStride.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE_WITH_STRIDE << " failed";
- }
- mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH;
- mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT;
- mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE;
- mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+/* Test Encode API-1 and Decode */
+TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle(), kQuality, nullptr),
+ OK);
+ // encode with luma stride set p010
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set p010
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set p010
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set but no chroma ptr p010
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 64, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma stride set 420
+ {
+ UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 14, 0));
+ ASSERT_TRUE(rawImg2420.allocateMemory());
+ ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set 420
+ {
+ UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 46, kImageWidth / 2 + 34));
+ ASSERT_TRUE(rawImg2420.setChromaMode(false));
+ ASSERT_TRUE(rawImg2420.allocateMemory());
+ ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set 420
+ {
+ UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth / 2 + 38));
+ ASSERT_TRUE(rawImg2420.setChromaMode(false));
+ ASSERT_TRUE(rawImg2420.allocateMemory());
+ ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set but no chroma ptr 420
+ {
+ UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 26, kImageWidth / 2 + 44));
+ ASSERT_TRUE(rawImg2420.allocateMemory());
+ ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle(), kQuality, nullptr),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- free(jpegR.data);
- free(decodedJpegR.data);
-}
+ auto jpg1 = jpgImg.getImageHandle();
-/* Test Encode API-1 and decode */
-TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR,
- DEFAULT_JPEG_QUALITY, nullptr);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api1_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
}
+#endif
- free(jpegR.data);
- free(decodedJpegR.data);
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api1_output.rgb"));
}
-/* Test Encode API-2 and decode */
-TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+/* Test Encode API-2 and Decode */
+TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgSdr(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgSdr.allocateMemory());
+ auto sdr = jpgSdr.getImageHandle();
+ ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2420.allocateMemory());
+ ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2420.setChromaMode(false));
+ ASSERT_TRUE(rawImg2420.allocateMemory());
+ ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
+ ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2420.setChromaMode(false));
+ ASSERT_TRUE(rawImg2420.allocateMemory());
+ ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- mRawYuv420Image.width = TEST_IMAGE_WIDTH;
- mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
- if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
- FAIL() << "Load file " << JPEG_IMAGE << " failed";
+ auto jpg1 = jpgImg.getImageHandle();
+
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api2_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
}
- mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
+#endif
- JpegR jpegRCodec;
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api2_output.rgb"));
+}
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mRawYuv420Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- &jpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
+/* Test Encode API-3 and Decode */
+TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg.allocateMemory());
+ UhdrCompressedStructWrapper jpgSdr(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgSdr.allocateMemory());
+ auto sdr = jpgSdr.getImageHandle();
+ ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
+ JpegR uHdrLib;
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg.getImageHandle()),
+ OK);
+ // encode with luma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
}
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_jpeg_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ // encode with luma and chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with chroma stride set
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
+ ASSERT_TRUE(rawImg2P010.setChromaMode(false));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+ // encode with luma and chroma stride set and no chroma ptr
+ {
+ UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
+ ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 32, kImageWidth + 256));
+ ASSERT_TRUE(rawImg2P010.allocateMemory());
+ ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
+ UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
+ ASSERT_TRUE(jpgImg2.allocateMemory());
+ ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
+ ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ jpgImg2.getImageHandle()),
+ OK);
+ auto jpg1 = jpgImg.getImageHandle();
+ auto jpg2 = jpgImg2.getImageHandle();
+ ASSERT_EQ(jpg1->length, jpg2->length);
+ ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
+ }
+
+ auto jpg1 = jpgImg.getImageHandle();
+
+#ifdef DUMP_OUTPUT
+ if (!writeFile("encode_api3_output.jpeg", jpg1->data, jpg1->length)) {
+ std::cerr << "unable to write output file" << std::endl;
}
+#endif
- free(jpegR.data);
- free(decodedJpegR.data);
+ ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api3_output.rgb"));
}
-/* Test Encode API-3 and decode */
-TEST_F(JpegRTest, encodeFromJpegThenDecode) {
- int ret;
-
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
- }
- mRawP010Image.width = TEST_IMAGE_WIDTH;
- mRawP010Image.height = TEST_IMAGE_HEIGHT;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
-
- if (SAVE_INPUT_RGBA) {
- size_t rgbaSize = mRawP010Image.width * mRawP010Image.height * sizeof(uint32_t);
- uint32_t *data = (uint32_t *)malloc(rgbaSize);
-
- for (size_t y = 0; y < mRawP010Image.height; ++y) {
- for (size_t x = 0; x < mRawP010Image.width; ++x) {
- Color hdr_yuv_gamma = getP010Pixel(&mRawP010Image, x, y);
- Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
- uint32_t rgba1010102 = colorToRgba1010102(hdr_rgb_gamma);
- size_t pixel_idx = x + y * mRawP010Image.width;
- reinterpret_cast<uint32_t*>(data)[pixel_idx] = rgba1010102;
- }
- }
+INSTANTIATE_TEST_SUITE_P(
+ JpegRAPIParameterizedTests, JpegRAPIEncodeAndDecodeTest,
+ ::testing::Combine(::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3,
+ ULTRAHDR_COLORGAMUT_BT2100),
+ ::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3,
+ ULTRAHDR_COLORGAMUT_BT2100)));
- // Output image data to file
- std::string filePath = "/sdcard/Documents/input_from_p010.rgb10";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)data, rgbaSize);
- free(data);
- }
- if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
- FAIL() << "Load file " << JPEG_IMAGE << " failed";
- }
- mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
-
- JpegR jpegRCodec;
-
- jpegr_compressed_struct jpegR;
- jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
- jpegR.data = malloc(jpegR.maxLength);
- ret = jpegRCodec.encodeJPEGR(
- &mRawP010Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_ENCODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_p010_jpeg_input.jpgr";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)jpegR.data, jpegR.length);
- }
-
- jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
- decodedJpegR.data = malloc(decodedJpegRSize);
- ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
- if (ret != OK) {
- FAIL() << "Error code is " << ret;
- }
- if (SAVE_DECODING_RESULT) {
- // Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb";
- std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
- if (!imageFile.is_open()) {
- ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
- }
- imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+// ============================================================================
+// Profiling
+// ============================================================================
+
+class Profiler {
+public:
+ void timerStart() { gettimeofday(&mStartingTime, nullptr); }
+
+ void timerStop() { gettimeofday(&mEndingTime, nullptr); }
+
+ int64_t elapsedTime() {
+ struct timeval elapsedMicroseconds;
+ elapsedMicroseconds.tv_sec = mEndingTime.tv_sec - mStartingTime.tv_sec;
+ elapsedMicroseconds.tv_usec = mEndingTime.tv_usec - mStartingTime.tv_usec;
+ return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
}
- free(jpegR.data);
- free(decodedJpegR.data);
-}
+private:
+ struct timeval mStartingTime;
+ struct timeval mEndingTime;
+};
+
+class JpegRBenchmark : public JpegR {
+public:
+ void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
+ ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
+ void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
+ ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
-TEST_F(JpegRTest, ProfileGainMapFuncs) {
- const size_t kWidth = TEST_IMAGE_WIDTH;
- const size_t kHeight = TEST_IMAGE_HEIGHT;
+private:
+ const int kProfileCount = 10;
+};
+
+void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
+ jr_uncompressed_ptr p010Image,
+ ultrahdr_metadata_ptr metadata,
+ jr_uncompressed_ptr map) {
+ ASSERT_EQ(yuv420Image->width, p010Image->width);
+ ASSERT_EQ(yuv420Image->height, p010Image->height);
+ Profiler profileGenerateMap;
+ profileGenerateMap.timerStart();
+ for (auto i = 0; i < kProfileCount; i++) {
+ ASSERT_EQ(OK,
+ generateGainMap(yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
+ metadata, map));
+ if (i != kProfileCount - 1) delete[] static_cast<uint8_t*>(map->data);
+ }
+ profileGenerateMap.timerStop();
+ ALOGE("Generate Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height,
+ profileGenerateMap.elapsedTime() / (kProfileCount * 1000.f));
+}
- // Load input files.
- if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
+ ultrahdr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest) {
+ Profiler profileRecMap;
+ profileRecMap.timerStart();
+ for (auto i = 0; i < kProfileCount; i++) {
+ ASSERT_EQ(OK,
+ applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
+ metadata->maxContentBoost /* displayBoost */, dest));
}
- mRawP010Image.width = kWidth;
- mRawP010Image.height = kHeight;
- mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
+ profileRecMap.timerStop();
+ ALOGE("Apply Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height,
+ profileRecMap.elapsedTime() / (kProfileCount * 1000.f));
+}
- if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
- FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+TEST(JpegRTest, ProfileGainMapFuncs) {
+ UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
+ ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
+ ASSERT_TRUE(rawImgP010.allocateMemory());
+ ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
+ UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
+ ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
+ ASSERT_TRUE(rawImg420.allocateMemory());
+ ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
+ ultrahdr_metadata_struct metadata = {.version = "1.0"};
+ jpegr_uncompressed_struct map = {.data = NULL,
+ .width = 0,
+ .height = 0,
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
+ {
+ auto rawImg = rawImgP010.getImageHandle();
+ if (rawImg->luma_stride == 0) rawImg->luma_stride = rawImg->width;
+ if (!rawImg->chroma_data) {
+ uint16_t* data = reinterpret_cast<uint16_t*>(rawImg->data);
+ rawImg->chroma_data = data + rawImg->luma_stride * rawImg->height;
+ rawImg->chroma_stride = rawImg->luma_stride;
+ }
+ }
+ {
+ auto rawImg = rawImg420.getImageHandle();
+ if (rawImg->luma_stride == 0) rawImg->luma_stride = rawImg->width;
+ if (!rawImg->chroma_data) {
+ uint8_t* data = reinterpret_cast<uint8_t*>(rawImg->data);
+ rawImg->chroma_data = data + rawImg->luma_stride * rawImg->height;
+ rawImg->chroma_stride = rawImg->luma_stride / 2;
+ }
}
- mRawYuv420Image.width = kWidth;
- mRawYuv420Image.height = kHeight;
- mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
JpegRBenchmark benchmark;
+ ASSERT_NO_FATAL_FAILURE(benchmark.BenchmarkGenerateGainMap(rawImg420.getImageHandle(),
+ rawImgP010.getImageHandle(), &metadata,
+ &map));
- ultrahdr_metadata_struct metadata = { .version = "1.0" };
-
- jpegr_uncompressed_struct map = { .data = NULL,
+ const int dstSize = kImageWidth * kImageWidth * 4;
+ auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
+ jpegr_uncompressed_struct dest = {.data = bufferDst.get(),
.width = 0,
.height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED };
-
- benchmark.BenchmarkGenerateGainMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map);
-
- const int dstSize = mRawYuv420Image.width * mRawYuv420Image.height * 4;
- auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
- jpegr_uncompressed_struct dest = { .data = bufferDst.get(),
- .width = 0,
- .height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED };
+ .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
- benchmark.BenchmarkApplyGainMap(&mRawYuv420Image, &map, &metadata, &dest);
+ ASSERT_NO_FATAL_FAILURE(
+ benchmark.BenchmarkApplyGainMap(rawImg420.getImageHandle(), &map, &metadata, &dest));
}
} // namespace android::ultrahdr
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 8d0eb590bf..04e2fffaef 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -41,24 +41,44 @@ namespace android {
/*
* EGL userspace drivers must be provided either:
* - as a single library:
- * /vendor/lib/egl/libGLES.so
+ * /vendor/${LIB}/egl/libGLES.so
*
* - as separate libraries:
- * /vendor/lib/egl/libEGL.so
- * /vendor/lib/egl/libGLESv1_CM.so
- * /vendor/lib/egl/libGLESv2.so
+ * /vendor/${LIB}/egl/libEGL.so
+ * /vendor/${LIB}/egl/libGLESv1_CM.so
+ * /vendor/${LIB}/egl/libGLESv2.so
*
* For backward compatibility and to facilitate the transition to
* this new naming scheme, the loader will additionally look for:
*
- * /{vendor|system}/lib/egl/lib{GLES | [EGL|GLESv1_CM|GLESv2]}_*.so
+ * /vendor/${LIB}/egl/lib{GLES | [EGL|GLESv1_CM|GLESv2]}_${SUFFIX}.so
*
*/
-Loader& Loader::getInstance() {
- static Loader loader;
- return loader;
-}
+#ifndef SYSTEM_LIB_PATH
+#if defined(__LP64__)
+#define SYSTEM_LIB_PATH "/system/lib64"
+#else
+#define SYSTEM_LIB_PATH "/system/lib"
+#endif
+#endif
+
+static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl";
+static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
+static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform";
+
+static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = {
+ PERSIST_DRIVER_SUFFIX_PROPERTY,
+ RO_DRIVER_SUFFIX_PROPERTY,
+ RO_BOARD_PLATFORM_PROPERTY,
+};
+
+static const char* const VENDOR_LIB_EGL_DIR =
+#if defined(__LP64__)
+ "/vendor/lib64/egl";
+#else
+ "/vendor/lib/egl";
+#endif
static void* do_dlopen(const char* path, int mode) {
ATRACE_CALL();
@@ -80,6 +100,17 @@ static int do_android_unload_sphal_library(void* dso) {
return android_unload_sphal_library(dso);
}
+static void* load_wrapper(const char* path) {
+ void* so = do_dlopen(path, RTLD_NOW | RTLD_LOCAL);
+ ALOGE_IF(!so, "dlopen(\"%s\") failed: %s", path, dlerror());
+ return so;
+}
+
+Loader& Loader::getInstance() {
+ static Loader loader;
+ return loader;
+}
+
Loader::driver_t::driver_t(void* gles)
{
dso[0] = gles;
@@ -123,30 +154,6 @@ Loader::Loader()
Loader::~Loader() {
}
-static void* load_wrapper(const char* path) {
- void* so = do_dlopen(path, RTLD_NOW | RTLD_LOCAL);
- ALOGE_IF(!so, "dlopen(\"%s\") failed: %s", path, dlerror());
- return so;
-}
-
-#ifndef EGL_WRAPPER_DIR
-#if defined(__LP64__)
-#define EGL_WRAPPER_DIR "/system/lib64"
-#else
-#define EGL_WRAPPER_DIR "/system/lib"
-#endif
-#endif
-
-static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl";
-static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
-static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform";
-
-static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = {
- PERSIST_DRIVER_SUFFIX_PROPERTY,
- RO_DRIVER_SUFFIX_PROPERTY,
- RO_BOARD_PLATFORM_PROPERTY,
-};
-
// Check whether the loaded system drivers should be unloaded in order to
// load ANGLE or the updatable graphics drivers.
// If ANGLE namespace is set, it means the application is identified to run on top of ANGLE.
@@ -169,6 +176,11 @@ static bool should_unload_system_driver(egl_connection_t* cnx) {
}
}
+ // Return true if native GLES drivers should be used and ANGLE is already loaded.
+ if (android::GraphicsEnv::getInstance().shouldUseNativeDriver() && cnx->angleLoaded) {
+ return true;
+ }
+
// Return true if updated driver namespace is set.
ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (ns) {
@@ -240,16 +252,28 @@ void* Loader::open(egl_connection_t* cnx) {
if (!hnd) {
// Secondly, try to load from driver apk.
hnd = attempt_to_load_updated_driver(cnx);
+
+ // If updated driver apk is set but fail to load, abort here.
+ LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace(),
+ "couldn't find an OpenGL ES implementation from %s",
+ android::GraphicsEnv::getInstance().getDriverPath().c_str());
}
+ // Attempt to load native GLES drivers specified by ro.hardware.egl if native is selected.
+ // If native is selected but fail to load, abort.
+ if (!hnd && android::GraphicsEnv::getInstance().shouldUseNativeDriver()) {
+ auto driverSuffix = base::GetProperty(RO_DRIVER_SUFFIX_PROPERTY, "");
+ LOG_ALWAYS_FATAL_IF(driverSuffix.empty(),
+ "Native GLES driver is selected but not specified in %s",
+ RO_DRIVER_SUFFIX_PROPERTY);
+ hnd = attempt_to_load_system_driver(cnx, driverSuffix.c_str(), true);
+ LOG_ALWAYS_FATAL_IF(!hnd, "Native GLES driver is selected but failed to load. %s=%s",
+ RO_DRIVER_SUFFIX_PROPERTY, driverSuffix.c_str());
+ }
+
+ // Finally, try to load default driver.
bool failToLoadFromDriverSuffixProperty = false;
if (!hnd) {
- // If updated driver apk is set but fail to load, abort here.
- if (android::GraphicsEnv::getInstance().getDriverNamespace()) {
- LOG_ALWAYS_FATAL("couldn't find an OpenGL ES implementation from %s",
- android::GraphicsEnv::getInstance().getDriverPath().c_str());
- }
- // Finally, try to load system driver.
// Start by searching for the library name appended by the system
// properties of the GLES userspace driver in both locations.
// i.e.:
@@ -306,13 +330,13 @@ void* Loader::open(egl_connection_t* cnx) {
HAL_SUBNAME_KEY_PROPERTIES[2]);
if (!cnx->libEgl) {
- cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
+ cnx->libEgl = load_wrapper(SYSTEM_LIB_PATH "/libEGL.so");
}
if (!cnx->libGles1) {
- cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");
+ cnx->libGles1 = load_wrapper(SYSTEM_LIB_PATH "/libGLESv1_CM.so");
}
if (!cnx->libGles2) {
- cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so");
+ cnx->libGles2 = load_wrapper(SYSTEM_LIB_PATH "/libGLESv2.so");
}
if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) {
@@ -415,31 +439,19 @@ static void* load_system_driver(const char* kind, const char* suffix, const bool
class MatchFile {
public:
static std::string find(const char* libraryName, const bool exact) {
- const char* const searchPaths[] = {
-#if defined(__LP64__)
- "/vendor/lib64/egl",
- "/system/lib64/egl"
-#else
- "/vendor/lib/egl",
- "/system/lib/egl"
-#endif
- };
-
- for (auto dir : searchPaths) {
- std::string absolutePath;
- if (find(absolutePath, libraryName, dir, exact)) {
- return absolutePath;
- }
+ std::string absolutePath;
+ if (findLibPath(absolutePath, libraryName, exact)) {
+ return absolutePath;
}
// Driver not found. gah.
return std::string();
}
private:
- static bool find(std::string& result,
- const std::string& pattern, const char* const search, bool exact) {
+ static bool findLibPath(std::string& result, const std::string& pattern, bool exact) {
+ const std::string vendorLibEglDirString = std::string(VENDOR_LIB_EGL_DIR);
if (exact) {
- std::string absolutePath = std::string(search) + "/" + pattern + ".so";
+ std::string absolutePath = vendorLibEglDirString + "/" + pattern + ".so";
if (!access(absolutePath.c_str(), R_OK)) {
result = absolutePath;
return true;
@@ -447,7 +459,7 @@ static void* load_system_driver(const char* kind, const char* suffix, const bool
return false;
}
- DIR* d = opendir(search);
+ DIR* d = opendir(VENDOR_LIB_EGL_DIR);
if (d != nullptr) {
struct dirent* e;
while ((e = readdir(d)) != nullptr) {
@@ -460,7 +472,7 @@ static void* load_system_driver(const char* kind, const char* suffix, const bool
}
if (strstr(e->d_name, pattern.c_str()) == e->d_name) {
if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {
- result = std::string(search) + "/" + e->d_name;
+ result = vendorLibEglDirString + "/" + e->d_name;
closedir(d);
return true;
}
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 4a08c11c14..48d793a4d4 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -143,7 +143,7 @@ status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<Stri
ALOGV("shellCommand");
for (size_t i = 0, n = args.size(); i < n; i++)
- ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string());
+ ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).c_str());
if (args.size() >= 1) {
if (args[0] == String16("vkjson")) return cmdVkjson(out, err);
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f749b0e5b5..d5512132be 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -87,6 +87,7 @@ cc_defaults {
srcs: [":libinputflinger_sources"],
shared_libs: [
"android.hardware.input.processor-V1-ndk",
+ "com.android.server.inputflinger-ndk",
"libbase",
"libbinder",
"libbinder_ndk",
@@ -107,6 +108,14 @@ cc_defaults {
"libpalmrejection",
"libui-types",
],
+ generated_headers: [
+ "cxx-bridge-header",
+ "inputflinger_rs_bootstrap_bridge_header",
+ ],
+ header_libs: ["inputflinger_rs_bootstrap_cxx_headers"],
+ generated_sources: ["inputflinger_rs_bootstrap_bridge_code"],
+ whole_static_libs: ["libinputflinger_rs"],
+ export_shared_lib_headers: ["com.android.server.inputflinger-ndk"],
target: {
android: {
shared_libs: [
@@ -249,7 +258,7 @@ phony {
"inputflinger_input_classifier_fuzzer",
// Java/Kotlin targets
- "CtsWindowManagerDeviceTestCases",
+ "CtsWindowManagerDeviceWindow",
"InputTests",
"CtsHardwareTestCases",
"CtsInputTestCases",
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index c90356459f..da79ae3a48 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -23,22 +23,22 @@
#include "InputReaderFactory.h"
#include "UnwantedInteractionBlocker.h"
+#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include <android/binder_interface_utils.h>
#include <android/sysprop/InputProperties.sysprop.h>
#include <binder/IPCThreadState.h>
-
+#include <inputflinger_bootstrap.rs.h>
#include <log/log.h>
-#include <unordered_map>
-
#include <private/android_filesystem_config.h>
namespace android {
-static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
- sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
+namespace {
-using gui::FocusRequest;
+const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
+ sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
-static int32_t exceptionCodeFromStatusT(status_t status) {
+int32_t exceptionCodeFromStatusT(status_t status) {
switch (status) {
case OK:
return binder::Status::EX_NONE;
@@ -57,6 +57,58 @@ static int32_t exceptionCodeFromStatusT(status_t status) {
}
}
+// Convert a binder interface into a raw pointer to an AIBinder.
+using IInputFlingerRustBootstrapCallback = aidl::com::android::server::inputflinger::
+ IInputFlingerRust::IInputFlingerRustBootstrapCallback;
+IInputFlingerRustBootstrapCallbackAIBinder* binderToPointer(
+ IInputFlingerRustBootstrapCallback& interface) {
+ ndk::SpAIBinder spAIBinder = interface.asBinder();
+ auto* ptr = spAIBinder.get();
+ AIBinder_incStrong(ptr);
+ return ptr;
+}
+
+// Create the Rust component of InputFlinger that uses AIDL interfaces as a the foreign function
+// interface (FFI). The bootstraping process for IInputFlingerRust is as follows:
+// - Create BnInputFlingerRustBootstrapCallback in C++.
+// - Use the cxxbridge ffi interface to call the Rust function `create_inputflinger_rust()`, and
+// pass the callback binder object as a raw pointer.
+// - The Rust implementation will create the implementation of IInputFlingerRust, and pass it
+// to C++ through the callback.
+// - After the Rust function returns, the binder interface provided to the callback will be the
+// only strong reference to the IInputFlingerRust.
+std::shared_ptr<IInputFlingerRust> createInputFlingerRust() {
+ using namespace aidl::com::android::server::inputflinger;
+
+ class Callback : public IInputFlingerRust::BnInputFlingerRustBootstrapCallback {
+ ndk::ScopedAStatus onProvideInputFlingerRust(
+ const std::shared_ptr<IInputFlingerRust>& inputFlingerRust) override {
+ mService = inputFlingerRust;
+ return ndk::ScopedAStatus::ok();
+ }
+
+ public:
+ std::shared_ptr<IInputFlingerRust> consumeInputFlingerRust() {
+ auto service = mService;
+ mService.reset();
+ return service;
+ }
+
+ private:
+ std::shared_ptr<IInputFlingerRust> mService;
+ };
+
+ auto callback = ndk::SharedRefBase::make<Callback>();
+ create_inputflinger_rust(binderToPointer(*callback));
+ auto service = callback->consumeInputFlingerRust();
+ LOG_ALWAYS_FATAL_IF(!service,
+ "create_inputflinger_rust did not provide the IInputFlingerRust "
+ "implementation through the callback.");
+ return service;
+}
+
+} // namespace
+
/**
* The event flow is via the "InputListener" interface, as follows:
* InputReader
@@ -67,6 +119,8 @@ static int32_t exceptionCodeFromStatusT(status_t status) {
*/
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
InputDispatcherPolicyInterface& dispatcherPolicy) {
+ mInputFlingerRust = createInputFlingerRust();
+
mDispatcher = createInputDispatcher(dispatcherPolicy);
if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
@@ -190,7 +244,7 @@ status_t InputManager::dump(int fd, const Vector<String16>& args) {
return NO_ERROR;
}
-binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
+binder::Status InputManager::setFocusedWindow(const gui::FocusRequest& request) {
mDispatcher->setFocusedWindow(request);
return binder::Status::ok();
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 9dc285f2c0..528d2aa5ca 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -30,6 +30,7 @@
#include <input/Input.h>
#include <input/InputTransport.h>
+#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
#include <android/os/BnInputFlinger.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -37,6 +38,8 @@
using android::os::BnInputFlinger;
+using aidl::com::android::server::inputflinger::IInputFlingerRust;
+
namespace android {
class InputChannel;
class InputDispatcherThread;
@@ -132,6 +135,8 @@ private:
std::unique_ptr<InputDeviceMetricsCollectorInterface> mCollector;
std::unique_ptr<InputDispatcherInterface> mDispatcher;
+
+ std::shared_ptr<IInputFlingerRust> mInputFlingerRust;
};
} // namespace android
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index ca00e7dac2..c2da5bafaf 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -1,10 +1,10 @@
{
"presubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceWindow",
"options": [
{
- "include-filter": "android.server.wm.WindowInputTests"
+ "include-filter": "android.server.wm.window.WindowInputTests"
}
]
},
@@ -150,10 +150,10 @@
],
"hwasan-postsubmit": [
{
- "name": "CtsWindowManagerDeviceTestCases",
+ "name": "CtsWindowManagerDeviceWindow",
"options": [
{
- "include-filter": "android.server.wm.WindowInputTests"
+ "include-filter": "android.server.wm.window.WindowInputTests"
}
]
},
diff --git a/services/inputflinger/aidl/Android.bp b/services/inputflinger/aidl/Android.bp
new file mode 100644
index 0000000000..314c43356d
--- /dev/null
+++ b/services/inputflinger/aidl/Android.bp
@@ -0,0 +1,31 @@
+// Copyright 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+aidl_interface {
+ name: "com.android.server.inputflinger",
+ srcs: ["**/*.aidl"],
+ unstable: true,
+ host_supported: true,
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ enabled: false,
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
new file mode 100644
index 0000000000..8e826fda44
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputflinger;
+
+/**
+ * A local AIDL interface used as a foreign function interface (ffi) to
+ * communicate with the Rust component of inputflinger.
+ *
+ * NOTE: Since we use this as a local interface, all processing happens on the
+ * calling thread.
+ */
+interface IInputFlingerRust {
+
+ /**
+ * An interface used to get a strong reference to IInputFlingerRust on boot.
+ */
+ interface IInputFlingerRustBootstrapCallback {
+ void onProvideInputFlingerRust(in IInputFlingerRust inputFlingerRust);
+ }
+}
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index ec0388d500..de99fc73ec 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -256,14 +256,14 @@ input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map,
const char* InputDriver::inputGetPropertyKey(input_property_t* property) {
if (property != nullptr) {
- return property->key.string();
+ return property->key.c_str();
}
return nullptr;
}
const char* InputDriver::inputGetPropertyValue(input_property_t* property) {
if (property != nullptr) {
- return property->value.string();
+ return property->value.c_str();
}
return nullptr;
}
@@ -281,7 +281,7 @@ void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) {
}
void InputDriver::dump(String8& result) {
- result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.string());
+ result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.c_str());
}
} // namespace android
diff --git a/services/inputflinger/host/InputFlinger.cpp b/services/inputflinger/host/InputFlinger.cpp
index 2da2a70c03..d974c43d78 100644
--- a/services/inputflinger/host/InputFlinger.cpp
+++ b/services/inputflinger/host/InputFlinger.cpp
@@ -57,7 +57,7 @@ status_t InputFlinger::dump(int fd, const Vector<String16>& args) {
} else {
dumpInternal(result);
}
- write(fd, result.string(), result.size());
+ write(fd, result.c_str(), result.size());
return OK;
}
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index a93a2ea615..25e1d21850 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -437,7 +437,8 @@ public:
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) = 0;
+ const InputDeviceIdentifier& identifier,
+ const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) = 0;
/* Gets a user-supplied alias for a particular input device, or an empty string if none. */
virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier) = 0;
@@ -447,6 +448,9 @@ public:
const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0;
/* Notifies the input reader policy that a stylus gesture has started. */
virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
+
+ /* Returns true if any InputConnection is currently active. */
+ virtual bool isInputMethodConnectionActive() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index bc4cdd1192..e69c99e9b5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -643,9 +643,6 @@ void EventHub::Device::populateAbsoluteAxisStates() {
identifier.name.c_str(), fd, strerror(errno));
continue;
}
- if (info.minimum == info.maximum) {
- continue;
- }
auto& [axisInfo, value] = absState[axis];
axisInfo.valid = true;
axisInfo.minValue = info.minimum;
@@ -778,26 +775,35 @@ void EventHub::Device::trackInputEvent(const struct input_event& event) {
LOG_ALWAYS_FATAL_IF(!currentFrameDropped &&
!keyState.set(static_cast<size_t>(event.code),
event.value != 0),
- "%s: received invalid EV_KEY event code: %s", __func__,
+ "%s: device '%s' received invalid EV_KEY event code: %s value: %d",
+ __func__, identifier.name.c_str(),
InputEventLookup::getLinuxEvdevLabel(EV_KEY, event.code, 1)
- .code.c_str());
+ .code.c_str(),
+ event.value);
break;
}
case EV_SW: {
LOG_ALWAYS_FATAL_IF(!currentFrameDropped &&
!swState.set(static_cast<size_t>(event.code),
event.value != 0),
- "%s: received invalid EV_SW event code: %s", __func__,
+ "%s: device '%s' received invalid EV_SW event code: %s value: %d",
+ __func__, identifier.name.c_str(),
InputEventLookup::getLinuxEvdevLabel(EV_SW, event.code, 1)
- .code.c_str());
+ .code.c_str(),
+ event.value);
break;
}
case EV_ABS: {
+ if (currentFrameDropped) {
+ break;
+ }
auto it = absState.find(event.code);
- LOG_ALWAYS_FATAL_IF(!currentFrameDropped && it == absState.end(),
- "%s: received invalid EV_ABS event code: %s", __func__,
+ LOG_ALWAYS_FATAL_IF(it == absState.end(),
+ "%s: device '%s' received invalid EV_ABS event code: %s value: %d",
+ __func__, identifier.name.c_str(),
InputEventLookup::getLinuxEvdevLabel(EV_ABS, event.code, 0)
- .code.c_str());
+ .code.c_str(),
+ event.value);
it->second.value = event.value;
break;
}
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 2aaddf59bb..bacc7203b6 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -225,23 +225,6 @@ std::list<NotifyArgs> InputDevice::configure(nsecs_t when,
mIsWaking = mConfiguration.getBool("device.wake").value_or(false);
}
- if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
- if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
- std::shared_ptr<KeyCharacterMap> keyboardLayout =
- mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
- bool shouldBumpGeneration = false;
- for_each_subdevice(
- [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
- if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
- shouldBumpGeneration = true;
- }
- });
- if (shouldBumpGeneration) {
- bumpGeneration();
- }
- }
- }
-
if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) {
if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index ea95f7857a..7f63355387 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -77,7 +77,7 @@ InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
: mContext(this),
mEventHub(eventHub),
mPolicy(policy),
- mQueuedListener(listener),
+ mNextListener(listener),
mGlobalMetaState(AMETA_NONE),
mLedMetaState(AMETA_NONE),
mGeneration(1),
@@ -140,7 +140,7 @@ void InputReader::loopOnce() {
mReaderIsAliveCondition.notify_all();
if (!events.empty()) {
- notifyArgs += processEventsLocked(events.data(), events.size());
+ mPendingArgs += processEventsLocked(events.data(), events.size());
}
if (mNextTimeout != LLONG_MAX) {
@@ -150,16 +150,18 @@ void InputReader::loopOnce() {
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
}
mNextTimeout = LLONG_MAX;
- notifyArgs += timeoutExpiredLocked(now);
+ mPendingArgs += timeoutExpiredLocked(now);
}
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
inputDevices = getInputDevicesLocked();
- notifyArgs.emplace_back(
+ mPendingArgs.emplace_back(
NotifyInputDevicesChangedArgs{mContext.getNextId(), inputDevices});
}
+
+ std::swap(notifyArgs, mPendingArgs);
} // release lock
// Send out a message that the describes the changed input devices.
@@ -175,8 +177,6 @@ void InputReader::loopOnce() {
}
}
- notifyAll(std::move(notifyArgs));
-
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
@@ -184,7 +184,9 @@ void InputReader::loopOnce() {
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
- mQueuedListener.flush();
+ for (const NotifyArgs& args : notifyArgs) {
+ mNextListener.notify(args);
+ }
}
std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
@@ -236,8 +238,8 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
- notifyAll(device->configure(when, mConfig, /*changes=*/{}));
- notifyAll(device->reset(when));
+ mPendingArgs += device->configure(when, mConfig, /*changes=*/{});
+ mPendingArgs += device->reset(when);
if (device->isIgnored()) {
ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
@@ -310,12 +312,10 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) {
notifyExternalStylusPresenceChangedLocked();
}
- std::list<NotifyArgs> resetEvents;
if (device->hasEventHubDevices()) {
- resetEvents += device->configure(when, mConfig, /*changes=*/{});
+ mPendingArgs += device->configure(when, mConfig, /*changes=*/{});
}
- resetEvents += device->reset(when);
- notifyAll(std::move(resetEvents));
+ mPendingArgs += device->reset(when);
}
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
@@ -387,7 +387,7 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
updateGlobalMetaStateLocked();
// Enqueue configuration changed.
- mQueuedListener.notifyConfigurationChanged({mContext.getNextId(), when});
+ mPendingArgs.emplace_back(NotifyConfigurationChangedArgs{mContext.getNextId(), when});
}
void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) {
@@ -409,7 +409,7 @@ void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) {
} else {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
- notifyAll(device->configure(now, mConfig, changes));
+ mPendingArgs += device->configure(now, mConfig, changes);
}
}
@@ -419,18 +419,13 @@ void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) {
"There was no change in the pointer capture state.");
} else {
mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
- mQueuedListener.notifyPointerCaptureChanged(
- {mContext.getNextId(), now, mCurrentPointerCaptureRequest});
+ mPendingArgs.emplace_back(
+ NotifyPointerCaptureChangedArgs{mContext.getNextId(), now,
+ mCurrentPointerCaptureRequest});
}
}
}
-void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) {
- for (const NotifyArgs& args : argsList) {
- mQueuedListener.notify(args);
- }
-}
-
void InputReader::updateGlobalMetaStateLocked() {
mGlobalMetaState = 0;
@@ -690,7 +685,7 @@ void InputReader::vibrate(int32_t deviceId, const VibrationSequence& sequence, s
InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
- notifyAll(device->vibrate(sequence, repeat, token));
+ mPendingArgs += device->vibrate(sequence, repeat, token);
}
}
@@ -699,7 +694,7 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
- notifyAll(device->cancelVibrate(token));
+ mPendingArgs += device->cancelVibrate(token);
}
}
@@ -1040,6 +1035,16 @@ int32_t InputReader::ContextImpl::getLedMetaState() {
return mReader->getLedMetaStateLocked();
}
+void InputReader::ContextImpl::setPreventingTouchpadTaps(bool prevent) {
+ // lock is already held by the input loop
+ mReader->mPreventingTouchpadTaps = prevent;
+}
+
+bool InputReader::ContextImpl::isPreventingTouchpadTaps() {
+ // lock is already held by the input loop
+ return mReader->mPreventingTouchpadTaps;
+}
+
void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
// lock is already held by the input loop
mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 8347df8bdc..0bcab42417 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -626,16 +626,16 @@ private:
ftl::Flags<InputDeviceClass> classes;
- BitArray<KEY_MAX> keyBitmask;
- BitArray<KEY_MAX> keyState;
- BitArray<REL_MAX> relBitmask;
- BitArray<SW_MAX> swBitmask;
- BitArray<SW_MAX> swState;
- BitArray<LED_MAX> ledBitmask;
- BitArray<FF_MAX> ffBitmask;
- BitArray<INPUT_PROP_MAX> propBitmask;
- BitArray<MSC_MAX> mscBitmask;
- BitArray<ABS_MAX> absBitmask;
+ BitArray<KEY_CNT> keyBitmask;
+ BitArray<KEY_CNT> keyState;
+ BitArray<REL_CNT> relBitmask;
+ BitArray<SW_CNT> swBitmask;
+ BitArray<SW_CNT> swState;
+ BitArray<LED_CNT> ledBitmask;
+ BitArray<FF_CNT> ffBitmask;
+ BitArray<INPUT_PROP_CNT> propBitmask;
+ BitArray<MSC_CNT> mscBitmask;
+ BitArray<ABS_CNT> absBitmask;
struct AxisState {
RawAbsoluteAxisInfo info;
int value;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index aae3fe79e0..1cbcbf47b4 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -289,7 +289,18 @@ public:
return mEventHub->getDeviceControllerNumber(mId);
}
inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
- return mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo);
+ if (const auto status = mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo); status != OK) {
+ return status;
+ }
+
+ // Validate axis info for InputDevice.
+ if (axisInfo->valid && axisInfo->minValue == axisInfo->maxValue) {
+ // Historically, we deem axes with the same min and max values as invalid to avoid
+ // dividing by zero when scaling by max - min.
+ // TODO(b/291772515): Perform axis info validation on a per-axis basis when it is used.
+ axisInfo->valid = false;
+ }
+ return OK;
}
inline bool hasRelativeAxis(int32_t code) const {
return mEventHub->hasRelativeAxis(mId, code);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9112913565..e21715eb27 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -155,6 +155,9 @@ protected:
int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
int32_t getLedMetaState() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
+ void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock)
+ REQUIRES(mLock) override;
+ bool isPreventingTouchpadTaps() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
} mContext;
friend class ContextImpl;
@@ -171,7 +174,14 @@ private:
// in parallel to passing it to the InputReader.
std::shared_ptr<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
- QueuedInputListener mQueuedListener;
+
+ // The next stage that should receive the events generated inside InputReader.
+ InputListenerInterface& mNextListener;
+ // As various events are generated inside InputReader, they are stored inside this list. The
+ // list can only be accessed with the lock, so the events inside it are well-ordered.
+ // Once the reader is done working, these events will be swapped into a temporary storage and
+ // sent to the 'mNextListener' without holding the lock.
+ std::list<NotifyArgs> mPendingArgs GUARDED_BY(mLock);
InputReaderConfiguration mConfig GUARDED_BY(mLock);
@@ -185,6 +195,9 @@ private:
std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
mDeviceToEventHubIdsMap GUARDED_BY(mLock);
+ // true if tap-to-click on touchpad currently disabled
+ bool mPreventingTouchpadTaps GUARDED_BY(mLock){false};
+
// low-level input event decoding and device management
[[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
REQUIRES(mLock);
@@ -236,8 +249,6 @@ private:
ConfigurationChanges mConfigurationChangesToRefresh GUARDED_BY(mLock);
void refreshConfigurationLocked(ConfigurationChanges changes) REQUIRES(mLock);
- void notifyAll(std::list<NotifyArgs>&& argsList);
-
PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
// state queries
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 0beace19ab..aed75636f7 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -62,6 +62,9 @@ public:
virtual void updateLedMetaState(int32_t metaState) = 0;
virtual int32_t getLedMetaState() = 0;
+
+ virtual void setPreventingTouchpadTaps(bool prevent) = 0;
+ virtual bool isPreventingTouchpadTaps() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index e03a7730ae..58b29b82f9 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -86,20 +86,26 @@ int32_t KeyboardInputMapper::getDisplayId() {
return ADISPLAY_ID_NONE;
}
+std::optional<KeyboardLayoutInfo> KeyboardInputMapper::getKeyboardLayoutInfo() const {
+ if (mKeyboardLayoutInfo) {
+ return mKeyboardLayoutInfo;
+ }
+ std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
+ if (!layoutInfo) {
+ return std::nullopt;
+ }
+ return KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType);
+}
+
void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
info.setKeyboardType(mKeyboardType);
info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
- if (mKeyboardLayoutInfo) {
- info.setKeyboardLayoutInfo(*mKeyboardLayoutInfo);
- } else {
- std::optional<RawLayoutInfo> layoutInfo = getDeviceContext().getRawLayoutInfo();
- if (layoutInfo) {
- info.setKeyboardLayoutInfo(
- KeyboardLayoutInfo(layoutInfo->languageTag, layoutInfo->layoutType));
- }
+ std::optional keyboardLayoutInfo = getKeyboardLayoutInfo();
+ if (keyboardLayoutInfo) {
+ info.setKeyboardLayoutInfo(*keyboardLayoutInfo);
}
}
@@ -152,13 +158,31 @@ std::list<NotifyArgs> KeyboardInputMapper::reconfigure(nsecs_t when,
getValueByKey(config.keyboardLayoutAssociations, getDeviceContext().getLocation());
if (mKeyboardLayoutInfo != newKeyboardLayoutInfo) {
mKeyboardLayoutInfo = newKeyboardLayoutInfo;
+ // Also update keyboard layout overlay as soon as we find the new layout info
+ updateKeyboardLayoutOverlay();
bumpGeneration();
}
}
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEYBOARD_LAYOUTS)) {
+ if (!getDeviceContext().getDeviceClasses().test(InputDeviceClass::VIRTUAL) &&
+ updateKeyboardLayoutOverlay()) {
+ bumpGeneration();
+ }
+ }
return out;
}
+bool KeyboardInputMapper::updateKeyboardLayoutOverlay() {
+ std::shared_ptr<KeyCharacterMap> keyboardLayout =
+ getDeviceContext()
+ .getContext()
+ ->getPolicy()
+ ->getKeyboardLayoutOverlay(getDeviceContext().getDeviceIdentifier(),
+ getKeyboardLayoutInfo());
+ return getDeviceContext().setKeyboardLayoutOverlay(keyboardLayout);
+}
+
void KeyboardInputMapper::configureParameters() {
const PropertyMap& config = getDeviceContext().getConfiguration();
mParameters.orientationAware = config.getBool("keyboard.orientationAware").value_or(false);
@@ -246,6 +270,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read
keyDown.flags = flags;
mKeyDowns.push_back(keyDown);
}
+ onKeyDownProcessed();
} else {
// Remove key down.
if (keyDownIndex) {
@@ -423,4 +448,19 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) {
return out;
}
+void KeyboardInputMapper::onKeyDownProcessed() {
+ InputReaderContext& context = *getContext();
+ if (context.isPreventingTouchpadTaps()) {
+ // avoid pinging java service unnecessarily
+ return;
+ }
+ // Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard
+ // shortcuts
+ bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode);
+ if (shouldHideCursor && context.getPolicy()->isInputMethodConnectionActive()) {
+ context.fadePointer();
+ context.setPreventingTouchpadTaps(true);
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 45fd68b8bb..09808df18c 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -99,12 +99,15 @@ private:
bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
std::optional<size_t> findKeyDownIndex(int32_t scanCode);
+ std::optional<KeyboardLayoutInfo> getKeyboardLayoutInfo() const;
+ bool updateKeyboardLayoutOverlay();
void resetLedState();
void initializeLedState(LedState& ledState, int32_t led);
void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
+ void onKeyDownProcessed();
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 986dabb6fc..eca0f86794 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -350,6 +350,7 @@ void TouchpadInputMapper::dump(std::string& dump) {
dump += addLinePrefix(mPropertyProvider.dump(), INDENT4);
dump += INDENT3 "Captured event converter:\n";
dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
+ dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
}
std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when,
@@ -361,13 +362,31 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when,
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
- std::optional<int32_t> displayId = mPointerController->getDisplayId();
+ mDisplayId = ADISPLAY_ID_NONE;
+ if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+ // This InputDevice is associated with a viewport.
+ // Only generate events for the associated display.
+ const bool mismatchedPointerDisplay =
+ (viewport->displayId != mPointerController->getDisplayId());
+ if (mismatchedPointerDisplay) {
+ ALOGW("Touchpad \"%s\" associated viewport display does not match pointer "
+ "controller",
+ mDeviceContext.getName().c_str());
+ }
+ mDisplayId = mismatchedPointerDisplay ? std::nullopt
+ : std::make_optional(viewport->displayId);
+ } else {
+ // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
+ mDisplayId = mPointerController->getDisplayId();
+ }
+
ui::Rotation orientation = ui::ROTATION_0;
- if (displayId.has_value()) {
- if (auto viewport = config.getDisplayViewportById(*displayId); viewport) {
+ if (mDisplayId.has_value()) {
+ if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
orientation = getInverseRotation(viewport->orientation);
}
}
+ mGestureConverter.setDisplayId(mDisplayId);
mGestureConverter.setOrientation(orientation);
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
@@ -497,13 +516,19 @@ void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out = {};
- MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance();
- for (Gesture& gesture : mGesturesToProcess) {
- out += mGestureConverter.handleGesture(when, readTime, gesture);
- metricsAccumulator.processGesture(mMetricsId, gesture);
+ if (mDisplayId) {
+ MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance();
+ for (Gesture& gesture : mGesturesToProcess) {
+ out += mGestureConverter.handleGesture(when, readTime, gesture);
+ metricsAccumulator.processGesture(mMetricsId, gesture);
+ }
}
mGesturesToProcess.clear();
return out;
}
+std::optional<int32_t> TouchpadInputMapper::getAssociatedDisplayId() {
+ return mDisplayId;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 73ca5afa04..47d712ef27 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -64,6 +64,8 @@ public:
using MetricsIdentifier = std::tuple<uint16_t /*busId*/, uint16_t /*vendorId*/,
uint16_t /*productId*/, uint16_t /*version*/>;
+ std::optional<int32_t> getAssociatedDisplayId() override;
+
private:
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
@@ -102,6 +104,11 @@ private:
std::set<int32_t> mLastFrameTrackingIds;
// Tracking IDs for touches that have at some point been reported as palms by the touchpad.
std::set<int32_t> mPalmTrackingIds;
+
+ // The display that events generated by this mapper should target. This can be set to
+ // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
+ // std::nullopt), all events will be ignored.
+ std::optional<int32_t> mDisplayId;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index e8263412e3..7006e9efc1 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -124,6 +124,11 @@ void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
+ if (!mDisplayId) {
+ // Ignore gestures when there is no target display configured.
+ return {};
+ }
+
switch (gesture.type) {
case kGestureTypeMove:
return {handleMove(when, readTime, gesture)};
@@ -153,6 +158,9 @@ NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
+ if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
+ enableTapToClick();
+ }
rotateDelta(mOrientation, &deltaX, &deltaY);
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -191,6 +199,15 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+
+ if (mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick();
+ if (gesture.details.buttons.is_tap) {
+ // return early to prevent this tap
+ return out;
+ }
+ }
+
const uint32_t buttonsPressed = gesture.details.buttons.down;
bool pointerDown = isPointerDown(mButtonState) ||
buttonsPressed &
@@ -337,6 +354,9 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi
// magnitude, which will also result in the pointer icon being updated.
// TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
// initiated with a touchpad.
+ if (!mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick();
+ }
return {handleMove(when, readTime,
Gesture(kGestureMove, gesture.start_time, gesture.end_time,
/*dx=*/0.f,
@@ -541,11 +561,11 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime
readTime,
mDeviceId,
SOURCE,
- mPointerController->getDisplayId(),
+ *mDisplayId,
/* policyFlags= */ POLICY_FLAG_WAKE,
action,
/* actionButton= */ actionButton,
- /* flags= */ 0,
+ /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0,
mReaderContext.getGlobalMetaState(),
buttonState,
mCurrentClassification,
@@ -561,4 +581,8 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime
/* videoFrames= */ {}};
}
+void GestureConverter::enableTapToClick() {
+ mReaderContext.setPreventingTouchpadTaps(false);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index b613b887cd..e6cf617500 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -46,6 +46,8 @@ public:
void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
+ void setDisplayId(std::optional<int32_t> displayId) { mDisplayId = displayId; }
+
void populateMotionRanges(InputDeviceInfo& info) const;
[[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -78,10 +80,13 @@ private:
const PointerCoords* pointerCoords, float xCursorPosition,
float yCursorPosition);
+ void enableTapToClick();
+
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
std::shared_ptr<PointerControllerInterface> mPointerController;
+ std::optional<int32_t> mDisplayId;
ui::Rotation mOrientation = ui::ROTATION_0;
RawAbsoluteAxisInfo mXAxisInfo;
RawAbsoluteAxisInfo mYAxisInfo;
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
new file mode 100644
index 0000000000..23c1691b28
--- /dev/null
+++ b/services/inputflinger/rust/Android.bp
@@ -0,0 +1,52 @@
+// Copyright 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generate the C++ code that Rust calls into.
+genrule {
+ name: "inputflinger_rs_bootstrap_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["inputflinger_rs_bootstrap_cxx_generated.cc"],
+}
+
+// Generate a C++ header containing the C++ bindings
+// to the Rust exported functions in lib.rs.
+genrule {
+ name: "inputflinger_rs_bootstrap_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["inputflinger_bootstrap.rs.h"],
+}
+
+rust_ffi_static {
+ name: "libinputflinger_rs",
+ crate_name: "inputflinger",
+ srcs: ["lib.rs"],
+ rustlibs: [
+ "libcxx",
+ "com.android.server.inputflinger-rust",
+ "libbinder_rs",
+ "liblog_rust",
+ "liblogger",
+ ],
+ host_supported: true,
+}
+
+cc_library_headers {
+ name: "inputflinger_rs_bootstrap_cxx_headers",
+ host_supported: true,
+ export_include_dirs: ["ffi"],
+}
diff --git a/libs/renderengine/include/renderengine/Image.h b/services/inputflinger/rust/ffi/InputFlingerBootstrap.h
index 3bb47318ef..eb79ab50da 100644
--- a/libs/renderengine/include/renderengine/Image.h
+++ b/services/inputflinger/rust/ffi/InputFlingerBootstrap.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,6 @@
#pragma once
-struct ANativeWindowBuffer;
+#include <android/binder_parcel.h>
-namespace android {
-namespace renderengine {
-
-class Image {
-public:
- virtual ~Image() = default;
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
-};
-
-} // namespace renderengine
-} // namespace android
+using IInputFlingerRustBootstrapCallbackAIBinder = AIBinder;
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
new file mode 100644
index 0000000000..501e4359cd
--- /dev/null
+++ b/services/inputflinger/rust/lib.rs
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! # The rust component of InputFlinger
+//!
+//! We use cxxbridge to create IInputFlingerRust - the Rust component of inputflinger - and
+//! pass it back to C++ as a local AIDL interface.
+
+use binder::{
+ unstable_api::{AIBinder, new_spibinder,},
+ BinderFeatures, Interface, StatusCode, Strong,
+};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputFlingerRust::{
+ BnInputFlingerRust, IInputFlingerRust,
+ IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback,
+};
+use log::debug;
+
+const LOG_TAG: &str = "inputflinger_bootstrap";
+
+#[cxx::bridge]
+#[allow(unsafe_op_in_unsafe_fn)]
+mod ffi {
+ extern "C++" {
+ include!("InputFlingerBootstrap.h");
+ type IInputFlingerRustBootstrapCallbackAIBinder;
+ }
+
+ extern "Rust" {
+ unsafe fn create_inputflinger_rust(
+ callback: *mut IInputFlingerRustBootstrapCallbackAIBinder,
+ );
+ }
+}
+
+/// Create the IInputFlingerRust implementation.
+/// This is the singular entry point from C++ into Rust.
+/// The `callback` parameter must be a valid pointer to an AIBinder implementation of
+/// the `IInputFlingerRustBootstrapCallback` interface. The IInputFlingerRust implementation that
+/// is created will be passed back through the callback from within this function.
+/// NOTE: This function must not hold a strong reference to the callback beyond its scope.
+///
+/// # Safety
+///
+/// This function is safe iff `callback` is a valid pointer to an `AIBinder` interface of type
+/// `IInputFlingerRustBootstrapCallback`. The pointer must have had its reference count manually
+/// incremented using `AIBinder_incStrong`. See `binder::unstable_api::new_spibinder`.
+unsafe fn create_inputflinger_rust(callback: *mut ffi::IInputFlingerRustBootstrapCallbackAIBinder) {
+ logger::init(
+ logger::Config::default().with_tag_on_device(LOG_TAG).with_min_level(log::Level::Trace),
+ );
+
+ let callback = callback as *mut AIBinder;
+ if callback.is_null() {
+ panic!("create_inputflinger_rust cannot be called with a null callback");
+ }
+
+ // SAFETY: Our caller guaranteed that `callback` is a valid pointer to an `AIBinder` and its
+ // reference count has been incremented..
+ let Some(callback) = (unsafe { new_spibinder(callback) }) else {
+ panic!("Failed to get SpAIBinder from raw callback pointer");
+ };
+
+ let callback: Result<Strong<dyn IInputFlingerRustBootstrapCallback>, StatusCode> =
+ callback.into_interface();
+ match callback {
+ Ok(callback) => {
+ debug!("Creating InputFlingerRust");
+ let service =
+ BnInputFlingerRust::new_binder(InputFlingerRust {}, BinderFeatures::default());
+ callback.onProvideInputFlingerRust(&service).unwrap();
+ }
+ Err(status) => {
+ panic!("Failed to convert AIBinder into the callback interface: {}", status);
+ }
+ }
+}
+
+struct InputFlingerRust {}
+
+impl Interface for InputFlingerRust {}
+
+impl IInputFlingerRust for InputFlingerRust {}
+
+impl Drop for InputFlingerRust {
+ fn drop(&mut self) {
+ debug!("Destroying InputFlingerRust");
+ }
+}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 38640b3f02..3d6df30b97 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -62,7 +62,9 @@ cc_test {
"SlopController_test.cpp",
"SyncQueue_test.cpp",
"TestInputListener.cpp",
+ "TestInputListenerMatchers.cpp",
"TouchpadInputMapper_test.cpp",
+ "KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
"UnwantedInteractionBlocker_test.cpp",
],
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 3486d0f2a4..41c98ef9e9 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -158,7 +158,8 @@ const InputReaderConfiguration& FakeInputReaderPolicy::getReaderConfiguration()
return mConfig;
}
-const std::vector<InputDeviceInfo>& FakeInputReaderPolicy::getInputDevices() const {
+const std::vector<InputDeviceInfo> FakeInputReaderPolicy::getInputDevices() const {
+ std::scoped_lock lock(mLock);
return mInputDevices;
}
@@ -209,6 +210,14 @@ void FakeInputReaderPolicy::setStylusPointerIconEnabled(bool enabled) {
mConfig.stylusPointerIconEnabled = enabled;
}
+void FakeInputReaderPolicy::setIsInputMethodConnectionActive(bool active) {
+ mIsInputMethodConnectionActive = active;
+}
+
+bool FakeInputReaderPolicy::isInputMethodConnectionActive() {
+ return mIsInputMethodConnectionActive;
+}
+
void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
*outConfig = mConfig;
}
@@ -220,14 +229,14 @@ std::shared_ptr<PointerControllerInterface> FakeInputReaderPolicy::obtainPointer
void FakeInputReaderPolicy::notifyInputDevicesChanged(
const std::vector<InputDeviceInfo>& inputDevices) {
- std::scoped_lock<std::mutex> lock(mLock);
+ std::scoped_lock lock(mLock);
mInputDevices = inputDevices;
mInputDevicesChanged = true;
mDevicesChangedCondition.notify_all();
}
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) {
+ const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
}
@@ -248,7 +257,7 @@ void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> proces
}
void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) {
- std::scoped_lock<std::mutex> lock(mLock);
+ std::scoped_lock lock(mLock);
mStylusGestureNotified = deviceId;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 85ff01a071..48912a6a28 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -64,7 +64,7 @@ public:
void removeDisabledDevice(int32_t deviceId);
void setPointerController(std::shared_ptr<FakePointerController> controller);
const InputReaderConfiguration& getReaderConfiguration() const;
- const std::vector<InputDeviceInfo>& getInputDevices() const;
+ const std::vector<InputDeviceInfo> getInputDevices() const;
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
ui::Rotation surfaceRotation);
void setTouchAffineTransformation(const TouchAffineTransformation t);
@@ -77,6 +77,8 @@ public:
void setVelocityControlParams(const VelocityControlParameters& params);
void setStylusButtonMotionEventsEnabled(bool enabled);
void setStylusPointerIconEnabled(bool enabled);
+ void setIsInputMethodConnectionActive(bool active);
+ bool isInputMethodConnectionActive() override;
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
@@ -84,12 +86,12 @@ private:
int32_t /*deviceId*/) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) override;
+ const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
- std::mutex mLock;
+ mutable std::mutex mLock;
std::condition_variable mDevicesChangedCondition;
InputReaderConfiguration mConfig;
@@ -99,6 +101,7 @@ private:
std::vector<DisplayViewport> mViewports;
TouchAffineTransformation transform;
std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
+ bool mIsInputMethodConnectionActive{false};
uint32_t mNextPointerCaptureSequenceNumber{0};
};
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 482a266f77..74ce359cac 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -19,6 +19,7 @@
#include <EventHub.h>
#include <gestures/GestureConverter.h>
#include <gtest/gtest.h>
+#include <gui/constants.h>
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
@@ -85,6 +86,7 @@ protected:
TEST_F(GestureConverterTest, Move) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
@@ -93,8 +95,8 @@ TEST_F(GestureConverterTest, Move) {
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
}
@@ -103,6 +105,7 @@ TEST_F(GestureConverterTest, Move_Rotated) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
@@ -111,8 +114,8 @@ TEST_F(GestureConverterTest, Move_Rotated) {
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5));
}
@@ -120,6 +123,7 @@ TEST_F(GestureConverterTest, Move_Rotated) {
TEST_F(GestureConverterTest, ButtonsChange) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
// Press left and right buttons at once
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -132,23 +136,23 @@ TEST_F(GestureConverterTest, ButtonsChange) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Then release the left button
Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -161,8 +165,8 @@ TEST_F(GestureConverterTest, ButtonsChange) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Finally release the right button
Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -174,22 +178,24 @@ TEST_F(GestureConverterTest, ButtonsChange) {
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, DragWithButton) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
// Press the button
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -201,15 +207,15 @@ TEST_F(GestureConverterTest, DragWithButton) {
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
// Move
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
@@ -219,8 +225,8 @@ TEST_F(GestureConverterTest, DragWithButton) {
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
@@ -234,23 +240,25 @@ TEST_F(GestureConverterTest, DragWithButton) {
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll) {
const nsecs_t downTime = 12345;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
@@ -261,7 +269,8 @@ TEST_F(GestureConverterTest, Scroll) {
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER), WithDownTime(downTime),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
@@ -269,7 +278,8 @@ TEST_F(GestureConverterTest, Scroll) {
WithGestureScrollDistance(0, 10, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
@@ -280,7 +290,8 @@ TEST_F(GestureConverterTest, Scroll) {
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -292,7 +303,8 @@ TEST_F(GestureConverterTest, Scroll) {
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_Rotated) {
@@ -300,6 +312,7 @@ TEST_F(GestureConverterTest, Scroll_Rotated) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
@@ -309,14 +322,15 @@ TEST_F(GestureConverterTest, Scroll_Rotated) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER), WithDownTime(downTime)));
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithCoords(POINTER_X - 10, POINTER_Y),
WithGestureScrollDistance(0, 10, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
@@ -326,7 +340,7 @@ TEST_F(GestureConverterTest, Scroll_Rotated) {
WithCoords(POINTER_X - 15, POINTER_Y),
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -337,12 +351,13 @@ TEST_F(GestureConverterTest, Scroll_Rotated) {
WithCoords(POINTER_X - 15, POINTER_Y),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -358,12 +373,14 @@ TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) {
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
ASSERT_EQ(1u, args.size());
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionClassification(MotionClassification::NONE));
+ AllOf(WithMotionClassification(MotionClassification::NONE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -387,6 +404,7 @@ TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) {
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/0);
@@ -406,6 +424,7 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAfterGesture)
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
/*dy=*/5);
@@ -431,6 +450,7 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
// only checks movement in one dimension.
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
@@ -444,7 +464,8 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -453,7 +474,8 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -462,7 +484,8 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
@@ -471,7 +494,8 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0, -0.01, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
@@ -488,7 +512,8 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
@@ -504,26 +529,30 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) {
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
@@ -535,28 +564,31 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) {
NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10);
@@ -571,7 +603,8 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) {
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15);
@@ -585,21 +618,24 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) {
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 10, /* dy= */ 0);
@@ -613,7 +649,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger0Start = arg.pointerCoords[0];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -622,7 +659,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger1Start = arg.pointerCoords[1];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -631,7 +669,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger2Start = arg.pointerCoords[2];
args.pop_front();
arg = std::get<NotifyMotionArgs>(args.front());
@@ -640,7 +679,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
PointerCoords finger3Start = arg.pointerCoords[3];
args.pop_front();
@@ -649,7 +689,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0.01, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10);
@@ -668,7 +709,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15);
@@ -686,32 +728,37 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_Inwards) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -722,7 +769,7 @@ TEST_F(GestureConverterTest, Pinch_Inwards) {
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -730,7 +777,7 @@ TEST_F(GestureConverterTest, Pinch_Inwards) {
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
@@ -742,7 +789,7 @@ TEST_F(GestureConverterTest, Pinch_Inwards) {
WithGesturePinchScaleFactor(0.8f, EPSILON),
WithPointerCoords(0, POINTER_X - 80, POINTER_Y),
WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -753,18 +800,19 @@ TEST_F(GestureConverterTest, Pinch_Inwards) {
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_Outwards) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -775,7 +823,7 @@ TEST_F(GestureConverterTest, Pinch_Outwards) {
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -783,7 +831,7 @@ TEST_F(GestureConverterTest, Pinch_Outwards) {
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 1.2, GESTURES_ZOOM_UPDATE);
@@ -795,7 +843,7 @@ TEST_F(GestureConverterTest, Pinch_Outwards) {
WithGesturePinchScaleFactor(1.2f, EPSILON),
WithPointerCoords(0, POINTER_X - 120, POINTER_Y),
WithPointerCoords(1, POINTER_X + 120, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -806,18 +854,19 @@ TEST_F(GestureConverterTest, Pinch_Outwards) {
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -841,6 +890,7 @@ TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) {
TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -866,6 +916,7 @@ TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) {
TEST_F(GestureConverterTest, ResetWithButtonPressed) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
@@ -879,24 +930,25 @@ TEST_F(GestureConverterTest, ResetWithButtonPressed) {
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER)));
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringScroll) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
(void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
@@ -909,12 +961,14 @@ TEST_F(GestureConverterTest, ResetDuringScroll) {
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/10);
@@ -927,24 +981,28 @@ TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) {
2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER)));
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, ResetDuringPinch) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -957,18 +1015,19 @@ TEST_F(GestureConverterTest, ResetDuringPinch) {
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
args.pop_front();
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER)));
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, FlingTapDown) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
@@ -977,10 +1036,252 @@ TEST_F(GestureConverterTest, FlingTapDown) {
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X, POINTER_Y));
ASSERT_TRUE(mFakePointerController->isPointerShown());
}
+TEST_F(GestureConverterTest, Tap) {
+ // Tap should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ ASSERT_EQ(5u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTest, Click) {
+ // Click should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+
+ ASSERT_EQ(2u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTest, TapWithTapToClickDisabled) {
+ // Tap should be ignored when disabled
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
+ // Click should still produce button press/release events
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+
+ ASSERT_EQ(3u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
+ // initially disable tap-to-click
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index bc87c8e7df..6d9cd87c4f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,6 +16,7 @@
#include "../dispatcher/InputDispatcher.h"
#include "../BlockingQueue.h"
+#include "TestInputListenerMatchers.h"
#include <NotifyArgsBuilders.h>
#include <android-base/properties.h>
@@ -107,6 +108,23 @@ static constexpr gui::Pid MONITOR_PID{2001};
static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+/**
+ * If we expect to receive the event, the timeout can be made very long. When the test are running
+ * correctly, we will actually never wait until the end of the timeout because the wait will end
+ * when the event comes in. Still, this value shouldn't be infinite. During development, a local
+ * change may cause the test to fail. This timeout should be short enough to not annoy so that the
+ * developer can see the failure quickly (on human scale).
+ */
+static constexpr std::chrono::duration CONSUME_TIMEOUT_EVENT_EXPECTED = 1000ms;
+/**
+ * When no event is expected, we can have a very short timeout. A large value here would slow down
+ * the tests. In the unlikely event of system being too slow, the event may still be present but the
+ * timeout would complete before it is consumed. This would result in test flakiness. If this
+ * occurs, the flakiness rate would be high. Since the flakes are treated with high priority, this
+ * would get noticed and addressed quickly.
+ */
+static constexpr std::chrono::duration CONSUME_TIMEOUT_NO_EVENT_EXPECTED = 10ms;
+
static constexpr int expectedWallpaperFlags =
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
@@ -136,41 +154,10 @@ static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) {
<< MotionEvent::actionToString(receivedAction);
}
-MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
- bool matches = action == arg.getAction();
- if (!matches) {
- *result_listener << "expected action " << MotionEvent::actionToString(action)
- << ", but got " << MotionEvent::actionToString(arg.getAction());
- }
- if (action == AMOTION_EVENT_ACTION_DOWN) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "downTime should match eventTime for ACTION_DOWN events";
- matches &= arg.getDownTime() == arg.getEventTime();
- }
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
- matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
- }
- return matches;
-}
-
MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
return arg.getDownTime() == downTime;
}
-MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
- return arg.getDisplayId() == displayId;
-}
-
-MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") {
- return arg.getDeviceId() == deviceId;
-}
-
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
<< inputEventSourceToString(arg.getSource());
@@ -892,9 +879,9 @@ public:
mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
}
- InputEvent* consume() {
+ InputEvent* consume(std::chrono::milliseconds timeout) {
InputEvent* event;
- std::optional<uint32_t> consumeSeq = receiveEvent(&event);
+ std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event);
if (!consumeSeq) {
return nullptr;
}
@@ -906,7 +893,8 @@ public:
* Receive an event without acknowledging it.
* Return the sequence number that could later be used to send finished signal.
*/
- std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
+ std::optional<uint32_t> receiveEvent(std::chrono::milliseconds timeout,
+ InputEvent** outEvent = nullptr) {
uint32_t consumeSeq;
InputEvent* event;
@@ -916,7 +904,7 @@ public:
status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
&event);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
- if (elapsed > 100ms) {
+ if (elapsed > timeout) {
break;
}
}
@@ -956,7 +944,7 @@ public:
void consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
@@ -1002,7 +990,7 @@ public:
}
MotionEvent* consumeMotion() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
if (event == nullptr) {
ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one.";
@@ -1023,7 +1011,7 @@ public:
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode) {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::FOCUS, event->getType())
@@ -1037,7 +1025,7 @@ public:
}
void consumeCaptureEvent(bool hasCapture) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::CAPTURE, event->getType())
@@ -1051,7 +1039,7 @@ public:
}
void consumeDragEvent(bool isExiting, float x, float y) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event;
@@ -1066,7 +1054,7 @@ public:
}
void consumeTouchModeEvent(bool inTouchMode) {
- const InputEvent* event = consume();
+ const InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType())
@@ -1079,7 +1067,7 @@ public:
}
void assertNoEvents() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
if (event == nullptr) {
return;
}
@@ -1262,6 +1250,25 @@ public:
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
+ KeyEvent* consumeKey() {
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consume failed : no event";
+ return nullptr;
+ }
+ if (event->getType() != InputEventType::KEY) {
+ ADD_FAILURE() << "Instead of key event, got " << *event;
+ return nullptr;
+ }
+ return static_cast<KeyEvent*>(event);
+ }
+
+ void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) {
+ KeyEvent* keyEvent = consumeKey();
+ ASSERT_NE(nullptr, keyEvent) << "Did not get a key event, but expected " << matcher;
+ ASSERT_THAT(*keyEvent, matcher);
+ }
+
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags);
}
@@ -1322,13 +1329,11 @@ public:
void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- InputEvent* event = consume();
- ASSERT_NE(nullptr, event);
- ASSERT_EQ(InputEventType::MOTION, event->getType());
- const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked());
- EXPECT_EQ(0.f, motionEvent.getRawPointerCoords(0)->getX());
- EXPECT_EQ(0.f, motionEvent.getRawPointerCoords(0)->getY());
+ MotionEvent* motionEvent = consumeMotion();
+ ASSERT_NE(nullptr, motionEvent);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent->getActionMasked());
+ EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getX());
+ EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getY());
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
@@ -1372,7 +1377,7 @@ public:
ADD_FAILURE() << "Invalid receive event on window with no receiver";
return std::nullopt;
}
- return mInputReceiver->receiveEvent(outEvent);
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED, outEvent);
}
void finishEvent(uint32_t sequenceNum) {
@@ -1385,15 +1390,15 @@ public:
mInputReceiver->sendTimeline(inputEventId, timeline);
}
- InputEvent* consume() {
+ InputEvent* consume(std::chrono::milliseconds timeout) {
if (mInputReceiver == nullptr) {
return nullptr;
}
- return mInputReceiver->consume();
+ return mInputReceiver->consume(timeout);
}
MotionEvent* consumeMotion() {
- InputEvent* event = consume();
+ InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
if (event == nullptr) {
ADD_FAILURE() << "Consume failed : no event";
return nullptr;
@@ -1441,7 +1446,7 @@ private:
std::atomic<int32_t> FakeWindowHandle::sId{1};
static InputEventInjectionResult injectKey(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+ InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
int32_t displayId = ADISPLAY_ID_NONE,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1459,10 +1464,19 @@ static InputEventInjectionResult injectKey(
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(&event, targetUid, syncMode, injectionTimeout, policyFlags);
+ return dispatcher.injectInputEvent(&event, targetUid, syncMode, injectionTimeout, policyFlags);
+}
+
+static void assertInjectedKeyTimesOut(InputDispatcher& dispatcher) {
+ InputEventInjectionResult result =
+ injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_NONE,
+ InputEventInjectionSync::WAIT_FOR_RESULT, CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
+ if (result != InputEventInjectionResult::TIMED_OUT) {
+ FAIL() << "Injection should have timed out, but got " << ftl::enum_string(result);
+ }
}
-static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectKeyDown(InputDispatcher& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, displayId);
}
@@ -1470,30 +1484,30 @@ static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispat
// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
// has to be woken up to process the repeating key.
-static InputEventInjectionResult injectKeyDownNoRepeat(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDownNoRepeat(InputDispatcher& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, displayId,
InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
/*allowKeyRepeat=*/false);
}
-static InputEventInjectionResult injectKeyUp(const std::unique_ptr<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectKeyUp(InputDispatcher& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, displayId);
}
static InputEventInjectionResult injectMotionEvent(
- const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
+ InputDispatcher& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::optional<gui::Uid> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
- return dispatcher->injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
- policyFlags);
+ return dispatcher.injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
+ policyFlags);
}
static InputEventInjectionResult injectMotionEvent(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t source,
- int32_t displayId, const PointF& position = {100, 200},
+ InputDispatcher& dispatcher, int32_t action, int32_t source, int32_t displayId,
+ const PointF& position = {100, 200},
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1517,14 +1531,14 @@ static InputEventInjectionResult injectMotionEvent(
targetUid, policyFlags);
}
-static InputEventInjectionResult injectMotionDown(
- const std::unique_ptr<InputDispatcher>& dispatcher, int32_t source, int32_t displayId,
- const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionDown(InputDispatcher& dispatcher, int32_t source,
+ int32_t displayId,
+ const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
-static InputEventInjectionResult injectMotionUp(const std::unique_ptr<InputDispatcher>& dispatcher,
- int32_t source, int32_t displayId,
+static InputEventInjectionResult injectMotionUp(InputDispatcher& dispatcher, int32_t source,
+ int32_t displayId,
const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
}
@@ -1634,7 +1648,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -1649,7 +1663,7 @@ TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Inject a MotionEvent to an unknown display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -1669,7 +1683,7 @@ TEST_F(InputDispatcherTest, SetInputWindowOnceWithSingleTouchWindow) {
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1689,7 +1703,7 @@ TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1708,7 +1722,7 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
mDispatcher->onWindowInfosChanged(
{{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Top window should receive the touch down event. Second window should not receive anything.
@@ -1735,7 +1749,7 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCance
mDispatcher->onWindowInfosChanged(
{{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1744,7 +1758,7 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCance
wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1814,7 +1828,7 @@ TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) {
mDispatcher->onWindowInfosChanged(
{{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1823,7 +1837,7 @@ TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) {
wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1865,7 +1879,7 @@ TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) {
// Touch down on top window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1881,7 +1895,7 @@ TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1897,14 +1911,14 @@ TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionPointerUp(0);
wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.displayId(ADISPLAY_ID_DEFAULT)
@@ -1952,7 +1966,7 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) {
// Touch down on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1968,7 +1982,7 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(300).y(100))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -1994,7 +2008,7 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(310).y(110))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
rightWindow->consumeMotionMove();
@@ -2032,7 +2046,7 @@ TEST_F(InputDispatcherTest, WallpaperWindowWhenSlippery) {
// Touch down on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -2042,7 +2056,7 @@ TEST_F(InputDispatcherTest, WallpaperWindowWhenSlippery) {
// Move to right window, the left window should receive cancel.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {201, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -2161,86 +2175,74 @@ TEST_F(InputDispatcherTest, HoverFromLeftToRightAndTap) {
const int32_t touchDeviceId = 4;
// Move the cursor from right
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
.downTime(baseTime + 10)
.eventTime(baseTime + 20)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// .. to the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
.downTime(baseTime + 10)
.eventTime(baseTime + 30)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(110)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Now tap the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 40)
.eventTime(baseTime + 40)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// release tap
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 40)
.eventTime(baseTime + 50)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
// Tap the window on the right
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 60)
.eventTime(baseTime + 60)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// release tap
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.downTime(baseTime + 60)
.eventTime(baseTime + 70)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
@@ -2526,12 +2528,10 @@ TEST_F(InputDispatcherTest, UnfinishedInjectedEvent) {
// Pretend a test injects an ACTION_DOWN mouse event, but forgets to lift up the touch after
// completion.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.deviceId(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
.build()));
window->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
@@ -2579,35 +2579,29 @@ TEST_F(InputDispatcherTest, HoverTapAndSplitTouch) {
const int32_t touchDeviceId = 4;
// Hover over the left window. Keep the cursor there.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
.deviceId(mouseDeviceId)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Tap on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
@@ -2615,27 +2609,21 @@ TEST_F(InputDispatcherTest, HoverTapAndSplitTouch) {
// First finger down on right window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build()));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// Second finger down on the left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(300)
- .y(100))
- .pointer(PointerBuilder(1, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
.build()));
leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
@@ -2662,32 +2650,28 @@ TEST_F(InputDispatcherTest, StylusHoverAndTouchTap) {
const int32_t touchDeviceId = 4;
// Start hovering with stylus
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Finger down on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
// Try to continue hovering with stylus. Since we are already down, injection should fail
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
@@ -2698,19 +2682,17 @@ TEST_F(InputDispatcherTest, StylusHoverAndTouchTap) {
// Lift up the finger
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
// Now that the touch is gone, stylus hovering should start working again
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
@@ -2953,22 +2935,21 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) {
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
mDispatcher->waitForIdle();
- InputEvent* inputEvent1 = window1->consume();
- ASSERT_NE(inputEvent1, nullptr);
+
+ MotionEvent* motionEvent1 = window1->consumeMotion();
+ ASSERT_NE(motionEvent1, nullptr);
window2->assertNoEvents();
- MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
- nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
- ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime());
+ nsecs_t downTimeForWindow1 = motionEvent1->getDownTime();
+ ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime());
// Now touch down on the window with another pointer
mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
mDispatcher->waitForIdle();
- InputEvent* inputEvent2 = window2->consume();
- ASSERT_NE(inputEvent2, nullptr);
- MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
- nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
+ MotionEvent* motionEvent2 = window2->consumeMotion();
+ ASSERT_NE(motionEvent2, nullptr);
+ nsecs_t downTimeForWindow2 = motionEvent2->getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
- ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime());
+ ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime());
// Now move the pointer on the second window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
@@ -3012,7 +2993,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
// Start cursor position in right window so that we can move the cursor to left window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(900).y(400))
@@ -3021,7 +3002,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
// Move cursor into left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3031,7 +3012,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3040,7 +3021,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
windowLeft->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
@@ -3050,7 +3031,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
AINPUT_SOURCE_MOUSE)
.buttonState(0)
@@ -3060,7 +3041,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3069,7 +3050,7 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
// Move mouse cursor back to right window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(900).y(400))
@@ -3157,12 +3138,10 @@ TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
// Send mouse cursor to the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
@@ -3191,22 +3170,18 @@ TEST_F(InputDispatcherTest, MouseAndTouchWithSpyWindows) {
// Send mouse cursor to the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(100)
- .y(100))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
.build()));
// Move mouse cursor
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(110)
- .y(110))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
@@ -3219,13 +3194,11 @@ TEST_F(InputDispatcherTest, MouseAndTouchWithSpyWindows) {
WithSource(AINPUT_SOURCE_MOUSE)));
// Touch down on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(200)
- .y(200))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
WithSource(AINPUT_SOURCE_MOUSE)));
@@ -3243,13 +3216,11 @@ TEST_F(InputDispatcherTest, MouseAndTouchWithSpyWindows) {
// Touch UP on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(200)
- .y(200))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
.build()));
spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3260,13 +3231,11 @@ TEST_F(InputDispatcherTest, MouseAndTouchWithSpyWindows) {
// One more tap - DOWN
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(250)
- .y(250))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(250))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3275,13 +3244,11 @@ TEST_F(InputDispatcherTest, MouseAndTouchWithSpyWindows) {
// Touch UP on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(SECOND_DEVICE_ID)
- .pointer(PointerBuilder(0, ToolType::FINGER)
- .x(250)
- .y(250))
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(250))
.build()));
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
@@ -3305,7 +3272,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3313,7 +3280,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3322,7 +3289,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
@@ -3332,7 +3299,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
AINPUT_SOURCE_MOUSE)
.buttonState(0)
@@ -3342,7 +3309,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3352,7 +3319,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
// We already canceled the hovering implicitly by injecting the "DOWN" event without lifting the
// hover first. Therefore, injection of HOVER_EXIT is inconsistent, and should fail.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
AINPUT_SOURCE_MOUSE)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
@@ -3375,12 +3342,10 @@ TEST_F(InputDispatcherTest, HoverExitIsSentToRemovedWindow) {
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(400))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
.build()));
window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
@@ -3482,13 +3447,11 @@ TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) {
// Set cursor position in window in default display and check that hover enter and move
// events are generated.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.displayId(ADISPLAY_ID_DEFAULT)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(300)
- .y(600))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(600))
.build()));
windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
@@ -3503,13 +3466,11 @@ TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) {
mDispatcher->onWindowInfosChanged(
{{*windowDefaultDisplay->getInfo(), *windowSecondDisplay->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
.displayId(ADISPLAY_ID_DEFAULT)
- .pointer(PointerBuilder(0, ToolType::MOUSE)
- .x(400)
- .y(700))
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(400).y(700))
.build()));
windowDefaultDisplay->consumeMotionEvent(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
@@ -3535,7 +3496,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
// Inject an event with coordinate in the area of right window, with mouse cursor in the area of
// left window. This event should be dispatched to the left window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowRight->assertNoEvents();
@@ -3777,7 +3738,7 @@ TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -3790,7 +3751,7 @@ TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(-30).y(-50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4106,7 +4067,7 @@ TEST_F(InputDispatcherDisplayProjectionTest, InjectionInLogicalDisplaySpace) {
// Send down to the first window. The point is represented in the logical display space. The
// point is selected so that if the hit test was done in logical display space, then it would
// end up in the incorrect window.
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
PointF{75 * 2, 55 * 4});
firstWindow->consumeMotionDown();
@@ -4133,7 +4094,7 @@ TEST_F(InputDispatcherDisplayProjectionTest, InjectionWithTransformInLogicalDisp
.build();
event.transform(matrix);
- injectMotionEvent(mDispatcher, event, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, event, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT);
firstWindow->consumeMotionDown();
@@ -4630,7 +4591,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) {
0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4645,14 +4606,14 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) {
secondWindowInPrimary->consumeMotionDown();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionMove();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
@@ -4690,7 +4651,8 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
// Touch on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
+ {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -4704,14 +4666,14 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
SECOND_DISPLAY_ID, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
@@ -4832,7 +4794,7 @@ TEST_F(InputDispatcherTest, InjectedTouchesPokeUserActivity) {
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -4961,7 +4923,9 @@ public:
expectedFlags);
}
- std::optional<int32_t> receiveEvent() { return mInputReceiver->receiveEvent(); }
+ std::optional<int32_t> receiveEvent() {
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ }
void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
@@ -4994,18 +4958,7 @@ public:
/*expectedFlags=*/0);
}
- MotionEvent* consumeMotion() {
- InputEvent* event = mInputReceiver->consume();
- if (!event) {
- ADD_FAILURE() << "No event was produced";
- return nullptr;
- }
- if (event->getType() != InputEventType::MOTION) {
- ADD_FAILURE() << "Expected MotionEvent, got " << *event;
- return nullptr;
- }
- return static_cast<MotionEvent*>(event);
- }
+ MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
void assertNoEvents() { mInputReceiver->assertNoEvents(); }
@@ -5032,7 +4985,7 @@ TEST_F(InputDispatcherMonitorTest, MonitorTouchIsCanceledWhenForegroundWindowDis
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -5041,7 +4994,7 @@ TEST_F(InputDispatcherMonitorTest, MonitorTouchIsCanceledWhenForegroundWindowDis
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -5056,7 +5009,7 @@ TEST_F(InputDispatcherMonitorTest, MonitorTouchIsCanceledWhenForegroundWindowDis
// If more events come in, there will be no more foreground window to send them to. This will
// cause a cancel for the monitor, as well.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {120, 200}))
<< "Injection should fail because the window was removed";
window->assertNoEvents();
@@ -5073,7 +5026,7 @@ TEST_F(InputDispatcherMonitorTest, ReceivesMotionEvents) {
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -5088,7 +5041,7 @@ TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) {
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -5098,7 +5051,7 @@ TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) {
EXPECT_NE(OK, mDispatcher->pilferPointers(monitor.getToken()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -5117,7 +5070,7 @@ TEST_F(InputDispatcherMonitorTest, NoWindowTransform) {
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
MotionEvent* event = monitor.consumeMotion();
@@ -5130,7 +5083,7 @@ TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) {
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Injection should fail if there is a monitor, but no touchable window";
monitor.assertNoEvents();
}
@@ -5228,7 +5181,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) {
const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
mDispatcher->notifyKey(keyArgs);
- InputEvent* event = window->consume();
+ KeyEvent* event = window->consumeKey();
ASSERT_NE(event, nullptr);
std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
@@ -5272,7 +5225,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(motionArgs);
- InputEvent* event = window->consume();
+ MotionEvent* event = window->consumeMotion();
ASSERT_NE(event, nullptr);
std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
@@ -5371,7 +5324,7 @@ TEST_F(InputDispatcherTest, SetFocusedWindow) {
setFocusedWindow(windowSecond);
windowSecond->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
@@ -5392,8 +5345,7 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestInvalidChannel) {
setFocusedWindow(window);
// Test inject a key down, should timeout.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
// window channel is invalid, so it should not receive any input event.
window->assertNoEvents();
@@ -5410,8 +5362,7 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestNoFocusableWindow) {
setFocusedWindow(window);
// Test inject a key down, should timeout.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
// window is not focusable, so it should not receive any input event.
window->assertNoEvents();
@@ -5438,7 +5389,7 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) {
windowSecond->consumeFocusEvent(true);
windowTop->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
@@ -5461,7 +5412,7 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_TransferFocusTokenNotFocusable) {
setFocusedWindow(windowTop);
windowTop->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Event should be dropped.
@@ -5492,8 +5443,8 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_DeferInvisibleWindow) {
// Injected key goes to pending queue.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
// Window does not get focus event or key down.
window->assertNoEvents();
@@ -5774,15 +5725,8 @@ protected:
void expectKeyRepeatOnce(int32_t repeatCount) {
SCOPED_TRACE(StringPrintf("Checking event with repeat count %" PRId32, repeatCount));
- InputEvent* repeatEvent = mWindow->consume();
- ASSERT_NE(nullptr, repeatEvent);
-
- ASSERT_EQ(InputEventType::KEY, repeatEvent->getType());
-
- KeyEvent* repeatKeyEvent = static_cast<KeyEvent*>(repeatEvent);
- uint32_t eventAction = repeatKeyEvent->getAction();
- EXPECT_EQ(AKEY_EVENT_ACTION_DOWN, eventAction);
- EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount());
+ mWindow->consumeKeyEvent(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithRepeatCount(repeatCount)));
}
void sendAndConsumeKeyUp(int32_t deviceId) {
@@ -5862,7 +5806,7 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFrom
GTEST_SKIP() << "Flaky test (b/270393106)";
sendAndConsumeKeyDown(/*deviceId=*/1);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- InputEvent* repeatEvent = mWindow->consume();
+ KeyEvent* repeatEvent = mWindow->consumeKey();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER,
IdGenerator::getSource(repeatEvent->getId()));
@@ -5875,7 +5819,7 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEvent
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- InputEvent* repeatEvent = mWindow->consume();
+ KeyEvent* repeatEvent = mWindow->consumeKey();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
int32_t id = repeatEvent->getId();
EXPECT_EQ(idSet.end(), idSet.find(id));
@@ -5935,14 +5879,14 @@ protected:
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
@@ -5951,13 +5895,13 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch)
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDownNoRepeat(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test inject a key down without display id specified.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
@@ -5970,8 +5914,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus)
AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDownNoRepeat(mDispatcher))
- << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
windowInPrimary->assertNoEvents();
windowInSecondary->consumeFocusEvent(false);
windowInSecondary->assertNoEvents();
@@ -5986,7 +5929,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -5995,7 +5938,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -6004,7 +5947,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
// Lift up the touch from the second display
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInSecondary->consumeMotionUp(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionUp(SECOND_DISPLAY_ID);
@@ -6013,7 +5956,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
// If specific a display, it will dispatch to the focused window of particular display,
// or it will dispatch to the focused window of focused display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -6030,7 +5973,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -6053,7 +5996,8 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay)
secondWindowInPrimary->consumeFocusEvent(true);
// Test inject a key down.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->assertNoEvents();
@@ -6068,14 +6012,14 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) {
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
@@ -6089,14 +6033,14 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) {
// Test inject a move motion event, no window/monitor should receive the event.
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
SECOND_DISPLAY_ID, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
windowInSecondary->assertNoEvents();
@@ -6239,12 +6183,7 @@ protected:
InputEventInjectionSync::WAIT_FOR_RESULT, 100ms,
policyFlags | additionalPolicyFlags));
- InputEvent* received = mWindow->consume();
- ASSERT_NE(nullptr, received);
- ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
- ASSERT_EQ(received->getType(), InputEventType::KEY);
- KeyEvent& keyEvent = static_cast<KeyEvent&>(*received);
- ASSERT_EQ(flags, keyEvent.getFlags());
+ mWindow->consumeKeyEvent(AllOf(WithDeviceId(resolvedDeviceId), WithFlags(flags)));
}
void testInjectedMotion(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId,
@@ -6274,12 +6213,7 @@ protected:
InputEventInjectionSync::WAIT_FOR_RESULT, 100ms,
policyFlags | additionalPolicyFlags));
- InputEvent* received = mWindow->consume();
- ASSERT_NE(nullptr, received);
- ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
- ASSERT_EQ(received->getType(), InputEventType::MOTION);
- MotionEvent& motionEvent = static_cast<MotionEvent&>(*received);
- ASSERT_EQ(flags, motionEvent.getFlags());
+ mWindow->consumeMotionEvent(AllOf(WithFlags(flags), WithDeviceId(resolvedDeviceId)));
}
private:
@@ -6355,7 +6289,7 @@ protected:
// the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{20, 20}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeMotionDown();
@@ -6369,7 +6303,8 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Succe
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT,
+ {20, 20}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -6381,7 +6316,7 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPo
// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDownNoRepeat(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
@@ -6394,7 +6329,7 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMo
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_TOUCH_POINT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -6412,7 +6347,7 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, NoFocusChangeFlag) {
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(20).y(20))
.addFlag(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)
.build();
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, event))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, event))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
@@ -6454,28 +6389,24 @@ protected:
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
const std::vector<PointF>& points) {
const std::string name = window->getName();
- InputEvent* event = window->consume();
-
- ASSERT_NE(nullptr, event) << name.c_str()
- << ": consumer should have returned non-NULL event.";
+ MotionEvent* motionEvent = window->consumeMotion();
- ASSERT_EQ(InputEventType::MOTION, event->getType())
- << name.c_str() << ": expected MotionEvent, got " << *event;
+ ASSERT_NE(nullptr, motionEvent)
+ << name.c_str() << ": consumer should have returned non-NULL event.";
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- assertMotionAction(expectedAction, motionEvent.getAction());
- ASSERT_EQ(points.size(), motionEvent.getPointerCount());
+ assertMotionAction(expectedAction, motionEvent->getAction());
+ ASSERT_EQ(points.size(), motionEvent->getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
float expectedY = points[i].y;
- EXPECT_EQ(expectedX, motionEvent.getX(i))
+ EXPECT_EQ(expectedX, motionEvent->getX(i))
<< "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent.getX(i);
- EXPECT_EQ(expectedY, motionEvent.getY(i))
+ << ", got " << motionEvent->getX(i);
+ EXPECT_EQ(expectedY, motionEvent->getY(i))
<< "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent.getY(i);
+ << ", got " << motionEvent->getY(i);
}
}
@@ -6688,16 +6619,17 @@ class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
}
protected:
+ static constexpr std::chrono::duration SPY_TIMEOUT = 200ms;
std::shared_ptr<FakeApplicationHandle> mApplication;
sp<FakeWindowHandle> mWindow;
static constexpr PointF WINDOW_LOCATION = {20, 20};
void tapOnWindow() {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
}
@@ -6707,7 +6639,7 @@ protected:
spy->setTrustedOverlay(true);
spy->setFocusable(false);
spy->setSpy(true);
- spy->setDispatchingTimeout(30ms);
+ spy->setDispatchingTimeout(SPY_TIMEOUT);
mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
return spy;
}
@@ -6724,7 +6656,7 @@ TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) {
// Send a regular key and respond, which should not cause an ANR.
TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher));
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -6736,8 +6668,8 @@ TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) {
mWindow->consumeFocusEvent(false);
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, CONSUME_TIMEOUT_EVENT_EXPECTED,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not go to window because we have no focused window.
@@ -6756,7 +6688,7 @@ TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) {
// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
@@ -6774,7 +6706,7 @@ TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
// Send a key to the app and have the app not respond right away.
TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
// Inject a key, and don't respond - expect that ANR is called.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6790,7 +6722,7 @@ TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
// taps on the window work as normal
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
mDispatcher->waitForIdle();
@@ -6800,7 +6732,7 @@ TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
InputEventInjectionSync::WAIT_FOR_RESULT, 50ms, /*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6851,8 +6783,8 @@ TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr)
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 50ms, /*allowKeyRepeat=*/false);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms, /*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration appTimeout =
mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6874,16 +6806,13 @@ TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
mWindow->consumeFocusEvent(false);
// Once a focused event arrives, we get an ANR for this application
- const InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 50ms);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
+ ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
// Future focused events get dropped right away
- ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(*mDispatcher));
ASSERT_TRUE(mDispatcher->waitForIdle());
mWindow->assertNoEvents();
}
@@ -6899,14 +6828,14 @@ TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
*/
TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// Now send ACTION_UP, with identical timestamp
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
@@ -6923,7 +6852,7 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowAnr) {
sp<FakeWindowHandle> spy = addSpyWindow();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
mWindow->consumeMotionDown();
@@ -6945,9 +6874,9 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnKey)
sp<FakeWindowHandle> spy = addSpyWindow();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT));
mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher, ADISPLAY_ID_DEFAULT));
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -6992,19 +6921,20 @@ TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnMoti
}
TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) {
- mDispatcher->setMonitorDispatchingTimeoutForTest(30ms);
+ mDispatcher->setMonitorDispatchingTimeoutForTest(SPY_TIMEOUT);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
const std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
- mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(30ms, monitor.getToken(), MONITOR_PID);
+ mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(SPY_TIMEOUT, monitor.getToken(),
+ MONITOR_PID);
monitor.finishEvent(*consumeSeq);
monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
@@ -7046,7 +6976,7 @@ TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) {
// it.
TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -7066,12 +6996,12 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
/**
* If a window is processing a motion event, and then a key event comes in, the key event should
- * not to to the focused window until the motion is processed.
+ * not get delivered to the focused window until the motion is processed.
*
* Warning!!!
* This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
* and the injection timeout that we specify when injecting the key.
- * We must have the injection timeout (10ms) be smaller than
+ * We must have the injection timeout (100ms) be smaller than
* KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
*
* If that value changes, this test should also change.
@@ -7091,13 +7021,15 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
// we will receive INJECTION_TIMED_OUT as the result.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 50ms);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::WAIT_FOR_RESULT, 100ms);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
// Key will not be sent to the window, yet, because the window is still processing events
// and the key remains pending, waiting for the touch events to be processed
- std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // Make sure that `assertNoEvents` doesn't wait too long, because it could cause an ANR.
+ // Rely here on the fact that it uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mWindow->assertNoEvents();
std::this_thread::sleep_for(500ms);
// if we wait long enough though, dispatcher will give up, and still send the key
@@ -7126,11 +7058,13 @@ TEST_F(InputDispatcherSingleWindowAnr,
// Don't finish the events yet, and send a key
// Injection is async, so it will succeed
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
// At this point, key is still pending, and should not be sent to the application yet.
- std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // Make sure the `assertNoEvents` check doesn't take too long. It uses
+ // CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mWindow->assertNoEvents();
// Now tap down again. It should cause the pending key to go to the focused window right away.
tapOnWindow();
@@ -7242,10 +7176,10 @@ protected:
private:
void tap(const PointF& location) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
}
};
@@ -7254,7 +7188,7 @@ private:
// should be ANR'd first.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -7265,7 +7199,7 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
mFakePolicy->assertNotifyAnrWasNotCalled();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
ASSERT_TRUE(unfocusedSequenceNum);
@@ -7346,10 +7280,10 @@ TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
// Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
// valid touch target
@@ -7370,7 +7304,7 @@ TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
// If you tap outside of all windows, there will not be ANR
TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
LOCATION_OUTSIDE_ALL_WINDOWS));
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -7383,7 +7317,7 @@ TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
{{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
@@ -7397,13 +7331,13 @@ TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
/**
* If a window is processing a motion event, and then a key event comes in, the key event should
- * not to to the focused window until the motion is processed.
+ * not get delivered to the focused window until the motion is processed.
* If a different window becomes focused at this time, the key should go to that window instead.
*
* Warning!!!
* This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
* and the injection timeout that we specify when injecting the key.
- * We must have the injection timeout (10ms) be smaller than
+ * We must have the injection timeout (100ms) be smaller than
* KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
*
* If that value changes, this test should also change.
@@ -7424,13 +7358,15 @@ TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) {
// window even if motions are still being processed.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not be sent to the window, yet, because the window is still processing events
- // and the key remains pending, waiting for the touch events to be processed
- std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
- ASSERT_FALSE(keySequenceNum);
+ // and the key remains pending, waiting for the touch events to be processed.
+ // Make sure `assertNoEvents` doesn't take too long. It uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED
+ // under the hood.
+ static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms);
+ mFocusedWindow->assertNoEvents();
// Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
mFocusedWindow->setFocusable(false);
@@ -7514,7 +7450,7 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) {
TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
std::shared_ptr<FakeApplicationHandle> focusedApplication =
std::make_shared<FakeApplicationHandle>();
- focusedApplication->setDispatchingTimeout(100ms);
+ focusedApplication->setDispatchingTimeout(200ms);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
mFocusedWindow->setFocusable(false);
@@ -7527,8 +7463,8 @@ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_
// 'focusedApplication' will get blamed if this timer completes.
// Key will not be sent anywhere because we have no focused window. It will remain pending.
InputEventInjectionResult result =
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/50ms,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
@@ -7539,7 +7475,7 @@ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_
// simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
// For this test, it means that the key would get delivered to the window once it becomes
// focused.
- std::this_thread::sleep_for(50ms);
+ std::this_thread::sleep_for(100ms);
// Touch unfocused window. This should force the pending key to get dropped.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
@@ -7662,7 +7598,7 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, CanGetFocus) {
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
}
@@ -7674,10 +7610,10 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) {
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
@@ -7687,7 +7623,7 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) {
// window loses focus since one of the windows associated with the token in not focusable
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7699,20 +7635,20 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAnyWindowVisible) {
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
mMirror->setVisible(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
@@ -7722,7 +7658,7 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAnyWindowVisible) {
// window loses focus only after all windows associated with the token become invisible.
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7733,20 +7669,20 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedWhileWindowsAlive) {
// window gets focused
mWindow->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
// single window is removed but the window token remains focused
mDispatcher->onWindowInfosChanged({{*mMirror->getInfo()}, {}, 0, 0});
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
@@ -7754,7 +7690,7 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedWhileWindowsAlive) {
mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::TIMED_OUT";
mWindow->assertNoEvents();
}
@@ -7769,8 +7705,8 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, DeferFocusWhenInvisible) {
// Injected key goes to pending queue.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE));
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
mMirror->setVisible(true);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
@@ -8373,33 +8309,33 @@ protected:
switch (fromSource) {
case AINPUT_SOURCE_TOUCHSCREEN:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
break;
case AINPUT_SOURCE_STYLUS:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(
- mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_STYLUS)
- .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
- .build()));
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_STYLUS)
+ .buttonState(
+ AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::STYLUS)
+ .x(50)
+ .y(50))
+ .build()));
break;
case AINPUT_SOURCE_MOUSE:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(
- mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
- .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
- .x(50)
- .y(50))
- .build()));
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
+ AINPUT_SOURCE_MOUSE)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID,
+ ToolType::MOUSE)
+ .x(50)
+ .y(50))
+ .build()));
break;
default:
FAIL() << "Source " << fromSource << " doesn't support drag and drop";
@@ -8446,7 +8382,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8455,7 +8391,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8464,7 +8400,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
// Move back to original window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8472,7 +8408,8 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
mSecondWindow->consumeDragEvent(true, -50, 50);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
mWindow->assertNoEvents();
@@ -8492,7 +8429,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndPointerDownPilfersPointers) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -8508,7 +8445,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) {
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8517,7 +8454,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) {
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8526,7 +8463,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) {
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
@@ -8540,7 +8477,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
// Move on window and keep button pressed.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
@@ -8552,7 +8489,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
// Move to another window and release button, expect to drop item.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
@@ -8565,7 +8502,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
// nothing to the window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
@@ -8586,7 +8523,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8595,7 +8532,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8604,7 +8541,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
@@ -8618,7 +8555,7 @@ TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
mWindow->setPreventSplitting(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -8631,7 +8568,7 @@ TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(75).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionPointerDown(/*pointerIndex=*/1);
@@ -8643,7 +8580,7 @@ TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
// First down on second window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -8658,7 +8595,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -8674,7 +8611,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
mWindow->consumeDragEvent(false, 50, 50);
@@ -8688,7 +8625,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
@@ -8711,7 +8648,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
// Let second display has a touch state.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN)
.displayId(SECOND_DISPLAY_ID)
@@ -8729,7 +8666,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8738,7 +8675,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
@@ -8747,7 +8684,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
@@ -8760,11 +8697,10 @@ TEST_F(InputDispatcherDragTests, MouseDragAndDrop) {
startDrag(true, AINPUT_SOURCE_MOUSE);
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(50)
.y(50))
.build()))
@@ -8775,11 +8711,10 @@ TEST_F(InputDispatcherDragTests, MouseDragAndDrop) {
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(150)
.y(50))
.build()))
@@ -8790,11 +8725,10 @@ TEST_F(InputDispatcherDragTests, MouseDragAndDrop) {
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
.x(150)
.y(50))
.build()))
@@ -9040,7 +8974,8 @@ TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchOnSecondaryDisplayOnly)
TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInteractedWindow) {
// Interact with the window first.
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
@@ -9105,7 +9040,7 @@ TEST_F(InputDispatcherSpyWindowTest, NoForegroundWindow) {
mDispatcher->onWindowInfosChanged({{*spy->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
@@ -9145,7 +9080,7 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesInputInOrder) {
}
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
std::vector<size_t> eventOrder;
@@ -9183,7 +9118,7 @@ TEST_F(InputDispatcherSpyWindowTest, NotTouchable) {
mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
spy->assertNoEvents();
@@ -9202,19 +9137,19 @@ TEST_F(InputDispatcherSpyWindowTest, TouchableRegion) {
// Inject an event outside the spy window's touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy->assertNoEvents();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionUp();
spy->assertNoEvents();
// Inject an event inside the spy window's touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{5, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9236,7 +9171,7 @@ TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) {
// Inject an event outside the spy window's frame and touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9258,7 +9193,7 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesMultiplePointers) {
{{*spy->getInfo(), *windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowLeft->consumeMotionDown();
@@ -9271,7 +9206,7 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesMultiplePointers) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowRight->consumeMotionDown();
@@ -9290,7 +9225,7 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) {
mDispatcher->onWindowInfosChanged({{*spyRight->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9303,7 +9238,7 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionPointerDown(/*pointerIndex=*/1);
@@ -9327,7 +9262,7 @@ TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) {
// First finger down, no window touched.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -9342,7 +9277,7 @@ TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -9363,11 +9298,11 @@ TEST_F(InputDispatcherSpyWindowTest, UnfocusableSpyDoesNotReceiveKeyEvents) {
setFocusedWindow(window);
window->consumeFocusEvent(true);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
window->consumeKeyDown(ADISPLAY_ID_NONE);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
window->consumeKeyUp(ADISPLAY_ID_NONE);
@@ -9388,7 +9323,7 @@ TEST_F(InputDispatcherPilferPointersTest, PilferPointers) {
{{*spy1->getInfo(), *spy2->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy1->consumeMotionDown();
@@ -9402,7 +9337,7 @@ TEST_F(InputDispatcherPilferPointersTest, PilferPointers) {
// The rest of the gesture should only be sent to the second spy window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy2->consumeMotionMove();
@@ -9420,7 +9355,7 @@ TEST_F(InputDispatcherPilferPointersTest, CanPilferAfterWindowIsRemovedMidStream
mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -9430,7 +9365,7 @@ TEST_F(InputDispatcherPilferPointersTest, CanPilferAfterWindowIsRemovedMidStream
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
@@ -9447,7 +9382,7 @@ TEST_F(InputDispatcherPilferPointersTest, ContinuesToReceiveGestureAfterPilfer)
// First finger down on the window and the spy.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown();
@@ -9466,7 +9401,7 @@ TEST_F(InputDispatcherPilferPointersTest, ContinuesToReceiveGestureAfterPilfer)
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
@@ -9482,7 +9417,7 @@ TEST_F(InputDispatcherPilferPointersTest, ContinuesToReceiveGestureAfterPilfer)
.pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(-5).y(-5))
.build();
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
@@ -9503,7 +9438,7 @@ TEST_F(InputDispatcherPilferPointersTest, PartiallyPilferRequiredPointers) {
// First finger down on the window only
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{150, 150}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9517,7 +9452,7 @@ TEST_F(InputDispatcherPilferPointersTest, PartiallyPilferRequiredPointers) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(10))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown();
@@ -9533,7 +9468,7 @@ TEST_F(InputDispatcherPilferPointersTest, PartiallyPilferRequiredPointers) {
.pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionPointerDown(1);
@@ -9563,7 +9498,7 @@ TEST_F(InputDispatcherPilferPointersTest, PilferAllRequiredPointers) {
// First finger down on both spy and window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{10, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9578,7 +9513,7 @@ TEST_F(InputDispatcherPilferPointersTest, PilferAllRequiredPointers) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionPointerDown(1);
@@ -9606,7 +9541,7 @@ TEST_F(InputDispatcherPilferPointersTest, CanReceivePointersAfterPilfer) {
// First finger down on both window and spy
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{10, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9625,7 +9560,7 @@ TEST_F(InputDispatcherPilferPointersTest, CanReceivePointersAfterPilfer) {
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
@@ -9777,7 +9712,7 @@ struct User {
: mPid(pid), mUid(uid), mDispatcher(dispatcher) {}
InputEventInjectionResult injectTargetedMotion(int32_t action) const {
- return injectMotionEvent(mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
+ return injectMotionEvent(*mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, {100, 200},
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
@@ -9786,7 +9721,7 @@ struct User {
}
InputEventInjectionResult injectTargetedKey(int32_t action) const {
- return inputdispatcher::injectKey(mDispatcher, action, /*repeatCount=*/0, ADISPLAY_ID_NONE,
+ return inputdispatcher::injectKey(*mDispatcher, action, /*repeatCount=*/0, ADISPLAY_ID_NONE,
InputEventInjectionSync::WAIT_FOR_RESULT,
INJECT_EVENT_TIMEOUT, /*allowKeyRepeat=*/false, {mUid},
mPolicyFlags);
@@ -9883,7 +9818,7 @@ TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoAnyWindowWhenNotTarget
// A user that has injection permission can inject into any window.
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT));
randosSpy->consumeMotionDown();
window->consumeMotionDown();
@@ -9891,7 +9826,7 @@ TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoAnyWindowWhenNotTarget
setFocusedWindow(randosSpy);
randosSpy->consumeFocusEvent(true);
- EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+ EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher));
randosSpy->consumeKeyDown(ADISPLAY_ID_NONE);
window->assertNoEvents();
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 477beaf7f6..72fe2af033 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1343,19 +1343,8 @@ protected:
mFakePolicy = sp<FakeInputReaderPolicy>::make();
mFakePointerController = std::make_shared<FakePointerController>();
mFakePolicy->setPointerController(mFakePointerController);
- mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms,
- /*eventDidNotHappenTimeout=*/30ms);
- mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy,
- *mTestListener);
- ASSERT_EQ(mReader->start(), OK);
-
- // Since this test is run on a real device, all the input devices connected
- // to the test device will show up in mReader. We wait for those input devices to
- // show up before beginning the tests.
- ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ setupInputReader();
}
void TearDown() override {
@@ -1376,6 +1365,22 @@ protected:
});
return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
}
+
+ void setupInputReader() {
+ mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms,
+ /*eventDidNotHappenTimeout=*/30ms);
+
+ mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy,
+ *mTestListener);
+ ASSERT_EQ(mReader->start(), OK);
+
+ // Since this test is run on a real device, all the input devices connected
+ // to the test device will show up in mReader. We wait for those input devices to
+ // show up before beginning the tests.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ }
};
TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
@@ -1509,7 +1514,7 @@ TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) {
// --- TouchIntegrationTest ---
-class TouchIntegrationTest : public InputReaderIntegrationTest {
+class BaseTouchIntegrationTest : public InputReaderIntegrationTest {
protected:
const std::string UNIQUE_ID = "local:0";
@@ -1554,7 +1559,55 @@ protected:
InputDeviceInfo mDeviceInfo;
};
-TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) {
+enum class TouchIntegrationTestDisplays { DISPLAY_INTERNAL, DISPLAY_INPUT_PORT, DISPLAY_UNIQUE_ID };
+
+class TouchIntegrationTest : public BaseTouchIntegrationTest,
+ public testing::WithParamInterface<TouchIntegrationTestDisplays> {
+protected:
+ static constexpr std::optional<uint8_t> DISPLAY_PORT = 0;
+ const std::string INPUT_PORT = "uinput_touch/input0";
+
+ void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP();
+#endif
+ if (GetParam() == TouchIntegrationTestDisplays::DISPLAY_INTERNAL) {
+ BaseTouchIntegrationTest::SetUp();
+ return;
+ }
+
+ // setup policy with a input-port or UniqueId association to the display
+ bool isInputPortAssociation =
+ GetParam() == TouchIntegrationTestDisplays::DISPLAY_INPUT_PORT;
+
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ if (isInputPortAssociation) {
+ mFakePolicy->addInputPortAssociation(INPUT_PORT, DISPLAY_PORT.value());
+ } else {
+ mFakePolicy->addInputUniqueIdAssociation(INPUT_PORT, UNIQUE_ID);
+ }
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+
+ InputReaderIntegrationTest::setupInputReader();
+
+ mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT),
+ INPUT_PORT);
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+ // Add a display linked to a physical port or UniqueId.
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ UNIQUE_ID, isInputPortAssociation ? DISPLAY_PORT : NO_PORT,
+ ViewportType::INTERNAL);
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto info = findDeviceByName(mDevice->getName());
+ ASSERT_TRUE(info);
+ mDeviceInfo = *info;
+ }
+};
+
+TEST_P(TouchIntegrationTest, MultiTouchDeviceSource) {
// The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus
// buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button
// presses).
@@ -1562,7 +1615,7 @@ TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) {
mDeviceInfo.getSources());
}
-TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1586,7 +1639,7 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
-TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1642,7 +1695,7 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
* palms, and wants to cancel Pointer 1, then it is safe to simply drop POINTER_1_UP event without
* losing information about non-palm pointers.
*/
-TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
+TEST_P(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1685,7 +1738,7 @@ TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
* In this scenario, the movement of the second pointer just prior to liftoff is ignored, and never
* gets sent to the listener.
*/
-TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
+TEST_P(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1725,7 +1778,7 @@ TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
assertReceivedMotion(AMOTION_EVENT_ACTION_MOVE, {centerPoint + Point(5, 5)});
}
-TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessPalm) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1776,7 +1829,39 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
-TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
+/**
+ * Some drivers historically have reported axis values outside of the range specified in the
+ * evdev axis info. Ensure we don't crash when this happens. For example, a driver may report a
+ * pressure value greater than the reported maximum, since it unclear what specific meaning the
+ * maximum value for pressure has (beyond the maximum value that can be produced by a sensor),
+ * and no units for pressure (resolution) is specified by the evdev documentation.
+ */
+TEST_P(TouchIntegrationTest, AcceptsAxisValuesOutsideReportedRange) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Down with pressure outside the reported range
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendPressure(UinputTouchScreen::RAW_PRESSURE_MAX + 2);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Move to a point outside the reported range
+ mDevice->sendMove(Point(DISPLAY_WIDTH, DISPLAY_HEIGHT) + Point(1, 1));
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+ // Up
+ mDevice->sendUp();
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+}
+
+TEST_P(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
const Point centerPoint = mDevice->getCenterPoint();
// Send down with the pen tool selected. The policy should be notified of the stylus presence.
@@ -1828,19 +1913,69 @@ TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
}
+TEST_P(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Down
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Move
+ mDevice->sendMove(centerPoint + Point(1, 1));
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+ // Connecting an external stylus mid-gesture should not interrupt the ongoing gesture stream.
+ auto externalStylus = createUinputDevice<UinputExternalStylus>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto stylusInfo = findDeviceByName(externalStylus->getName());
+ ASSERT_TRUE(stylusInfo);
+
+ // Move
+ mDevice->sendMove(centerPoint + Point(2, 2));
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+ // Disconnecting an external stylus mid-gesture should not interrupt the ongoing gesture stream.
+ externalStylus.reset();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+
+ // Up
+ mDevice->sendUp();
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+}
+
+INSTANTIATE_TEST_SUITE_P(TouchIntegrationTestDisplayVariants, TouchIntegrationTest,
+ testing::Values(TouchIntegrationTestDisplays::DISPLAY_INTERNAL,
+ TouchIntegrationTestDisplays::DISPLAY_INPUT_PORT,
+ TouchIntegrationTestDisplays::DISPLAY_UNIQUE_ID));
+
// --- StylusButtonIntegrationTest ---
// Verify the behavior of button presses reported by various kinds of styluses, including buttons
// reported by the touchscreen's device, by a fused external stylus, and by an un-fused external
// stylus.
template <typename UinputStylusDevice>
-class StylusButtonIntegrationTest : public TouchIntegrationTest {
+class StylusButtonIntegrationTest : public BaseTouchIntegrationTest {
protected:
void SetUp() override {
#if !defined(__ANDROID__)
GTEST_SKIP();
#endif
- TouchIntegrationTest::SetUp();
+ BaseTouchIntegrationTest::SetUp();
mTouchscreen = mDevice.get();
mTouchscreenInfo = mDeviceInfo;
@@ -1878,8 +2013,8 @@ private:
std::unique_ptr<UinputStylusDevice> mStylusDeviceLifecycleTracker{};
// Hide the base class's device to expose it with a different name for readability.
- using TouchIntegrationTest::mDevice;
- using TouchIntegrationTest::mDeviceInfo;
+ using BaseTouchIntegrationTest::mDevice;
+ using BaseTouchIntegrationTest::mDeviceInfo;
};
using StylusButtonIntegrationTestTypes =
@@ -2131,7 +2266,7 @@ TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) {
// Verify the behavior of an external stylus. An external stylus can report pressure or button
// data independently of the touchscreen, which is then sent as a MotionEvent as part of an
// ongoing stylus gesture that is being emitted by the touchscreen.
-using ExternalStylusIntegrationTest = TouchIntegrationTest;
+using ExternalStylusIntegrationTest = BaseTouchIntegrationTest;
TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
const Point centerPoint = mDevice->getCenterPoint();
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index 7f8d5562ef..fef58ecfe4 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -103,12 +103,16 @@ protected:
mExternalStylusDevices = devices;
}
+ void setPreventingTouchpadTaps(bool prevent) override { mPreventingTouchpadTaps = prevent; }
+ bool isPreventingTouchpadTaps() override { return mPreventingTouchpadTaps; }
+
private:
int32_t mGlobalMetaState;
bool mUpdateGlobalMetaStateWasCalled;
int32_t mGeneration;
std::optional<nsecs_t> mRequestedTimeout;
std::vector<InputDeviceInfo> mExternalStylusDevices;
+ bool mPreventingTouchpadTaps{false};
} mFakeContext;
friend class InputReaderTest;
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index d720a902dc..b6720c5712 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -49,6 +49,9 @@ public:
MOCK_METHOD(void, updateLedMetaState, (int32_t metaState), (override));
MOCK_METHOD(int32_t, getLedMetaState, (), (override));
+
+ MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override));
+ MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override));
};
class MockEventHubInterface : public EventHubInterface {
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
new file mode 100644
index 0000000000..08a5559de4
--- /dev/null
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyboardInputMapper.h"
+
+#include <gtest/gtest.h>
+
+#include "InputMapperTest.h"
+#include "InterfaceMocks.h"
+
+#define TAG "KeyboardInputMapper_test"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+/**
+ * Unit tests for KeyboardInputMapper.
+ */
+class KeyboardInputMapperUnitTest : public InputMapperUnitTest {
+protected:
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ const std::unordered_map<int32_t, int32_t> mKeyCodeMap{{KEY_0, AKEYCODE_0},
+ {KEY_A, AKEYCODE_A},
+ {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT},
+ {KEY_LEFTALT, AKEYCODE_ALT_LEFT},
+ {KEY_RIGHTALT, AKEYCODE_ALT_RIGHT},
+ {KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT},
+ {KEY_RIGHTSHIFT, AKEYCODE_SHIFT_RIGHT},
+ {KEY_FN, AKEYCODE_FUNCTION},
+ {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT},
+ {KEY_RIGHTCTRL, AKEYCODE_CTRL_RIGHT},
+ {KEY_LEFTMETA, AKEYCODE_META_LEFT},
+ {KEY_RIGHTMETA, AKEYCODE_META_RIGHT},
+ {KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK},
+ {KEY_NUMLOCK, AKEYCODE_NUM_LOCK},
+ {KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK}};
+
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+
+ // set key-codes expected in tests
+ for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, scanCode, _, _, _, _, _))
+ .WillRepeatedly(DoAll(SetArgPointee<4>(outKeycode), Return(NO_ERROR)));
+ }
+
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get()));
+
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ }
+
+ void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) {
+ EXPECT_CALL(mMockInputReaderContext, fadePointer)
+ .Times(expectVisible ? 0 : keyCodes.size());
+ for (int32_t keyCode : keyCodes) {
+ process(EV_KEY, keyCode, 1);
+ process(EV_SYN, SYN_REPORT, 0);
+ process(EV_KEY, keyCode, 0);
+ process(EV_SYN, SYN_REPORT, 0);
+ }
+ }
+
+ void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes,
+ const bool expectPrevent) {
+ EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).Times(keyCodes.size());
+ if (expectPrevent) {
+ EXPECT_CALL(mMockInputReaderContext, setPreventingTouchpadTaps(true))
+ .Times(keyCodes.size());
+ }
+ for (int32_t keyCode : keyCodes) {
+ process(EV_KEY, keyCode, 1);
+ process(EV_SYN, SYN_REPORT, 0);
+ process(EV_KEY, keyCode, 0);
+ process(EV_SYN, SYN_REPORT, 0);
+ }
+ }
+};
+
+/**
+ * Pointer visibility should remain unaffected if there is no active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDoesNotHidePointer) {
+ testPointerVisibilityForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectVisible= */ true);
+}
+
+/**
+ * Pointer should hide if there is a active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionHidePointer) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false);
+}
+
+/**
+ * Pointer visibility should remain unaffected by meta keys even if Input Method Connection is
+ * active
+ */
+TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDoesNotHidePointer) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
+ KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA,
+ KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK};
+ testPointerVisibilityForKeys(metaKeys, /* expectVisible= */ true);
+}
+
+/**
+ * Touchpad tap should not be disabled if there is no active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) {
+ testTouchpadTapStateForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectPrevent= */ false);
+}
+
+/**
+ * Touchpad tap should be disabled if there is a active Input Method Connection
+ */
+TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionDisableTouchpadTap) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ testTouchpadTapStateForKeys({KEY_0, KEY_A}, /* expectPrevent= */ true);
+}
+
+/**
+ * Touchpad tap should not be disabled by meta keys even if Input Method Connection is active
+ */
+TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTouchpadTap) {
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
+ KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA,
+ KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK};
+ testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index fc917dd582..41e250f789 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -57,6 +57,18 @@ void TestInputListener::assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs*
"Expected notifyDeviceReset() to have been called."));
}
+void TestInputListener::clearNotifyDeviceResetCalls() {
+ std::scoped_lock<std::mutex> lock(mLock);
+ std::get<std::vector<NotifyDeviceResetArgs>>(mQueues).clear();
+}
+
+void TestInputListener::assertNotifyDeviceResetWasCalled(
+ const ::testing::Matcher<NotifyDeviceResetArgs>& matcher) {
+ NotifyDeviceResetArgs outEventArgs;
+ ASSERT_NO_FATAL_FAILURE(assertNotifyDeviceResetWasCalled(&outEventArgs));
+ ASSERT_THAT(outEventArgs, matcher);
+}
+
void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
ASSERT_NO_FATAL_FAILURE(
assertNotCalled<NotifyDeviceResetArgs>("notifyDeviceReset() should not be called."));
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index deb60483fb..3c5e0146d6 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -43,6 +43,10 @@ public:
void assertNotifyConfigurationChangedWasNotCalled();
+ void clearNotifyDeviceResetCalls();
+
+ void assertNotifyDeviceResetWasCalled(const ::testing::Matcher<NotifyDeviceResetArgs>& matcher);
+
void assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs = nullptr);
void assertNotifyDeviceResetWasNotCalled();
diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/services/inputflinger/tests/TestInputListenerMatchers.cpp
index 65111278e0..1464e6097e 100644
--- a/libs/renderengine/include/renderengine/Framebuffer.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,22 +14,24 @@
* limitations under the License.
*/
-#pragma once
+#include "TestInputListenerMatchers.h"
-#include <cstdint>
+namespace android {
-struct ANativeWindowBuffer;
+WithKeyActionMatcher WithKeyAction(int32_t action) {
+ return WithKeyActionMatcher(action);
+}
-namespace android {
-namespace renderengine {
+WithMotionActionMatcher WithMotionAction(int32_t action) {
+ return WithMotionActionMatcher(action);
+}
-class Framebuffer {
-public:
- virtual ~Framebuffer() = default;
+WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
+ return WithDisplayIdMatcher(displayId);
+}
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- const bool useFramebufferCache) = 0;
-};
+WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
+ return WithDeviceIdMatcher(deviceId);
+}
-} // namespace renderengine
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 70bad7c66b..183383f57d 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -23,53 +23,149 @@
#include <gtest/gtest.h>
#include <input/Input.h>
+#include "NotifyArgs.h"
#include "TestConstants.h"
namespace android {
-MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
- bool matches = action == arg.action;
- if (!matches) {
- *result_listener << "expected action " << MotionEvent::actionToString(action)
- << ", but got " << MotionEvent::actionToString(arg.action);
- }
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- if (!matches) {
- *result_listener << "; ";
- }
- *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
- matches &= (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
- }
- return matches;
-}
-
-MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") {
- *result_listener << "expected action " << KeyEvent::actionToString(action) << ", but got "
- << KeyEvent::actionToString(arg.action);
- return arg.action == action;
-}
-
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
<< inputEventSourceToString(arg.source);
return arg.source == source;
}
-MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
- *result_listener << "expected displayId " << displayId << ", but got " << arg.displayId;
- return arg.displayId == displayId;
-}
+/// Key action
+class WithKeyActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithKeyActionMatcher(int32_t action) : mAction(action) {}
-MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") {
- *result_listener << "expected deviceId " << deviceId << ", but got " << arg.deviceId;
- return arg.deviceId == deviceId;
-}
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mAction == args.action;
+ }
+
+ bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+ return mAction == event.getAction();
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with key action " << KeyEvent::actionToString(mAction);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+WithKeyActionMatcher WithKeyAction(int32_t action);
+
+/// Motion action
+class WithMotionActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ bool matches = mAction == args.action;
+ if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+ bool matches = mAction == event.getAction();
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with motion action " << MotionEvent::actionToString(mAction);
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ *os << " and FLAG_CANCELED";
+ }
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+WithMotionActionMatcher WithMotionAction(int32_t action);
+
+/// Display Id
+class WithDisplayIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDisplayId == args.displayId;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDisplayId == args.displayId;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDisplayId == event.getDisplayId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with display id " << mDisplayId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
+
+private:
+ const int32_t mDisplayId;
+};
+
+WithDisplayIdMatcher WithDisplayId(int32_t displayId);
+
+/// Device Id
+class WithDeviceIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDeviceIdMatcher(int32_t deviceId) : mDeviceId(deviceId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const NotifyDeviceResetArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDeviceId == event.getDeviceId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
+
+private:
+ const int32_t mDeviceId;
+};
+
+WithDeviceIdMatcher WithDeviceId(int32_t deviceId);
MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
*result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
return arg.keyCode == keyCode;
}
+MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
+ return arg.getRepeatCount() == repeatCount;
+}
+
MATCHER_P(WithPointerCount, count, "MotionEvent with specified number of pointers") {
*result_listener << "expected " << count << " pointer(s), but got " << arg.getPointerCount();
return arg.getPointerCount() == count;
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 97a26141e0..19f7bb4409 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -159,10 +159,11 @@ void UinputExternalStylusWithPressure::setPressure(int32_t pressure) {
// --- UinputTouchScreen ---
-UinputTouchScreen::UinputTouchScreen(const Rect& size)
+UinputTouchScreen::UinputTouchScreen(const Rect& size, const std::string& physicalPort)
: UinputKeyboard(DEVICE_NAME, PRODUCT_ID,
{BTN_TOUCH, BTN_TOOL_PEN, BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}),
- mSize(size) {}
+ mSize(size),
+ mPhysicalPort(physicalPort) {}
void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
UinputKeyboard::configureDevice(fd, device);
@@ -176,7 +177,11 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ if (!mPhysicalPort.empty()) {
+ ioctl(fd, UI_SET_PHYS, mPhysicalPort.c_str());
+ }
device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
@@ -190,6 +195,8 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER;
device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX;
+ device->absmin[ABS_MT_PRESSURE] = RAW_PRESSURE_MIN;
+ device->absmax[ABS_MT_PRESSURE] = RAW_PRESSURE_MAX;
}
void UinputTouchScreen::sendSlot(int32_t slot) {
@@ -202,6 +209,7 @@ void UinputTouchScreen::sendTrackingId(int32_t trackingId) {
void UinputTouchScreen::sendDown(const Point& point) {
injectEvent(EV_KEY, BTN_TOUCH, 1);
+ injectEvent(EV_ABS, ABS_MT_PRESSURE, RAW_PRESSURE_MAX);
injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x);
injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
}
@@ -211,6 +219,10 @@ void UinputTouchScreen::sendMove(const Point& point) {
injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
}
+void UinputTouchScreen::sendPressure(int32_t pressure) {
+ injectEvent(EV_ABS, ABS_MT_PRESSURE, pressure);
+}
+
void UinputTouchScreen::sendPointerUp() {
sendTrackingId(0xffffffff);
}
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 51e331df8f..e7010c32a5 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -189,6 +189,7 @@ public:
void sendTrackingId(int32_t trackingId);
void sendDown(const Point& point);
void sendMove(const Point& point);
+ void sendPressure(int32_t pressure);
void sendPointerUp();
void sendUp();
void sendToolType(int32_t toolType);
@@ -197,11 +198,12 @@ public:
const Point getCenterPoint();
protected:
- explicit UinputTouchScreen(const Rect& size);
+ explicit UinputTouchScreen(const Rect& size, const std::string& physicalPort = "");
private:
void configureDevice(int fd, uinput_user_dev* device) override;
const Rect mSize;
+ const std::string mPhysicalPort;
};
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index d7980f564e..9313a89005 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -56,6 +56,15 @@ cc_defaults {
],
fuzz_config: {
cc: ["android-framework-input@google.com"],
+ componentid: 155276,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libinputflinger library",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index e88731aafd..bdedfdfa0c 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -295,7 +295,8 @@ public:
}
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier) override {
+ const InputDeviceIdentifier& identifier,
+ const std::optional<KeyboardLayoutInfo> layoutInfo) override {
return nullptr;
}
std::string getDeviceAlias(const InputDeviceIdentifier& identifier) {
@@ -307,6 +308,7 @@ public:
}
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+ bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); }
};
class FuzzInputListener : public virtual InputListenerInterface {
@@ -355,6 +357,9 @@ public:
void updateLedMetaState(int32_t metaState) override{};
int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
+
+ void setPreventingTouchpadTaps(bool prevent) {}
+ bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
};
template <class Fdp>
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 2523f3b6a6..8b16890a45 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -9,7 +9,7 @@ package {
cc_library_shared {
name: "libpowermanager",
-
+ defaults: ["android.hardware.power-ndk_export_shared"],
srcs: [
"BatterySaverPolicyConfig.cpp",
"CoolingDevice.cpp",
@@ -41,7 +41,6 @@ cc_library_shared {
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
],
export_shared_lib_headers: [
@@ -49,7 +48,6 @@ cc_library_shared {
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
],
cflags: [
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index 03fc38d304..2b5ddb1460 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -23,6 +23,7 @@ package {
cc_benchmark {
name: "libpowermanager_benchmarks",
+ defaults: ["android.hardware.power-ndk_shared"],
srcs: [
"main.cpp",
"PowerHalAidlBenchmarks.cpp",
@@ -41,7 +42,6 @@ cc_benchmark {
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
],
static_libs: [
"libtestUtil",
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 08fcdc8275..6fc96c0959 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -23,6 +23,10 @@ package {
cc_test {
name: "libpowermanager_test",
+ defaults: [
+ "android.hardware.power-ndk_shared",
+ "android.hardware.power-ndk_shared",
+ ],
test_suites: ["device-tests"],
srcs: [
"IThermalManagerTest.cpp",
@@ -52,7 +56,6 @@ cc_test {
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
],
static_libs: [
"libgmock",
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 90d75414d6..cfafc69484 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -63,8 +63,10 @@
#include <sys/types.h>
#include <unistd.h>
+#include <condition_variable>
#include <ctime>
#include <future>
+#include <mutex>
#include <string>
#include <private/android_filesystem_config.h>
@@ -196,6 +198,16 @@ int SensorService::registerRuntimeSensor(
if (mRuntimeSensorCallbacks.find(deviceId) == mRuntimeSensorCallbacks.end()) {
mRuntimeSensorCallbacks.emplace(deviceId, callback);
}
+
+ if (mRuntimeSensorHandler == nullptr) {
+ mRuntimeSensorEventBuffer =
+ new sensors_event_t[SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT];
+ mRuntimeSensorHandler = new RuntimeSensorHandler(this);
+ // Use PRIORITY_URGENT_DISPLAY as the injected sensor events should be dispatched as soon as
+ // possible, and also for consistency within the SensorService.
+ mRuntimeSensorHandler->run("RuntimeSensorHandler", PRIORITY_URGENT_DISPLAY);
+ }
+
return handle;
}
@@ -232,8 +244,9 @@ status_t SensorService::unregisterRuntimeSensor(int handle) {
}
status_t SensorService::sendRuntimeSensorEvent(const sensors_event_t& event) {
- Mutex::Autolock _l(mLock);
+ std::unique_lock<std::mutex> lock(mRutimeSensorThreadMutex);
mRuntimeSensorEventQueue.push(event);
+ mRuntimeSensorsCv.notify_all();
return OK;
}
@@ -458,6 +471,7 @@ void SensorService::onFirstRef() {
const size_t minBufferSize = SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT;
mSensorEventBuffer = new sensors_event_t[minBufferSize];
mSensorEventScratch = new sensors_event_t[minBufferSize];
+ mRuntimeSensorEventBuffer = nullptr;
mMapFlushEventsToConnections = new wp<const SensorEventConnection> [minBufferSize];
mCurrentOperatingMode = NORMAL;
@@ -1055,12 +1069,7 @@ bool SensorService::threadLoop() {
if (count < 0) {
if(count == DEAD_OBJECT && device.isReconnecting()) {
device.reconnect();
- // There are no "real" events at this point, but do not skip the rest of the loop
- // if there are pending runtime events.
- Mutex::Autolock _l(&mLock);
- if (mRuntimeSensorEventQueue.empty()) {
- continue;
- }
+ continue;
} else {
ALOGE("sensor poll failed (%s)", strerror(-count));
break;
@@ -1094,7 +1103,6 @@ bool SensorService::threadLoop() {
recordLastValueLocked(mSensorEventBuffer, count);
// handle virtual sensors
- bool bufferNeedsSorting = false;
if (count && vcount) {
sensors_event_t const * const event = mSensorEventBuffer;
if (!mActiveVirtualSensors.empty()) {
@@ -1130,37 +1138,11 @@ bool SensorService::threadLoop() {
// record the last synthesized values
recordLastValueLocked(&mSensorEventBuffer[count], k);
count += k;
- bufferNeedsSorting = true;
+ sortEventBuffer(mSensorEventBuffer, count);
}
}
}
- // handle runtime sensors
- {
- size_t k = 0;
- while (!mRuntimeSensorEventQueue.empty()) {
- if (count + k >= minBufferSize) {
- ALOGE("buffer too small to hold all events: count=%zd, k=%zu, size=%zu",
- count, k, minBufferSize);
- break;
- }
- mSensorEventBuffer[count + k] = mRuntimeSensorEventQueue.front();
- mRuntimeSensorEventQueue.pop();
- k++;
- }
- if (k) {
- // record the last synthesized values
- recordLastValueLocked(&mSensorEventBuffer[count], k);
- count += k;
- bufferNeedsSorting = true;
- }
- }
-
- if (bufferNeedsSorting) {
- // sort the buffer by time-stamps
- sortEventBuffer(mSensorEventBuffer, count);
- }
-
// handle backward compatibility for RotationVector sensor
if (halVersion < SENSORS_DEVICE_API_VERSION_1_0) {
for (int i = 0; i < count; i++) {
@@ -1239,7 +1221,7 @@ bool SensorService::threadLoop() {
bool needsWakeLock = false;
for (const sp<SensorEventConnection>& connection : activeConnections) {
connection->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
- mMapFlushEventsToConnections);
+ mMapFlushEventsToConnections);
needsWakeLock |= connection->needsWakeLock();
// If the connection has one-shot sensors, it may be cleaned up after first trigger.
// Early check for one-shot sensors.
@@ -1258,6 +1240,46 @@ bool SensorService::threadLoop() {
return false;
}
+void SensorService::processRuntimeSensorEvents() {
+ size_t count = 0;
+ const size_t maxBufferSize = SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT;
+
+ {
+ std::unique_lock<std::mutex> lock(mRutimeSensorThreadMutex);
+
+ if (mRuntimeSensorEventQueue.empty()) {
+ mRuntimeSensorsCv.wait(lock, [this] { return !mRuntimeSensorEventQueue.empty(); });
+ }
+
+ // Pop the events from the queue into the buffer until it's empty or the buffer is full.
+ while (!mRuntimeSensorEventQueue.empty()) {
+ if (count >= maxBufferSize) {
+ ALOGE("buffer too small to hold all events: count=%zd, size=%zu", count,
+ maxBufferSize);
+ break;
+ }
+ mRuntimeSensorEventBuffer[count] = mRuntimeSensorEventQueue.front();
+ mRuntimeSensorEventQueue.pop();
+ count++;
+ }
+ }
+
+ if (count) {
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+
+ recordLastValueLocked(mRuntimeSensorEventBuffer, count);
+ sortEventBuffer(mRuntimeSensorEventBuffer, count);
+
+ for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
+ connection->sendEvents(mRuntimeSensorEventBuffer, count, /* scratch= */ nullptr,
+ /* mapFlushEventsToConnections= */ nullptr);
+ if (connection->hasOneShotSensors()) {
+ cleanupAutoDisabledSensorLocked(connection, mRuntimeSensorEventBuffer, count);
+ }
+ }
+ }
+}
+
sp<Looper> SensorService::getLooper() const {
return mLooper;
}
@@ -1305,6 +1327,14 @@ bool SensorService::SensorEventAckReceiver::threadLoop() {
return false;
}
+bool SensorService::RuntimeSensorHandler::threadLoop() {
+ ALOGD("new thread RuntimeSensorHandler");
+ do {
+ mService->processRuntimeSensorEvents();
+ } while (!Thread::exitPending());
+ return false;
+}
+
void SensorService::recordLastValueLocked(
const sensors_event_t* buffer, size_t count) {
for (size_t i = 0; i < count; i++) {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 545f6c25d5..bf4310119d 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -42,6 +42,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <condition_variable>
+#include <mutex>
#include <queue>
#include <unordered_map>
#include <unordered_set>
@@ -208,6 +210,7 @@ private:
class SensorEventAckReceiver;
class SensorRecord;
class SensorRegistrationInfo;
+ class RuntimeSensorHandler;
// Promoting a SensorEventConnection or SensorDirectConnection from wp to sp must be done with
// mLock held, but destroying that sp must be done unlocked to avoid a race condition that
@@ -264,6 +267,14 @@ private:
SortedVector< wp<SensorDirectConnection> > mDirectConnections;
};
+ class RuntimeSensorHandler : public Thread {
+ sp<SensorService> const mService;
+ public:
+ virtual bool threadLoop();
+ explicit RuntimeSensorHandler(const sp<SensorService>& service) : mService(service) {
+ }
+ };
+
// If accessing a sensor we need to make sure the UID has access to it. If
// the app UID is idle then it cannot access sensors and gets no trigger
// events, no on-change events, flush event behavior does not change, and
@@ -368,6 +379,8 @@ private:
// Thread interface
virtual bool threadLoop();
+ void processRuntimeSensorEvents();
+
// ISensorServer interface
virtual Vector<Sensor> getSensorList(const String16& opPackageName);
virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName);
@@ -512,6 +525,10 @@ private:
uint32_t mSocketBufferSize;
sp<Looper> mLooper;
sp<SensorEventAckReceiver> mAckReceiver;
+ sp<RuntimeSensorHandler> mRuntimeSensorHandler;
+ // Mutex and CV used to notify the mRuntimeSensorHandler thread that there are new events.
+ std::mutex mRutimeSensorThreadMutex;
+ std::condition_variable mRuntimeSensorsCv;
// protected by mLock
mutable Mutex mLock;
@@ -519,7 +536,7 @@ private:
std::unordered_set<int> mActiveVirtualSensors;
SensorConnectionHolder mConnectionHolder;
bool mWakeLockAcquired;
- sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
+ sensors_event_t *mSensorEventBuffer, *mSensorEventScratch, *mRuntimeSensorEventBuffer;
// WARNING: these SensorEventConnection instances must not be promoted to sp, except via
// modification to add support for them in ConnectionSafeAutolock
wp<const SensorEventConnection> * mMapFlushEventsToConnections;
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index e939d51f5b..1baf397905 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -87,7 +87,7 @@ void testInvalidSharedMem_NoCrash(SensorManager &mgr) {
int ret = mgr.createDirectChannel(
kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, resourceHandle);
- // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart
+ // Should not succeed (ret != OK) and the device runtime shouldn't restart
printf("createInvalidDirectChannel=%d\n", ret);
// Secondary test: correct channel creation & destruction (should print 0)
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 326645e596..0101c1712a 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,6 +26,7 @@ cc_defaults {
name: "libsurfaceflinger_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "android.hardware.power-ndk_shared",
"librenderengine_deps",
"libtimestats_deps",
"surfaceflinger_defaults",
@@ -48,7 +49,6 @@ cc_defaults {
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.power-V4-ndk",
"libbase",
"libbinder",
"libbinder_ndk",
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index b56b252d9f..fefc04054d 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -29,7 +29,9 @@
#include <set>
#include <unordered_map>
-#define BUFFER_CACHE_MAX_SIZE 64
+// 4096 is based on 64 buffers * 64 layers. Once this limit is reached, the least recently used
+// buffer is uncached before the new buffer is cached.
+#define BUFFER_CACHE_MAX_SIZE 4096
namespace android {
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 3426495297..06c5e4c933 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -11,6 +11,7 @@ cc_defaults {
name: "libcompositionengine_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "android.hardware.power-ndk_shared",
"librenderengine_deps",
"libtimestats_deps",
"surfaceflinger_defaults",
@@ -27,7 +28,6 @@ cc_defaults {
"android.hardware.graphics.composer@2.4",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
"libbase",
"libcutils",
"libgui",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 09bc46747a..1a8644e9fe 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -67,9 +67,6 @@ struct CompositionRefreshArgs {
// Controls how the color mode is chosen for an output
OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
- // If not Dataspace::UNKNOWN, overrides the dataspace on each output
- ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
-
// Forces a color mode on the outputs being refreshed
ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index df44e75625..9c80cacf6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -87,10 +87,6 @@ public:
// Returns true if HWC for this profile supports the dataspace
virtual bool isDataspaceSupported(ui::Dataspace) const = 0;
- // Returns the target dataspace for picked color mode and dataspace
- virtual ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace,
- ui::Dataspace colorSpaceAgnosticDataspace) const = 0;
-
// Debugging
virtual void dump(std::string&) const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index a3d86398cf..370c7cf223 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -136,7 +136,6 @@ public:
ui::ColorMode mode{ui::ColorMode::NATIVE};
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
- ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
};
// Use internally to incrementally compute visibility/coverage
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
index 9bc0e681c7..3aad04c1a3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -55,7 +55,6 @@ public:
const HdrCapabilities& getHdrCapabilities() const override;
bool isDataspaceSupported(ui::Dataspace) const override;
- ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace) const override;
void dump(std::string&) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 28c6e92b06..6cb1e7e4c2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -116,9 +116,6 @@ struct OutputCompositionState {
// Current active dataspace
ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
- // Current target dataspace
- ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
-
std::optional<android::HWComposer::DeviceRequestedChanges> previousDeviceRequestedChanges{};
bool previousDeviceRequestedSuccess = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
index 1aaebea295..a9045211af 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -43,8 +43,6 @@ public:
MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
MOCK_CONST_METHOD1(isDataspaceSupported, bool(ui::Dataspace));
- MOCK_CONST_METHOD3(getTargetDataspace,
- ui::Dataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index 7e020ee1c1..bdaa1d0ae1 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -41,12 +41,10 @@ inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs,
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
- return lhs.textureName == rhs.textureName &&
- lhs.useTextureFiltering == rhs.useTextureFiltering &&
+ return lhs.useTextureFiltering == rhs.useTextureFiltering &&
lhs.textureTransform == rhs.textureTransform &&
lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
- lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
- lhs.maxLuminanceNits == rhs.maxLuminanceNits;
+ lhs.isOpaque == rhs.isOpaque && lhs.maxLuminanceNits == rhs.maxLuminanceNits;
}
inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs,
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 85fc09549b..f2acfc9e6e 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -107,14 +107,9 @@ void Display::setColorTransform(const compositionengine::CompositionRefreshArgs&
}
void Display::setColorProfile(const ColorProfile& colorProfile) {
- const ui::Dataspace targetDataspace =
- getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
- colorProfile.colorSpaceAgnosticDataspace);
-
if (colorProfile.mode == getState().colorMode &&
colorProfile.dataspace == getState().dataspace &&
- colorProfile.renderIntent == getState().renderIntent &&
- targetDataspace == getState().targetDataspace) {
+ colorProfile.renderIntent == getState().renderIntent) {
return;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index a7c45129b1..97725ea636 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -220,17 +220,6 @@ DisplayColorProfile::DisplayColorProfile(const DisplayColorProfileCreationArgs&
minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
- if (args.hasWideColorGamut) {
- // insert HDR10/HLG as we will force client composition for HDR10/HLG
- // layers
- if (!hasHDR10Support()) {
- types.push_back(ui::Hdr::HDR10);
- }
-
- if (!hasHLGSupport()) {
- types.push_back(ui::Hdr::HLG);
- }
- }
mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
}
@@ -391,17 +380,6 @@ bool DisplayColorProfile::isDataspaceSupported(Dataspace dataspace) const {
}
}
-ui::Dataspace DisplayColorProfile::getTargetDataspace(ColorMode mode, Dataspace dataspace,
- Dataspace colorSpaceAgnosticDataspace) const {
- if (isHdrColorMode(mode)) {
- return Dataspace::UNKNOWN;
- }
- if (colorSpaceAgnosticDataspace != ui::Dataspace::UNKNOWN) {
- return colorSpaceAgnosticDataspace;
- }
- return dataspace;
-}
-
void DisplayColorProfile::dump(std::string& out) const {
out.append(" Composition Display Color State:");
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index b1b5708473..0b11e747ca 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -261,22 +261,16 @@ void Output::setColorTransform(const compositionengine::CompositionRefreshArgs&
}
void Output::setColorProfile(const ColorProfile& colorProfile) {
- ui::Dataspace targetDataspace =
- getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
- colorProfile.colorSpaceAgnosticDataspace);
-
auto& outputState = editState();
if (outputState.colorMode == colorProfile.mode &&
outputState.dataspace == colorProfile.dataspace &&
- outputState.renderIntent == colorProfile.renderIntent &&
- outputState.targetDataspace == targetDataspace) {
+ outputState.renderIntent == colorProfile.renderIntent) {
return;
}
outputState.colorMode = colorProfile.mode;
outputState.dataspace = colorProfile.dataspace;
outputState.renderIntent = colorProfile.renderIntent;
- outputState.targetDataspace = targetDataspace;
mRenderSurface->setBufferDataspace(colorProfile.dataspace);
@@ -901,13 +895,19 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr
compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
for (auto* layer : getOutputLayersOrderedByZ()) {
- auto* compState = layer->getLayerFE().getCompositionState();
+ const auto* compState = layer->getLayerFE().getCompositionState();
// If any layer has a sideband stream, we will disable blurs. In that case, we don't
// want to force client composition because of the blur.
if (compState->sidebandStream != nullptr) {
return nullptr;
}
+
+ // If RenderEngine cannot render protected content, we cannot blur.
+ if (compState->hasProtectedContent &&
+ !getCompositionEngine().getRenderEngine().supportsProtectedContent()) {
+ return nullptr;
+ }
if (compState->isOpaque) {
continue;
}
@@ -984,8 +984,7 @@ compositionengine::Output::ColorProfile Output::pickColorProfile(
const compositionengine::CompositionRefreshArgs& refreshArgs) const {
if (refreshArgs.outputColorSetting == OutputColorSetting::kUnmanaged) {
return ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC,
- refreshArgs.colorSpaceAgnosticDataspace};
+ ui::RenderIntent::COLORIMETRIC};
}
ui::Dataspace hdrDataSpace;
@@ -1031,8 +1030,7 @@ compositionengine::Output::ColorProfile Output::pickColorProfile(
mDisplayColorProfile->getBestColorMode(bestDataSpace, intent, &outDataSpace, &outMode,
&outRenderIntent);
- return ColorProfile{outMode, outDataSpace, outRenderIntent,
- refreshArgs.colorSpaceAgnosticDataspace};
+ return ColorProfile{outMode, outDataSpace, outRenderIntent};
}
void Output::beginFrame() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 9713e79fe3..39cf67165a 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -52,7 +52,6 @@ void OutputCompositionState::dump(std::string& out) const {
dumpVal(out, "colorMode", toString(colorMode), colorMode);
dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
dumpVal(out, "dataspace", toString(dataspace), dataspace);
- dumpVal(out, "targetDataspace", toString(targetDataspace), targetDataspace);
out.append("\n ");
dumpVal(out, "colorTransformMatrix", colorTransformMatrix);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0ac0ecb727..4fe6927d80 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include <DisplayHardware/Hal.h>
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -26,7 +25,7 @@
#include <cstdint>
#include "system/graphics-base-v1.0.h"
-#include <ui/DataspaceUtils.h>
+#include <ui/HdrRenderTypeUtils.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -312,14 +311,26 @@ void OutputLayer::updateCompositionState(
}
}
+ auto pixelFormat = layerFEState->buffer ? std::make_optional(static_cast<ui::PixelFormat>(
+ layerFEState->buffer->getPixelFormat()))
+ : std::nullopt;
+
+ auto hdrRenderType =
+ getHdrRenderType(outputState.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+
// Determine the output dependent dataspace for this layer. If it is
// colorspace agnostic, it just uses the dataspace chosen for the output to
// avoid the need for color conversion.
- state.dataspace = layerFEState->isColorspaceAgnostic &&
- outputState.targetDataspace != ui::Dataspace::UNKNOWN
- ? outputState.targetDataspace
+ // For now, also respect the colorspace agnostic flag if we're drawing to HDR, to avoid drastic
+ // luminance shift. TODO(b/292162273): we should check if that's true though.
+ state.dataspace = layerFEState->isColorspaceAgnostic && hdrRenderType == HdrRenderType::SDR
+ ? outputState.dataspace
: layerFEState->dataspace;
+ // re-get HdrRenderType after the dataspace gets changed.
+ hdrRenderType =
+ getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+
// Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
// We do this here instead of in buffer info so that dumpsys can still report layers that are
// using the 170M transfer. Also we only do this if the colorspace is not agnostic for the
@@ -334,7 +345,7 @@ void OutputLayer::updateCompositionState(
// For hdr content, treat the white point as the display brightness - HDR content should not be
// boosted or dimmed.
// If the layer explicitly requests to disable dimming, then don't dim either.
- if (isHdrDataspace(state.dataspace) ||
+ if (hdrRenderType == HdrRenderType::GENERIC_HDR ||
getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
getOutput().getState().displayBrightnessNits == 0.f || !layerFEState->dimmingEnabled) {
state.dimmingRatio = 1.f;
@@ -343,8 +354,7 @@ void OutputLayer::updateCompositionState(
float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
// RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
// range that we may need to re-adjust to the current display conditions
- if ((state.dataspace & HAL_DATASPACE_RANGE_MASK) == HAL_DATASPACE_RANGE_EXTENDED &&
- layerFEState->currentHdrSdrRatio > 1.01f) {
+ if (hdrRenderType == HdrRenderType::DISPLAY_HDR) {
layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
}
state.dimmingRatio =
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index f439caf9e1..8dab6ce61b 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -34,7 +34,7 @@ LayerState::LayerState(compositionengine::OutputLayer* layer)
[](const mat4& mat) {
using namespace std::string_literals;
std::vector<std::string> split =
- base::Split(std::string(mat.asString().string()), "\n"s);
+ base::Split(std::string(mat.asString().c_str()), "\n"s);
split.pop_back(); // Strip the last (empty) line
return split;
}}) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 54133d92b0..5e6cade56f 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -216,32 +216,32 @@ void Planner::dump(const Vector<String16>& args, std::string& result) {
base::StringAppendF(&result,
"Expected two layer stack hashes, e.g. '--planner %s "
"<left_hash> <right_hash>'\n",
- command.string());
+ command.c_str());
return;
}
if (args.size() > 4) {
base::StringAppendF(&result,
"Too many arguments found, expected '--planner %s <left_hash> "
"<right_hash>'\n",
- command.string());
+ command.c_str());
return;
}
const String8 leftHashString(args[2]);
size_t leftHash = 0;
- int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash);
+ int fieldsRead = sscanf(leftHashString.c_str(), "%zx", &leftHash);
if (fieldsRead != 1) {
base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
- leftHashString.string());
+ leftHashString.c_str());
return;
}
const String8 rightHashString(args[3]);
size_t rightHash = 0;
- fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash);
+ fieldsRead = sscanf(rightHashString.c_str(), "%zx", &rightHash);
if (fieldsRead != 1) {
base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
- rightHashString.string());
+ rightHashString.c_str());
return;
}
@@ -252,22 +252,22 @@ void Planner::dump(const Vector<String16>& args, std::string& result) {
if (args.size() < 3) {
base::StringAppendF(&result,
"Expected a layer stack hash, e.g. '--planner %s <hash>'\n",
- command.string());
+ command.c_str());
return;
}
if (args.size() > 3) {
base::StringAppendF(&result,
"Too many arguments found, expected '--planner %s <hash>'\n",
- command.string());
+ command.c_str());
return;
}
const String8 hashString(args[2]);
size_t hash = 0;
- const int fieldsRead = sscanf(hashString.string(), "%zx", &hash);
+ const int fieldsRead = sscanf(hashString.c_str(), "%zx", &hash);
if (fieldsRead != 1) {
base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
- hashString.string());
+ hashString.c_str());
return;
}
@@ -279,20 +279,20 @@ void Planner::dump(const Vector<String16>& args, std::string& result) {
} else if (command == "--similar" || command == "-s") {
if (args.size() < 3) {
base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n",
- command.string());
+ command.c_str());
return;
}
if (args.size() > 3) {
base::StringAppendF(&result,
"Too many arguments found, expected '--planner %s <plan>'\n",
- command.string());
+ command.c_str());
return;
}
const String8 planString(args[2]);
- std::optional<Plan> plan = Plan::fromString(std::string(planString.string()));
+ std::optional<Plan> plan = Plan::fromString(std::string(planString.c_str()));
if (!plan) {
- base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string());
+ base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.c_str());
return;
}
@@ -302,7 +302,7 @@ void Planner::dump(const Vector<String16>& args, std::string& result) {
} else if (command == "--layers" || command == "-l") {
mFlattener.dumpLayers(result);
} else {
- base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
+ base::StringAppendF(&result, "Unknown command '%s'\n\n", command.c_str());
dumpUsage(result);
}
return;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index 21b9aa93ea..03a97dc770 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -282,39 +282,6 @@ TEST_F(DisplayColorProfileTest, ctorUsesOrDefaultsLuminanceValuesFromInputArgs)
}
}
-TEST_F(DisplayColorProfileTest, ctorSignalsHdrSupportForAnyWideColorGamutDevice) {
- {
- // If the output does not profile wide color gamut, then no HDR modes
- // will be profileed in the generated HDR capabilities.
- auto profile = ProfileFactory().setHasWideColorGamut(false).build();
-
- EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), IsEmpty());
- }
-
- {
- // If the HWC does not show profile for certain HDR modes, then the
- // generated HDR capabilities will indicate profile anyway.
- auto profile = ProfileFactory().setHasWideColorGamut(true).build();
-
- EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
- EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
- EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
- }
-
- {
- // If the HWC profiles the HDR modes, then the generated capabilities
- // still has one entry for each HDR type.
- auto profile = ProfileFactory()
- .setHasWideColorGamut(true)
- .addHdrTypes({Hdr::HLG, Hdr::HDR10})
- .build();
-
- EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
- EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
- EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
- }
-}
-
/* ------------------------------------------------------------------------
* DisplayColorProfile::hasRenderIntent
*/
@@ -646,29 +613,5 @@ TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHlgSuppor
EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
}
-/*
- * RenderSurface::getTargetDataspace()
- */
-
-TEST_F(DisplayColorProfileTest, getTargetDataspaceWorks) {
- auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
-
- // For a non-HDR colorspace with no colorSpaceAgnosticDataspace override,
- // the input dataspace should be returned.
- EXPECT_EQ(Dataspace::DISPLAY_P3,
- profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
- Dataspace::UNKNOWN));
-
- // If colorSpaceAgnosticDataspace is set, its value should be returned
- EXPECT_EQ(Dataspace::V0_SRGB,
- profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
- Dataspace::V0_SRGB));
-
- // For an HDR colorspace, Dataspace::UNKNOWN should be returned.
- EXPECT_EQ(Dataspace::UNKNOWN,
- profile.getTargetDataspace(ColorMode::BT2100_PQ, Dataspace::BT2020_PQ,
- Dataspace::UNKNOWN));
-}
-
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 9be6bc2075..027004acf6 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -403,23 +403,18 @@ TEST_F(DisplaySetColorModeTest, setsModeUnlessNoChange) {
mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
mDisplay->setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile));
- EXPECT_CALL(*colorProfile, getTargetDataspace(_, _, _))
- .WillRepeatedly(Return(ui::Dataspace::UNKNOWN));
-
// These values are expected to be the initial state.
ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
- ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
// Otherwise if the values are unchanged, nothing happens
mDisplay->setColorProfile(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
// Otherwise if the values are different, updates happen
EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
@@ -429,13 +424,11 @@ TEST_F(DisplaySetColorModeTest, setsModeUnlessNoChange) {
.Times(1);
mDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
}
TEST_F(DisplaySetColorModeTest, doesNothingForVirtualDisplay) {
@@ -448,19 +441,13 @@ TEST_F(DisplaySetColorModeTest, doesNothingForVirtualDisplay) {
virtualDisplay->setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(colorProfile));
- EXPECT_CALL(*colorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
-
- virtualDisplay->setColorProfile(
- ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN});
+ virtualDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3,
+ ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay->getState().colorMode);
EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().targetDataspace);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index aa83883e95..9039d16aeb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -642,7 +642,7 @@ TEST_F(OutputLayerUpdateCompositionStateTest,
TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly) {
mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
- mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
+ mOutputState.dataspace = ui::Dataspace::V0_SCRGB;
// If the layer is not colorspace agnostic, the output layer dataspace
// should use the layers requested colorspace.
@@ -659,11 +659,18 @@ TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly
mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
+
+ // If the output is HDR, then don't blind the user with a colorspace agnostic dataspace
+ // drawing all white
+ mOutputState.dataspace = ui::Dataspace::BT2020_PQ;
+
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+ EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
}
TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceWith170mReplacement) {
mLayerFEState.dataspace = ui::Dataspace::TRANSFER_SMPTE_170M;
- mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
mOutputState.treat170mAsSrgb = false;
mLayerFEState.isColorspaceAgnostic = false;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 9e0e7b5a53..ebf9a2b2e2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -175,12 +175,10 @@ const Rect OutputTest::kDefaultDisplaySize{100, 200};
using ColorProfile = compositionengine::Output::ColorProfile;
void dumpColorProfile(ColorProfile profile, std::string& result, const char* name) {
- android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d] %s[%d]) ", name,
+ android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d]) ", name,
toString(profile.mode).c_str(), profile.mode,
toString(profile.dataspace).c_str(), profile.dataspace,
- toString(profile.renderIntent).c_str(), profile.renderIntent,
- toString(profile.colorSpaceAgnosticDataspace).c_str(),
- profile.colorSpaceAgnosticDataspace);
+ toString(profile.renderIntent).c_str(), profile.renderIntent);
}
// Checks for a ColorProfile match
@@ -192,8 +190,7 @@ MATCHER_P(ColorProfileEq, expected, "") {
*result_listener << buf;
return (expected.mode == arg.mode) && (expected.dataspace == arg.dataspace) &&
- (expected.renderIntent == arg.renderIntent) &&
- (expected.colorSpaceAgnosticDataspace == arg.colorSpaceAgnosticDataspace);
+ (expected.renderIntent == arg.renderIntent);
}
/*
@@ -540,20 +537,14 @@ using OutputSetColorProfileTest = OutputTest;
TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) {
using ColorProfile = Output::ColorProfile;
- EXPECT_CALL(*mDisplayColorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput->getState().colorMode);
EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput->getState().dataspace);
EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput->getState().renderIntent);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput->getState().targetDataspace);
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
@@ -561,19 +552,12 @@ TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) {
TEST_F(OutputSetColorProfileTest, doesNothingIfNoChange) {
using ColorProfile = Output::ColorProfile;
- EXPECT_CALL(*mDisplayColorProfile,
- getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::Dataspace::UNKNOWN))
- .WillOnce(Return(ui::Dataspace::UNKNOWN));
-
mOutput->editState().colorMode = ui::ColorMode::DISPLAY_P3;
mOutput->editState().dataspace = ui::Dataspace::DISPLAY_P3;
mOutput->editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
- mOutput->editState().targetDataspace = ui::Dataspace::UNKNOWN;
mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
- ui::RenderIntent::TONE_MAP_COLORIMETRIC,
- ui::Dataspace::UNKNOWN});
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC});
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
}
@@ -2133,12 +2117,11 @@ TEST_F(OutputUpdateColorProfileTest, setsAColorProfileWhenUnmanaged) {
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput,
- setColorProfile(ColorProfileEq(
- ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN})));
+ setColorProfile(
+ ColorProfileEq(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC})));
mRefreshArgs.outputColorSetting = OutputColorSetting::kUnmanaged;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mOutput.updateColorProfile(mRefreshArgs);
}
@@ -2148,7 +2131,6 @@ struct OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile
OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() {
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
}
struct ExpectBestColorModeCallResultUsedToSetColorProfileState
@@ -2163,8 +2145,7 @@ struct OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile
SetArgPointee<4>(renderIntent)));
EXPECT_CALL(getInstance()->mOutput,
setColorProfile(
- ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent,
- ui::Dataspace::UNKNOWN})));
+ ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent})));
return nextState<ExecuteState>();
}
};
@@ -2191,55 +2172,6 @@ TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
.execute();
}
-struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile
- : public OutputUpdateColorProfileTest {
- OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() {
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
- EXPECT_CALL(*mDisplayColorProfile,
- getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _))
- .WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN),
- SetArgPointee<3>(ui::ColorMode::NATIVE),
- SetArgPointee<4>(ui::RenderIntent::COLORIMETRIC)));
- mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- }
-
- struct IfColorSpaceAgnosticDataspaceSetToState
- : public CallOrderStateMachineHelper<TestType, IfColorSpaceAgnosticDataspaceSetToState> {
- [[nodiscard]] auto ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace dataspace) {
- getInstance()->mRefreshArgs.colorSpaceAgnosticDataspace = dataspace;
- return nextState<ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState>();
- }
- };
-
- struct ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState
- : public CallOrderStateMachineHelper<
- TestType, ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState> {
- [[nodiscard]] auto thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(
- ui::Dataspace dataspace) {
- EXPECT_CALL(getInstance()->mOutput,
- setColorProfile(ColorProfileEq(
- ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
- ui::RenderIntent::COLORIMETRIC, dataspace})));
- return nextState<ExecuteState>();
- }
- };
-
- // Call this member function to start using the mini-DSL defined above.
- [[nodiscard]] auto verify() { return IfColorSpaceAgnosticDataspaceSetToState::make(this); }
-};
-
-TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, DisplayP3) {
- verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::DISPLAY_P3)
- .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::DISPLAY_P3)
- .execute();
-}
-
-TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, V0_SRGB) {
- verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::V0_SRGB)
- .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::V0_SRGB)
- .execute();
-}
-
struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference
: public OutputUpdateColorProfileTest {
// Internally the implementation looks through the dataspaces of all the
@@ -2248,7 +2180,6 @@ struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference
OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
@@ -2368,7 +2299,6 @@ struct OutputUpdateColorProfileTest_ForceOutputColorOverrides
OutputUpdateColorProfileTest_ForceOutputColorOverrides() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020;
@@ -2424,7 +2354,6 @@ TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, DisplayP3_Overrid
struct OutputUpdateColorProfileTest_Hdr : public OutputUpdateColorProfileTest {
OutputUpdateColorProfileTest_Hdr() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
}
@@ -2703,7 +2632,6 @@ struct OutputUpdateColorProfile_AffectsChosenRenderIntentTest
OutputUpdateColorProfile_AffectsChosenRenderIntentTest() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
- mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ;
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 32bd890aee..70ccaf883e 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -38,7 +38,6 @@
#include <log/log.h>
#include <system/window.h>
-#include "Display/DisplaySnapshot.h"
#include "DisplayDevice.h"
#include "FrontEnd/DisplayInfo.h"
#include "HdrSdrRatioOverlay.h"
@@ -214,10 +213,7 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps rend
ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
mRefreshRateSelector->setActiveMode(modeId, renderFps);
-
- if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
- }
+ updateRefreshRateOverlayRate(displayFps, renderFps);
}
status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
@@ -231,10 +227,18 @@ status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
return BAD_VALUE;
}
mUpcomingActiveMode = info;
- ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.modeOpt->modePtr->getFps().getIntValue());
- return mHwComposer.setActiveModeWithConstraints(getPhysicalId(),
- info.modeOpt->modePtr->getHwcId(), constraints,
- outTimeline);
+ mIsModeSetPending = true;
+
+ const auto& pendingMode = *info.modeOpt->modePtr;
+ ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), pendingMode.getFps().getIntValue());
+
+ return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), pendingMode.getHwcId(),
+ constraints, outTimeline);
+}
+
+void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps displayFps, Fps renderFps) {
+ setActiveMode(modeId, displayFps, renderFps);
+ mIsModeSetPending = false;
}
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -466,7 +470,7 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool sh
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+ updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps, setByHwc);
}
void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index e92125a45d..a3fa701be2 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -218,6 +218,8 @@ public:
return mUpcomingActiveMode;
}
+ bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; }
+
scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
return mRefreshRateSelector->getActiveMode();
}
@@ -229,6 +231,9 @@ public:
hal::VsyncPeriodChangeTimeline* outTimeline)
REQUIRES(kMainThreadContext);
+ void finalizeModeChange(DisplayModeId, Fps displayFps, Fps renderFps)
+ REQUIRES(kMainThreadContext);
+
scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
// Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice.
@@ -313,7 +318,9 @@ private:
ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) =
{ftl::Concat("DesiredActiveModeChanged-", getId().value).c_str(), false};
+
ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
+ bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
};
struct DisplayDeviceState {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index aaf2523338..0c2b77de7d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -311,6 +311,14 @@ bool Display::hasCapability(DisplayCapability capability) const {
}
Error Display::supportsDoze(bool* outSupport) const {
+ {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ if (!mDisplayCapabilities) {
+ // The display has not turned on since boot, so DOZE support is unknown.
+ ALOGW("%s: haven't queried capabilities yet!", __func__);
+ return Error::NO_RESOURCES;
+ }
+ }
*outSupport = hasCapability(DisplayCapability::DOZE);
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3177b33538..a9bb928f59 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -616,19 +616,29 @@ status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mo
ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str());
{
bool supportsDoze = false;
- auto error = hwcDisplay->supportsDoze(&supportsDoze);
- if (error != hal::Error::NONE) {
- LOG_HWC_ERROR("supportsDoze", error, displayId);
- }
+ const auto queryDozeError = hwcDisplay->supportsDoze(&supportsDoze);
- if (!supportsDoze) {
+ // queryDozeError might be NO_RESOURCES, in the case of a display that has never
+ // been turned on. In that case, attempt to set to DOZE anyway.
+ if (!supportsDoze && queryDozeError == hal::Error::NONE) {
mode = hal::PowerMode::ON;
}
- error = hwcDisplay->setPowerMode(mode);
+ auto error = hwcDisplay->setPowerMode(mode);
if (error != hal::Error::NONE) {
LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(), error,
displayId);
+ // If the display had never been turned on, so its doze
+ // support was unknown, it may truly not support doze. Try
+ // switching it to ON instead.
+ if (queryDozeError == hal::Error::NO_RESOURCES) {
+ ALOGD("%s: failed to set %s to %s. Trying again with ON", __func__,
+ to_string(displayId).c_str(), to_string(mode).c_str());
+ error = hwcDisplay->setPowerMode(hal::PowerMode::ON);
+ if (error != hal::Error::NONE) {
+ LOG_HWC_ERROR("setPowerMode(ON)", error, displayId);
+ }
+ }
}
}
break;
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index c26edb5d22..0788d1abce 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -54,7 +54,6 @@ struct LayerCreationArgs {
gui::LayerMetadata metadata;
pid_t ownerPid;
uid_t ownerUid;
- uint32_t textureName;
uint32_t sequence;
bool addToRoot = true;
wp<IBinder> parentHandle = nullptr;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index d389a799ad..80bedf4a39 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "SurfaceFlinger"
#include "LayerSnapshot.h"
+#include "Layer.h"
namespace android::surfaceflinger::frontend {
@@ -117,7 +118,6 @@ LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
sequence = static_cast<int32_t>(state.id);
name = state.name;
debugName = state.debugName;
- textureName = state.textureName;
premultipliedAlpha = state.premultipliedAlpha;
inputInfo.name = state.name;
inputInfo.id = static_cast<int32_t>(uniqueSequence);
@@ -345,10 +345,9 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate
clientChanges = requested.what;
changes = requested.changes;
contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
- // TODO(b/238781169) scope down the changes to only buffer updates.
- hasReadyFrame = requested.hasReadyFrame();
+ hasReadyFrame = requested.autoRefresh;
sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
- updateSurfaceDamage(requested, hasReadyFrame, forceFullDamage, surfaceDamage);
+ updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage);
if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) {
transparentRegionHint = requested.transparentRegion;
@@ -364,7 +363,7 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate
geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
}
if (forceUpdate || requested.what & layer_state_t::eDataspaceChanged) {
- dataspace = requested.dataspace;
+ dataspace = Layer::translateDataspace(requested.dataspace);
}
if (forceUpdate || requested.what & layer_state_t::eExtendedRangeBrightnessChanged) {
currentHdrSdrRatio = requested.currentHdrSdrRatio;
@@ -469,19 +468,9 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate
if (forceUpdate ||
requested.what &
(layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
- layer_state_t::eApiChanged)) {
- isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
- requested.api == NATIVE_WINDOW_API_MEDIA &&
- requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
- }
-
- if (forceUpdate ||
- requested.what &
- (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged |
layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged)) {
- forceClientComposition = isHdrY410 || shadowSettings.length > 0 ||
- requested.blurRegions.size() > 0 || stretchEffect.hasEffect();
+ forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect();
}
if (forceUpdate ||
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 1afcef9e44..7537a39060 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -64,7 +64,6 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState {
int32_t sequence;
std::string name;
std::string debugName;
- uint32_t textureName;
bool contentOpaque;
bool layerOpaqueFlagSet;
RoundedCornerState roundedCorner;
@@ -72,14 +71,13 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState {
Rect transformedBoundsWithoutTransparentRegion;
renderengine::ShadowSettings shadowSettings;
bool premultipliedAlpha;
- bool isHdrY410;
ui::Transform parentTransform;
Rect bufferSize;
Rect croppedBufferSize;
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
gui::LayerMetadata layerMetadata;
gui::LayerMetadata relativeLayerMetadata;
- bool hasReadyFrame;
+ bool hasReadyFrame; // used in post composition to check if there is another frame ready
ui::Transform localTransformInverse;
gui::WindowInfo inputInfo;
ui::Transform localTransform;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 23cfe928f5..159d0f028d 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -854,8 +854,9 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a
}
if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
- snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
- updateRoundedCorner(snapshot, requested, parentSnapshot);
+ snapshot.changes.any(RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::BufferUsageFlags)) {
+ updateRoundedCorner(snapshot, requested, parentSnapshot, args);
}
if (forceUpdate || snapshot.clientChanges & layer_state_t::eShadowRadiusChanged ||
@@ -870,8 +871,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a
}
// computed snapshot properties
- snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
- requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
+ snapshot.forceClientComposition =
+ snapshot.shadowSettings.length > 0 || snapshot.stretchEffect.hasEffect();
snapshot.contentOpaque = snapshot.isContentOpaque();
snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() &&
snapshot.color.a == 1.f;
@@ -886,7 +887,12 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a
void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot,
const RequestedLayerState& requested,
- const LayerSnapshot& parentSnapshot) {
+ const LayerSnapshot& parentSnapshot,
+ const Args& args) {
+ if (args.skipRoundCornersWhenProtected && requested.isProtected()) {
+ snapshot.roundedCorner = RoundedCornerState();
+ return;
+ }
snapshot.roundedCorner = RoundedCornerState();
RoundedCornerState parentRoundedCorner;
if (parentSnapshot.roundedCorner.hasRoundedCorners()) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index d361605875..3d64b362ea 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -54,6 +54,7 @@ public:
std::unordered_set<uint32_t> excludeLayerIds;
const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata;
const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap;
+ bool skipRoundCornersWhenProtected = false;
};
LayerSnapshotBuilder();
@@ -103,7 +104,7 @@ private:
bool parentIsRelative, const Args& args);
static void resetRelativeState(LayerSnapshot& snapshot);
static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
- const LayerSnapshot& parentSnapshot);
+ const LayerSnapshot& parentSnapshot, const Args& args);
void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index a4777d1148..a5d556328d 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -51,7 +51,6 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args)
name(args.name + "#" + std::to_string(args.sequence)),
canBeRoot(args.addToRoot),
layerCreationFlags(args.flags),
- textureName(args.textureName),
ownerUid(args.ownerUid),
ownerPid(args.ownerPid),
parentId(args.parentId),
@@ -147,6 +146,8 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta
const ui::Size oldBufferSize = hadBuffer
? ui::Size(externalTexture->getWidth(), externalTexture->getHeight())
: ui::Size();
+ const uint64_t oldUsageFlags = hadBuffer ? externalTexture->getUsage() : 0;
+
const bool hadSideStream = sidebandStream != nullptr;
const layer_state_t& clientState = resolvedComposerState.state;
const bool hadBlur = hasBlur();
@@ -177,6 +178,10 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta
changes |= RequestedLayerState::Changes::BufferSize;
changes |= RequestedLayerState::Changes::Geometry;
}
+ const uint64_t usageFlags = hasBuffer ? externalTexture->getUsage() : 0;
+ if (oldUsageFlags != usageFlags) {
+ changes |= RequestedLayerState::Changes::BufferUsageFlags;
+ }
}
if (hasBuffer != hadBuffer) {
@@ -570,6 +575,10 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const {
return true;
}
+bool RequestedLayerState::isProtected() const {
+ return externalTexture && externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
+}
+
void RequestedLayerState::clearChanges() {
what = 0;
changes.clear();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 1c19d6d5fc..8eff22bbe4 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -56,6 +56,7 @@ struct RequestedLayerState : layer_state_t {
Animation = 1u << 17,
BufferSize = 1u << 18,
GameMode = 1u << 19,
+ BufferUsageFlags = 1u << 20,
};
static Rect reduce(const Rect& win, const Region& exclude);
RequestedLayerState(const LayerCreationArgs&);
@@ -85,6 +86,7 @@ struct RequestedLayerState : layer_state_t {
bool willReleaseBufferOnLatch() const;
bool backpressureEnabled() const;
bool isSimpleBufferUpdate(const layer_state_t&) const;
+ bool isProtected() const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
@@ -93,7 +95,6 @@ struct RequestedLayerState : layer_state_t {
const std::string name;
bool canBeRoot = false;
const uint32_t layerCreationFlags;
- const uint32_t textureName;
// The owner of the layer. If created from a non system process, it will be the calling uid.
// If created from a system process, the value can be passed in.
const gui::Uid ownerUid;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 0d3c6ebd47..ca7c3c25f7 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -188,21 +188,36 @@ bool TransactionHandler::hasPendingTransactions() {
}
void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId,
- sp<ITransactionCompletedListener>& listener,
- const std::string& reason) {
- if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) !=
- mStalledTransactions.end()) {
- return;
- }
+ StalledTransactionInfo stalledTransactionInfo) {
+ std::lock_guard lock{mStalledMutex};
+ mStalledTransactions.emplace(transactionId, std::move(stalledTransactionInfo));
+}
+
+void TransactionHandler::removeFromStalledTransactions(uint64_t transactionId) {
+ std::lock_guard lock{mStalledMutex};
+ mStalledTransactions.erase(transactionId);
+}
- mStalledTransactions.push_back(transactionId);
- listener->onTransactionQueueStalled(String8(reason.c_str()));
+std::optional<TransactionHandler::StalledTransactionInfo>
+TransactionHandler::getStalledTransactionInfo(pid_t pid) {
+ std::lock_guard lock{mStalledMutex};
+ for (auto [_, stalledTransactionInfo] : mStalledTransactions) {
+ if (pid == stalledTransactionInfo.pid) {
+ return stalledTransactionInfo;
+ }
+ }
+ return std::nullopt;
}
-void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
- auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
- if (it != mStalledTransactions.end()) {
- mStalledTransactions.erase(it);
+void TransactionHandler::onLayerDestroyed(uint32_t layerId) {
+ std::lock_guard lock{mStalledMutex};
+ for (auto it = mStalledTransactions.begin(); it != mStalledTransactions.end();) {
+ if (it->second.layerId == layerId) {
+ it = mStalledTransactions.erase(it);
+ } else {
+ it++;
+ }
}
}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 04183bcdb8..00f6bcebe6 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -18,6 +18,7 @@
#include <semaphore.h>
#include <cstdint>
+#include <optional>
#include <vector>
#include <LocklessQueue.h>
@@ -63,9 +64,18 @@ public:
std::vector<TransactionState> flushTransactions();
void addTransactionReadyFilter(TransactionFilter&&);
void queueTransaction(TransactionState&&);
- void onTransactionQueueStalled(uint64_t transactionId, sp<ITransactionCompletedListener>&,
- const std::string& reason);
+
+ struct StalledTransactionInfo {
+ pid_t pid;
+ uint32_t layerId;
+ std::string layerName;
+ uint64_t bufferId;
+ uint64_t frameNumber;
+ };
+ void onTransactionQueueStalled(uint64_t transactionId, StalledTransactionInfo);
void removeFromStalledTransactions(uint64_t transactionId);
+ std::optional<StalledTransactionInfo> getStalledTransactionInfo(pid_t pid);
+ void onLayerDestroyed(uint32_t layerId);
private:
// For unit tests
@@ -81,7 +91,10 @@ private:
LocklessQueue<TransactionState> mLocklessTransactionQueue;
std::atomic<size_t> mPendingTransactionCount = 0;
ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
- std::vector<uint64_t> mStalledTransactions;
+
+ std::mutex mStalledMutex;
+ std::unordered_map<uint64_t /* transactionId */, StalledTransactionInfo> mStalledTransactions
+ GUARDED_BY(mStalledMutex);
};
} // namespace surfaceflinger::frontend
} // namespace android
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
index 9eefbe463d..278833244c 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.cpp
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -18,14 +18,22 @@
#define LOG_TAG "HdrLayerInfoReporter"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
+#include <inttypes.h>
#include <utils/Trace.h>
#include "HdrLayerInfoReporter.h"
namespace android {
+using base::StringAppendF;
+
void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) {
ATRACE_CALL();
+ if (mHdrInfoHistory.size() == 0 || mHdrInfoHistory.back().info != info) {
+ mHdrInfoHistory.next() = EventHistoryEntry{info};
+ }
+
std::vector<sp<gui::IHdrLayerInfoListener>> toInvoke;
{
std::scoped_lock lock(mMutex);
@@ -62,4 +70,15 @@ void HdrLayerInfoReporter::removeListener(const sp<gui::IHdrLayerInfoListener>&
mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
}
+void HdrLayerInfoReporter::dump(std::string& result) const {
+ for (size_t i = 0; i < mHdrInfoHistory.size(); i++) {
+ const auto& event = mHdrInfoHistory[i];
+ const auto& info = event.info;
+ StringAppendF(&result,
+ "%" PRId64 ": numHdrLayers(%d), size(%dx%d), flags(%X), desiredRatio(%.2f)\n",
+ event.timestamp, info.numberOfHdrLayers, info.maxW, info.maxH, info.flags,
+ info.maxDesiredHdrSdrRatio);
+ }
+}
+
} // namespace android \ No newline at end of file
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index bf7c7753d2..614f33fbce 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -19,9 +19,11 @@
#include <android-base/thread_annotations.h>
#include <android/gui/IHdrLayerInfoListener.h>
#include <binder/IBinder.h>
+#include <utils/Timers.h>
#include <unordered_map>
+#include "Utils/RingBuffer.h"
#include "WpHash.h"
namespace android {
@@ -79,6 +81,8 @@ public:
return !mListeners.empty();
}
+ void dump(std::string& result) const;
+
private:
mutable std::mutex mMutex;
@@ -88,6 +92,17 @@ private:
};
std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+
+ struct EventHistoryEntry {
+ nsecs_t timestamp = -1;
+ HdrLayerInfo info;
+
+ EventHistoryEntry() {}
+
+ EventHistoryEntry(const HdrLayerInfo& info) : info(info) { timestamp = systemTime(); }
+ };
+
+ utils::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory;
};
} // namespace android \ No newline at end of file
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3a41f1599b..30dce114d9 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -50,10 +50,10 @@
#include <stdlib.h>
#include <sys/types.h>
#include <system/graphics-base-v1.0.h>
-#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
+#include <ui/HdrRenderTypeUtils.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <ui/Transform.h>
@@ -149,7 +149,6 @@ Layer::Layer(const LayerCreationArgs& args)
args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
mLayerCreationFlags(args.flags),
mBorderEnabled(false),
- mTextureName(args.textureName),
mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
ALOGV("Creating Layer %s", getDebugName());
@@ -214,7 +213,6 @@ Layer::Layer(const LayerCreationArgs& args)
mSnapshot->sequence = sequence;
mSnapshot->name = getDebugName();
- mSnapshot->textureName = mTextureName;
mSnapshot->premultipliedAlpha = mPremultipliedAlpha;
mSnapshot->parentTransform = {};
}
@@ -236,13 +234,6 @@ Layer::~Layer() {
mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
mBufferInfo.mFence);
}
- if (!isClone()) {
- // The original layer and the clone layer share the same texture. Therefore, only one of
- // the layers, in this case the original layer, needs to handle the deletion. The original
- // layer and the clone should be removed at the same time so there shouldn't be any issue
- // with the clone layer trying to use the deleted texture.
- mFlinger->deleteTextureAsync(mTextureName);
- }
const int32_t layerId = getSequence();
mFlinger->mTimeStats->onDestroy(layerId);
mFlinger->mFrameTracer->onDestroy(layerId);
@@ -594,8 +585,8 @@ void Layer::prepareBasicGeometryCompositionState() {
snapshot->localTransformInverse = snapshot->localTransform.inverse();
snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
snapshot->alpha = alpha;
- snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
- snapshot->blurRegions = drawingState.blurRegions;
+ snapshot->backgroundBlurRadius = getBackgroundBlurRadius();
+ snapshot->blurRegions = getBlurRegions();
snapshot->stretchEffect = getStretchEffect();
}
@@ -659,13 +650,12 @@ void Layer::preparePerFrameCompositionState() {
// Force client composition for special cases known only to the front-end.
// Rounded corners no longer force client composition, since we may use a
// hole punch so that the layer will appear to have rounded corners.
- if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 ||
- snapshot->stretchEffect.hasEffect()) {
+ if (drawShadows() || snapshot->stretchEffect.hasEffect()) {
snapshot->forceClientComposition = true;
}
// If there are no visible region changes, we still need to update blur parameters.
- snapshot->blurRegions = drawingState.blurRegions;
- snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+ snapshot->blurRegions = getBlurRegions();
+ snapshot->backgroundBlurRadius = getBackgroundBlurRadius();
// Layer framerate is used in caching decisions.
// Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
@@ -836,12 +826,12 @@ uint32_t Layer::doTransaction(uint32_t flags) {
mFlinger->mUpdateInputInfo = true;
}
- commitTransaction(mDrawingState);
+ commitTransaction();
return flags;
}
-void Layer::commitTransaction(State&) {
+void Layer::commitTransaction() {
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -2111,6 +2101,13 @@ const std::vector<BlurRegion> Layer::getBlurRegions() const {
}
RoundedCornerState Layer::getRoundedCornerState() const {
+ // Today's DPUs cannot do rounded corners. If RenderEngine cannot render
+ // protected content, remove rounded corners from protected content so it
+ // can be rendered by the DPU.
+ if (isProtected() && !mFlinger->getRenderEngine().supportsProtectedContent()) {
+ return {};
+ }
+
// Get parent settings
RoundedCornerState parentSettings;
const auto& parent = mDrawingParent.promote();
@@ -3618,7 +3615,6 @@ Rect Layer::computeBufferCrop(const State& s) {
sp<Layer> Layer::createClone(uint32_t mirrorRootId) {
LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
- args.textureName = mTextureName;
sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
layer->setInitialValuesForClone(sp<Layer>::fromExisting(this), mirrorRootId);
return layer;
@@ -3881,13 +3877,6 @@ bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const {
return true;
}
-bool Layer::isHdrY410() const {
- // pixel format is HDR Y410 masquerading as RGBA_1010102
- return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
- mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
- mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
-}
-
sp<LayerFE> Layer::getCompositionEngineLayerFE() const {
// There's no need to get a CE Layer if the layer isn't going to draw anything.
return hasSomethingToDraw() ? mLegacyLayerFE : nullptr;
@@ -4289,7 +4278,6 @@ void Layer::updateSnapshot(bool updateGeometry) {
snapshot->contentOpaque = isOpaque(mDrawingState);
snapshot->layerOpaqueFlagSet =
(mDrawingState.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
- snapshot->isHdrY410 = isHdrY410();
sp<Layer> p = mDrawingParent.promote();
if (p != nullptr) {
snapshot->parentTransform = p->getTransform();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5d77657415..8a65d9dba6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -23,8 +23,6 @@
#include <gui/WindowInfo.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/vec4.h>
-#include <renderengine/Mesh.h>
-#include <renderengine/Texture.h>
#include <sys/types.h>
#include <ui/BlurRegion.h>
#include <ui/FloatRect.h>
@@ -427,12 +425,10 @@ public:
bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const;
// from graphics API
- ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+ static ui::Dataspace translateDataspace(ui::Dataspace dataspace);
void updateCloneBufferInfo();
uint64_t mPreviousFrameNumber = 0;
- bool isHdrY410() const;
-
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
@@ -912,6 +908,7 @@ public:
void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
mTransformHint = transformHint;
}
+ void commitTransaction();
// Keeps track of the previously presented layer stacks. This is used to get
// the release fences from the correct displays when we release the last buffer
// from the layer.
@@ -932,7 +929,6 @@ protected:
void preparePerFrameCompositionState();
void preparePerFrameBufferCompositionState();
void preparePerFrameEffectsCompositionState();
- virtual void commitTransaction(State& stateToCommit);
void gatherBufferInfo();
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
@@ -1195,8 +1191,6 @@ private:
void setTransformHintLegacy(ui::Transform::RotationFlags);
void resetDrawingStateBufferInfo();
- const uint32_t mTextureName;
-
// Transform hint provided to the producer. This must be accessed holding
// the mStateLock.
ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index f855f278c3..a0024d52d4 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -16,7 +16,7 @@
// #define LOG_NDEBUG 0
#undef LOG_TAG
-#define LOG_TAG "LayerFE"
+#define LOG_TAG "SurfaceFlinger"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <gui/GLConsumer.h>
@@ -223,9 +223,7 @@ void LayerFE::prepareBufferStateClientComposition(
layerSettings.source.buffer.buffer = mSnapshot->externalTexture;
layerSettings.source.buffer.isOpaque = mSnapshot->contentOpaque;
layerSettings.source.buffer.fence = mSnapshot->acquireFence;
- layerSettings.source.buffer.textureName = mSnapshot->textureName;
layerSettings.source.buffer.usePremultipliedAlpha = mSnapshot->premultipliedAlpha;
- layerSettings.source.buffer.isY410BT2020 = mSnapshot->isHdrY410;
bool hasSmpte2086 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
bool hasCta861_3 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
float maxLuminance = 0.f;
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 473409715f..3270e4c95c 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1075131
+
adyabr@google.com
alecmouri@google.com
chaviw@google.com
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 2529095f99..2bb8c3ff41 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -148,8 +148,8 @@ std::string toString(const RefreshRateSelector::PolicyVariant& policy) {
} // namespace
auto RefreshRateSelector::createFrameRateModes(
- std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const
- -> std::vector<FrameRateMode> {
+ const Policy& policy, std::function<bool(const DisplayMode&)>&& filterModes,
+ const FpsRange& renderRange) const -> std::vector<FrameRateMode> {
struct Key {
Fps fps;
int32_t group;
@@ -202,11 +202,25 @@ auto RefreshRateSelector::createFrameRateModes(
ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(),
to_string(mode->getFps()).c_str());
} else {
- // We might need to update the map as we found a lower refresh rate
- if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) {
+ // If the primary physical range is a single rate, prefer to stay in that rate
+ // even if there is a lower physical refresh rate available. This would cause more
+ // cases to stay within the primary physical range
+ const Fps existingModeFps = existingIter->second->second->getFps();
+ const bool existingModeIsPrimaryRange = policy.primaryRangeIsSingleRate() &&
+ policy.primaryRanges.physical.includes(existingModeFps);
+ const bool newModeIsPrimaryRange = policy.primaryRangeIsSingleRate() &&
+ policy.primaryRanges.physical.includes(mode->getFps());
+ if (newModeIsPrimaryRange == existingModeIsPrimaryRange) {
+ // We might need to update the map as we found a lower refresh rate
+ if (isStrictlyLess(mode->getFps(), existingModeFps)) {
+ existingIter->second = it;
+ ALOGV("%s: changing %s (%s) as we found a lower physical rate", __func__,
+ to_string(fps).c_str(), to_string(mode->getFps()).c_str());
+ }
+ } else if (newModeIsPrimaryRange) {
existingIter->second = it;
- ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(),
- to_string(mode->getFps()).c_str());
+ ALOGV("%s: changing %s (%s) to stay in the primary range", __func__,
+ to_string(fps).c_str(), to_string(mode->getFps()).c_str());
}
}
}
@@ -302,6 +316,19 @@ float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const Layer
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
+ using fps_approx_ops::operator<;
+ if (refreshRate < 60_Hz) {
+ const bool favorsAtLeast60 =
+ std::find_if(mFrameRatesThatFavorsAtLeast60.begin(),
+ mFrameRatesThatFavorsAtLeast60.end(), [&](Fps fps) {
+ using fps_approx_ops::operator==;
+ return fps == layer.desiredRefreshRate;
+ }) != mFrameRatesThatFavorsAtLeast60.end();
+ if (favorsAtLeast60) {
+ return 0;
+ }
+ }
+
const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue();
// We only want to score this layer as a fractional pair if the content is not
@@ -487,10 +514,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi
// If the primary range consists of a single refresh rate then we can only
// move out the of range if layers explicitly request a different refresh
// rate.
- const bool primaryRangeIsSingleRate =
- isApproxEqual(policy->primaryRanges.physical.min, policy->primaryRanges.physical.max);
-
- if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+ if (!signals.touch && signals.idle &&
+ !(policy->primaryRangeIsSingleRate() && hasExplicitVoteLayers)) {
ALOGV("Idle");
const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending);
ATRACE_FORMAT_INSTANT("%s (Idle)", to_string(ranking.front().frameRateMode.fps).c_str());
@@ -564,8 +589,11 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi
continue;
}
- const bool inPrimaryRange = policy->primaryRanges.render.includes(fps);
- if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
+ const bool inPrimaryPhysicalRange =
+ policy->primaryRanges.physical.includes(modePtr->getFps());
+ const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps);
+ if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
+ !inPrimaryRenderRange) &&
!(layer.focused &&
(layer.vote == LayerVoteType::ExplicitDefault ||
layer.vote == LayerVoteType::ExplicitExact))) {
@@ -676,7 +704,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi
return score.overallScore == 0;
});
- if (primaryRangeIsSingleRate) {
+ if (policy->primaryRangeIsSingleRate()) {
// If we never scored any layers, then choose the rate from the primary
// range instead of picking a random score from the app range.
if (noLayerScore) {
@@ -1223,14 +1251,14 @@ void RefreshRateSelector::constructAvailableRefreshRates() {
(supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
};
- auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ auto frameRateModes = createFrameRateModes(*policy, filterModes, ranges.render);
if (frameRateModes.empty()) {
ALOGW("No matching frame rate modes for %s range. policy: %s", rangeName,
policy->toString().c_str());
// TODO(b/292105422): Ideally DisplayManager should not send render ranges smaller than
// the min supported. See b/292047939.
// For not we just ignore the render ranges.
- frameRateModes = createFrameRateModes(filterModes, {});
+ frameRateModes = createFrameRateModes(*policy, filterModes, {});
}
LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
"No matching frame rate modes for %s range even after ignoring the "
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 5052e6e257..b25919e7a0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -18,6 +18,7 @@
#include <algorithm>
#include <numeric>
+#include <set>
#include <type_traits>
#include <utility>
#include <variant>
@@ -100,6 +101,11 @@ public:
}
bool operator!=(const Policy& other) const { return !(*this == other); }
+
+ bool primaryRangeIsSingleRate() const {
+ return isApproxEqual(primaryRanges.physical.min, primaryRanges.physical.max);
+ }
+
std::string toString() const;
};
@@ -467,8 +473,8 @@ private:
}
std::vector<FrameRateMode> createFrameRateModes(
- std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const
- REQUIRES(mLock);
+ const Policy&, std::function<bool(const DisplayMode&)>&& filterModes,
+ const FpsRange&) const REQUIRES(mLock);
// The display modes of the active display. The DisplayModeIterators below are pointers into
// this container, so must be invalidated whenever the DisplayModes change. The Policy below
@@ -500,6 +506,12 @@ private:
const std::vector<Fps> mKnownFrameRates;
const Config mConfig;
+
+ // A list of known frame rates that favors at least 60Hz if there is no exact match display
+ // refresh rate
+ const std::vector<Fps> mFrameRatesThatFavorsAtLeast60 = {23.976_Hz, 25_Hz, 29.97_Hz, 50_Hz,
+ 59.94_Hz};
+
Config::FrameRateOverride mFrameRateOverrideConfig;
struct GetRankedFrameRatesCache {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index d6d7725f6c..5a19ec5095 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -186,7 +186,17 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
FrameTargeter& pacesetterTargeter = *pacesetterOpt->get().targeterPtr;
pacesetterTargeter.beginFrame(beginFrameArgs, *pacesetterOpt->get().schedulePtr);
- if (!compositor.commit(pacesetterTargeter.target())) return;
+ FrameTargets targets;
+ targets.try_emplace(pacesetterId, &pacesetterTargeter.target());
+
+ for (const auto& [id, display] : mDisplays) {
+ if (id == pacesetterId) continue;
+
+ const FrameTargeter& targeter = *display.targeterPtr;
+ targets.try_emplace(id, &targeter.target());
+ }
+
+ if (!compositor.commit(pacesetterId, targets)) return;
// TODO(b/256196556): Choose the frontrunner display.
FrameTargeters targeters;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
index 6fe813a181..12ee36e084 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -29,6 +29,7 @@ namespace scheduler {
class FrameTarget;
class FrameTargeter;
+using FrameTargets = ui::PhysicalDisplayMap<PhysicalDisplayId, const scheduler::FrameTarget*>;
using FrameTargeters = ui::PhysicalDisplayMap<PhysicalDisplayId, scheduler::FrameTargeter*>;
} // namespace scheduler
@@ -39,7 +40,7 @@ struct ICompositor {
// Commits transactions for layers and displays. Returns whether any state has been invalidated,
// i.e. whether a frame should be composited for each display.
- virtual bool commit(const scheduler::FrameTarget&) = 0;
+ virtual bool commit(PhysicalDisplayId pacesetterId, const scheduler::FrameTargets&) = 0;
// Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
// via RenderEngine and the Composer HAL, respectively.
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 01038432de..ef9b457fc9 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -45,10 +45,9 @@ Rect getOrientedDisplaySpaceRect(ui::Rotation orientation, int reqWidth, int req
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
- const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine,
- args.renderArea,
- args.colorProfile,
- args.regionSampling);
+ const compositionengine::Output::ColorProfile&,
+ bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling,
+ args.dimInGammaSpaceForEnhancedScreenshots);
output->editState().isSecure = args.renderArea.isSecure();
output->setCompositionEnabled(true);
output->setLayerFilter({args.layerStack});
@@ -81,8 +80,11 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp
ScreenCaptureOutput::ScreenCaptureOutput(
const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling)
- : mRenderArea(renderArea), mColorProfile(colorProfile), mRegionSampling(regionSampling) {}
+ bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots)
+ : mRenderArea(renderArea),
+ mColorProfile(colorProfile),
+ mRegionSampling(regionSampling),
+ mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots) {}
void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) {
auto& outputState = editState();
@@ -95,6 +97,14 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp
auto clientCompositionDisplay =
compositionengine::impl::Output::generateClientCompositionDisplaySettings();
clientCompositionDisplay.clip = mRenderArea.getSourceCrop();
+
+ auto renderIntent = static_cast<ui::RenderIntent>(clientCompositionDisplay.renderIntent);
+ if (mDimInGammaSpaceForEnhancedScreenshots && renderIntent != ui::RenderIntent::COLORIMETRIC &&
+ renderIntent != ui::RenderIntent::TONE_MAP_COLORIMETRIC) {
+ clientCompositionDisplay.dimmingStage =
+ aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF;
+ }
+
return clientCompositionDisplay;
}
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index 159c2bf903..fc095def99 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -37,6 +37,7 @@ struct ScreenCaptureOutputArgs {
float targetBrightness;
bool regionSampling;
bool treat170mAsSrgb;
+ bool dimInGammaSpaceForEnhancedScreenshots;
};
// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
@@ -47,7 +48,7 @@ class ScreenCaptureOutput : public compositionengine::impl::Output {
public:
ScreenCaptureOutput(const RenderArea& renderArea,
const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling);
+ bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots);
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
@@ -63,6 +64,7 @@ private:
const RenderArea& mRenderArea;
const compositionengine::Output::ColorProfile& mColorProfile;
const bool mRegionSampling;
+ const bool mDimInGammaSpaceForEnhancedScreenshots;
};
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fda8d689a5..1ef381c417 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -68,6 +68,7 @@
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
#include <gui/TraceUtils.h>
#include <hidl/ServiceManagement.h>
#include <layerproto/LayerProtoParser.h>
@@ -80,7 +81,6 @@
#include <scheduler/FrameTargeter.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
-#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
@@ -88,16 +88,16 @@
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/GraphicBufferAllocator.h>
+#include <ui/HdrRenderTypeUtils.h>
#include <ui/LayerStack.h>
#include <ui/PixelFormat.h>
#include <ui/StaticDisplayInfo.h>
+#include <unistd.h>
#include <utils/StopWatch.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/misc.h>
-
-#include <unistd.h>
#include <algorithm>
#include <cerrno>
#include <cinttypes>
@@ -406,9 +406,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI
wideColorGamutCompositionPixelFormat =
static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
- mColorSpaceAgnosticDataspace =
- static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
-
mLayerCachingEnabled = [] {
const bool enable =
android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
@@ -455,6 +452,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI
property_get("debug.sf.treat_170m_as_sRGB", value, "0");
mTreat170mAsSrgb = atoi(value);
+ property_get("debug.sf.dim_in_gamma_in_enhanced_screenshots", value, 0);
+ mDimInGammaSpaceForEnhancedScreenshots = atoi(value);
+
mIgnoreHwcPhysicalDisplayOrientation =
base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false);
@@ -647,14 +647,6 @@ sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId)
return getPhysicalDisplayTokenLocked(displayId);
}
-status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const {
- if (!outGetColorManagement) {
- return BAD_VALUE;
- }
- *outGetColorManagement = useColorManagement;
- return NO_ERROR;
-}
-
HWComposer& SurfaceFlinger::getHwComposer() const {
return mCompositionEngine->getHwComposer();
}
@@ -741,52 +733,12 @@ void SurfaceFlinger::bootFinished() {
}));
}
-uint32_t SurfaceFlinger::getNewTexture() {
- {
- std::lock_guard lock(mTexturePoolMutex);
- if (!mTexturePool.empty()) {
- uint32_t name = mTexturePool.back();
- mTexturePool.pop_back();
- ATRACE_INT("TexturePoolSize", mTexturePool.size());
- return name;
- }
-
- // The pool was too small, so increase it for the future
- ++mTexturePoolSize;
- }
-
- // The pool was empty, so we need to get a new texture name directly using a
- // blocking call to the main thread
- auto genTextures = [this] {
- uint32_t name = 0;
- getRenderEngine().genTextures(1, &name);
- return name;
- };
- if (std::this_thread::get_id() == mMainThreadId) {
- return genTextures();
- } else {
- return mScheduler->schedule(genTextures).get();
- }
-}
-
-void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
- std::lock_guard lock(mTexturePoolMutex);
- // We don't change the pool size, so the fix-up logic in postComposition will decide whether
- // to actually delete this or not based on mTexturePoolSize
- mTexturePool.push_back(texture);
- ATRACE_INT("TexturePoolSize", mTexturePool.size());
-}
-
static std::optional<renderengine::RenderEngine::RenderEngineType>
chooseRenderEngineTypeViaSysProp() {
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "skiaglthreaded");
- if (strcmp(prop, "gles") == 0) {
- return renderengine::RenderEngine::RenderEngineType::GLES;
- } else if (strcmp(prop, "threaded") == 0) {
- return renderengine::RenderEngine::RenderEngineType::THREADED;
- } else if (strcmp(prop, "skiagl") == 0) {
+ if (strcmp(prop, "skiagl") == 0) {
return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
} else if (strcmp(prop, "skiaglthreaded") == 0) {
return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
@@ -815,7 +767,6 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
auto builder = renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
.setImageCacheSize(maxFrameBufferAcquiredBuffers)
- .setUseColorManagerment(useColorManagement)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(mSupportsBlur)
@@ -918,7 +869,7 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
auto writeFn = [&]() {
const std::string filename =
TransactionTracing::DIR_NAME + prefix + TransactionTracing::FILE_NAME;
- if (overwrite && access(filename.c_str(), F_OK) == 0) {
+ if (!overwrite && access(filename.c_str(), F_OK) == 0) {
ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str());
return;
}
@@ -1198,9 +1149,9 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken,
}
void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
- ATRACE_CALL();
-
const auto displayId = request.mode.modePtr->getPhysicalDisplayId();
+ ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
+
const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
ALOGW("%s: display is no longer valid", __func__);
@@ -1228,17 +1179,24 @@ void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request,
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated);
- updatePhaseConfiguration(mode.fps);
+
+ if (displayId == mActiveDisplayId) {
+ updatePhaseConfiguration(mode.fps);
+ }
+
mScheduler->setModeChangePending(true);
break;
case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
mScheduler->setRenderRate(displayId, mode.fps);
- updatePhaseConfiguration(mode.fps);
- mRefreshRateStats->setRefreshRate(mode.fps);
- if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode);
+
+ if (displayId == mActiveDisplayId) {
+ updatePhaseConfiguration(mode.fps);
+ mRefreshRateStats->setRefreshRate(mode.fps);
}
+ if (emitEvent) {
+ dispatchDisplayModeChangeEvent(displayId, mode);
+ }
break;
case DisplayDevice::DesiredActiveModeAction::None:
break;
@@ -1294,24 +1252,20 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke
return future.get();
}
-void SurfaceFlinger::updateInternalStateWithChangedMode() {
- ATRACE_CALL();
+void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) {
+ const auto displayId = display.getPhysicalId();
+ ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display) {
- return;
- }
-
- const auto upcomingModeInfo = display->getUpcomingActiveMode();
+ const auto upcomingModeInfo = display.getUpcomingActiveMode();
if (!upcomingModeInfo.modeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
return;
}
- if (display->getActiveMode().modePtr->getResolution() !=
+ if (display.getActiveMode().modePtr->getResolution() !=
upcomingModeInfo.modeOpt->modePtr->getResolution()) {
- auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
+ auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
state.sequenceId = DisplayDeviceState{}.sequenceId;
@@ -1322,27 +1276,24 @@ void SurfaceFlinger::updateInternalStateWithChangedMode() {
return;
}
- mPhysicalDisplays.get(display->getPhysicalId())
- .transform(&PhysicalDisplay::snapshotRef)
- .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
- FTL_FAKE_GUARD(kMainThreadContext,
- display->setActiveMode(upcomingModeInfo.modeOpt->modePtr->getId(),
- upcomingModeInfo.modeOpt->modePtr->getFps(),
- upcomingModeInfo.modeOpt->fps));
- }));
-
- const Fps refreshRate = upcomingModeInfo.modeOpt->fps;
- mRefreshRateStats->setRefreshRate(refreshRate);
- updatePhaseConfiguration(refreshRate);
+ const auto& activeMode = *upcomingModeInfo.modeOpt;
+ display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getFps(),
+ activeMode.fps);
+
+ if (displayId == mActiveDisplayId) {
+ mRefreshRateStats->setRefreshRate(activeMode.fps);
+ updatePhaseConfiguration(activeMode.fps);
+ }
if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) {
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, *upcomingModeInfo.modeOpt);
+ dispatchDisplayModeChangeEvent(displayId, activeMode);
}
}
void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) {
display->clearDesiredActiveModeState();
if (display->getPhysicalId() == mActiveDisplayId) {
+ // TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
}
}
@@ -1356,21 +1307,18 @@ void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& displa
clearDesiredActiveModeState(display);
mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, displayFps);
mScheduler->setRenderRate(displayId, renderFps);
- updatePhaseConfiguration(renderFps);
+
+ if (displayId == mActiveDisplayId) {
+ updatePhaseConfiguration(renderFps);
+ }
}
-void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
+void SurfaceFlinger::initiateDisplayModeChanges() {
ATRACE_CALL();
std::optional<PhysicalDisplayId> displayToUpdateImmediately;
for (const auto& [id, physical] : mPhysicalDisplays) {
- const auto& snapshot = physical.snapshot();
-
- if (snapshot.connectionType() != ui::DisplayConnectionType::Internal) {
- continue;
- }
-
const auto display = getDisplayDeviceLocked(id);
if (!display) continue;
@@ -1381,14 +1329,14 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
continue;
}
- if (id != mActiveDisplayId) {
- // Display is no longer the active display, so abort the mode change.
+ if (!display->isPoweredOn()) {
+ // Display is no longer powered on, so abort the mode change.
clearDesiredActiveModeState(display);
continue;
}
const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId();
- const auto displayModePtrOpt = snapshot.displayModes().get(desiredModeId);
+ const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId);
if (!displayModePtrOpt) {
ALOGW("Desired display mode is no longer supported. Mode ID = %d",
@@ -1438,19 +1386,18 @@ void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
if (outTimeline.refreshRequired) {
scheduleComposite(FrameHint::kNone);
- mSetActiveModePending = true;
} else {
- // Updating the internal state should be done outside the loop,
- // because it can recreate a DisplayDevice and modify mDisplays
- // which will invalidate the iterator.
+ // TODO(b/255635711): Remove `displayToUpdateImmediately` to `finalizeDisplayModeChange`
+ // for all displays. This was only needed when the loop iterated over `mDisplays` rather
+ // than `mPhysicalDisplays`.
displayToUpdateImmediately = display->getPhysicalId();
}
}
if (displayToUpdateImmediately) {
- updateInternalStateWithChangedMode();
-
const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
+ finalizeDisplayModeChange(*display);
+
const auto desiredActiveMode = display->getDesiredActiveMode();
if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) {
desiredActiveModeChangeDone(display);
@@ -1526,7 +1473,7 @@ status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui:
}
display->getCompositionDisplay()->setColorProfile(
- {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN});
+ {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC});
return NO_ERROR;
});
@@ -2318,7 +2265,9 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
.forceFullDamage = mForceFullDamage,
.supportedLayerGenericMetadata =
getHwComposer().getSupportedLayerGenericMetadata(),
- .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+ .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(),
+ .skipRoundCornersWhenProtected =
+ !getRenderEngine().supportsProtectedContent()};
mLayerSnapshotBuilder.update(args);
}
@@ -2327,7 +2276,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
mUpdateInputInfo = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
- Changes::Visibility)) {
+ Changes::Visibility | Changes::Geometry)) {
mVisibleRegionsDirty = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) {
@@ -2342,6 +2291,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
if (!mLegacyFrontEndEnabled) {
ATRACE_NAME("DisplayCallbackAndStatsUpdates");
applyTransactions(update.transactions, vsyncId);
+ traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); });
const nsecs_t latchTime = systemTime();
bool unused = false;
@@ -2366,19 +2316,21 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
mLayersWithBuffersRemoved.emplace(it->second);
}
it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
+ newDataLatched = true;
+
mLayersWithQueuedFrames.emplace(it->second);
+ mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
- for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
- updateLayerHistory(*snapshot);
- if (!snapshot->hasReadyFrame) continue;
- newDataLatched = true;
- if (!snapshot->isVisible) break;
-
+ mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ updateLayerHistory(snapshot);
+ if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) ==
+ mLayersIdsWithQueuedFrames.end())
+ return;
Region visibleReg;
- visibleReg.set(snapshot->transformedBoundsWithoutTransparentRegion);
- invalidateLayerStack(snapshot->outputFilter, visibleReg);
- }
+ visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
+ invalidateLayerStack(snapshot.outputFilter, visibleReg);
+ });
for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) {
mLegacyLayers.erase(destroyedLayer->id);
@@ -2401,7 +2353,10 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
return mustComposite;
}
-bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget) {
+bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargets& frameTargets) {
+ const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get();
+
const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
@@ -2414,20 +2369,35 @@ bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget)
mTracingEnabledChanged = false;
}
- // If we are in the middle of a mode change and the fence hasn't
- // fired yet just wait for the next commit.
- if (mSetActiveModePending) {
- if (pacesetterFrameTarget.isFramePending()) {
- mScheduler->scheduleFrame();
- return false;
- }
+ // If a mode set is pending and the fence hasn't fired yet, wait for the next commit.
+ if (std::any_of(frameTargets.begin(), frameTargets.end(),
+ [this](const auto& pair) FTL_FAKE_GUARD(mStateLock)
+ FTL_FAKE_GUARD(kMainThreadContext) {
+ if (!pair.second->isFramePending()) return false;
- // We received the present fence from the HWC, so we assume it successfully updated
- // the mode, hence we update SF.
- mSetActiveModePending = false;
- {
- Mutex::Autolock lock(mStateLock);
- updateInternalStateWithChangedMode();
+ if (const auto display = getDisplayDeviceLocked(pair.first)) {
+ return display->isModeSetPending();
+ }
+
+ return false;
+ })) {
+ mScheduler->scheduleFrame();
+ return false;
+ }
+
+ {
+ Mutex::Autolock lock(mStateLock);
+
+ for (const auto [id, target] : frameTargets) {
+ // TODO(b/241285876): This is `nullptr` when the DisplayDevice is about to be removed in
+ // this commit, since the PhysicalDisplay has already been removed. Rather than checking
+ // for `nullptr` below, change Scheduler::onFrameSignal to filter out the FrameTarget of
+ // the removed display.
+ const auto display = getDisplayDeviceLocked(id);
+
+ if (display && display->isModeSetPending()) {
+ finalizeDisplayModeChange(*display);
+ }
}
}
@@ -2518,7 +2488,7 @@ bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget)
? &mLayerHierarchyBuilder.getHierarchy()
: nullptr,
updateAttachedChoreographer);
- setActiveModeInHwcIfNeeded();
+ initiateDisplayModeChanges();
}
updateCursorAsync();
@@ -2606,10 +2576,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite(
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
- refreshArgs.outputColorSetting = useColorManagement
- ? mDisplayColorSetting
- : compositionengine::OutputColorSetting::kUnmanaged;
- refreshArgs.colorSpaceAgnosticDataspace = mColorSpaceAgnosticDataspace;
+ refreshArgs.outputColorSetting = mDisplayColorSetting;
refreshArgs.forceOutputColorMode = mForceColorMode;
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
@@ -2731,6 +2698,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite(
mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
mLayersWithQueuedFrames.clear();
+ mLayersIdsWithQueuedFrames.clear();
if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and should only be used for debugging.
addToLayerTracing(mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(), vsyncId);
@@ -2786,7 +2754,14 @@ bool SurfaceFlinger::isHdrLayer(const frontend::LayerSnapshot& snapshot) const {
return false;
}
}
- if (isHdrDataspace(snapshot.dataspace)) {
+ // RANGE_EXTENDED layer may identify themselves as being "HDR"
+ // via a desired hdr/sdr ratio
+ auto pixelFormat = snapshot.buffer
+ ? std::make_optional(static_cast<ui::PixelFormat>(snapshot.buffer->getPixelFormat()))
+ : std::nullopt;
+
+ if (getHdrRenderType(snapshot.dataspace, pixelFormat, snapshot.desiredHdrSdrRatio) !=
+ HdrRenderType::SDR) {
return true;
}
// If the layer is not allowed to be dimmed, treat it as HDR. WindowManager may disable
@@ -2796,12 +2771,6 @@ bool SurfaceFlinger::isHdrLayer(const frontend::LayerSnapshot& snapshot) const {
if (!snapshot.dimmingEnabled) {
return true;
}
- // RANGE_EXTENDED layers may identify themselves as being "HDR" via a desired sdr/hdr ratio
- if ((snapshot.dataspace & (int32_t)Dataspace::RANGE_MASK) ==
- (int32_t)Dataspace::RANGE_EXTENDED &&
- snapshot.desiredHdrSdrRatio > 1.01f) {
- return true;
- }
return false;
}
@@ -3059,23 +3028,6 @@ void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId,
// Cleanup any outstanding resources due to rendering a prior frame.
getRenderEngine().cleanupPostRender();
- {
- std::lock_guard lock(mTexturePoolMutex);
- if (mTexturePool.size() < mTexturePoolSize) {
- const size_t refillCount = mTexturePoolSize - mTexturePool.size();
- const size_t offset = mTexturePool.size();
- mTexturePool.resize(mTexturePoolSize);
- getRenderEngine().genTextures(refillCount, mTexturePool.data() + offset);
- ATRACE_INT("TexturePoolSize", mTexturePool.size());
- } else if (mTexturePool.size() > mTexturePoolSize) {
- const size_t deleteCount = mTexturePool.size() - mTexturePoolSize;
- const size_t offset = mTexturePoolSize;
- getRenderEngine().deleteTextures(deleteCount, mTexturePool.data() + offset);
- mTexturePool.resize(mTexturePoolSize);
- ATRACE_INT("TexturePoolSize", mTexturePool.size());
- }
- }
-
if (mNumTrustedPresentationListeners > 0) {
// We avoid any reverse traversal upwards so this shouldn't be too expensive
traverseLegacyLayers([&](Layer* layer) {
@@ -3326,6 +3278,16 @@ void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bo
mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
}
+void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId,
+ const scheduler::FrameRateMode& mode) {
+ // TODO(b/255635821): Merge code paths and move to Scheduler.
+ const auto onDisplayModeChanged = displayId == mActiveDisplayId
+ ? &scheduler::Scheduler::onPrimaryDisplayModeChanged
+ : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged;
+
+ ((*mScheduler).*onDisplayModeChanged)(mAppConnectionHandle, mode);
+}
+
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -3369,18 +3331,16 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked();
- if (useColorManagement) {
- mPhysicalDisplays.get(physical->id)
- .transform(&PhysicalDisplay::snapshotRef)
- .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
- for (const auto mode : snapshot.colorModes()) {
- creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
- creationArgs.hwcColorModes
- .emplace(mode,
- getHwComposer().getRenderIntents(physical->id, mode));
- }
- }));
- }
+ mPhysicalDisplays.get(physical->id)
+ .transform(&PhysicalDisplay::snapshotRef)
+ .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+ for (const auto mode : snapshot.colorModes()) {
+ creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
+ creationArgs.hwcColorModes
+ .emplace(mode,
+ getHwComposer().getRenderIntents(physical->id, mode));
+ }
+ }));
}
if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
@@ -3421,18 +3381,11 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
}
display->getCompositionDisplay()->setColorProfile(
compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace,
- RenderIntent::COLORIMETRIC,
- Dataspace::UNKNOWN});
+ RenderIntent::COLORIMETRIC});
if (const auto& physical = state.physical) {
- mPhysicalDisplays.get(physical->id)
- .transform(&PhysicalDisplay::snapshotRef)
- .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
- FTL_FAKE_GUARD(kMainThreadContext,
- display->setActiveMode(physical->activeMode->getId(),
- physical->activeMode->getFps(),
- physical->activeMode->getFps()));
- }));
+ const auto& mode = *physical->activeMode;
+ display->setActiveMode(mode.getId(), mode.getFps(), mode.getFps());
}
display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
@@ -3572,8 +3525,6 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
// Recreate the DisplayDevice if the surface or sequence ID changed.
if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
- getRenderEngine().cleanFramebufferCache();
-
if (const auto display = getDisplayDeviceLocked(displayToken)) {
display->disconnect();
if (display->isVirtual()) {
@@ -3951,12 +3902,8 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest
if (!display) continue;
- const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
- .transform(&PhysicalDisplay::isInternal)
- .value_or(false);
-
- if (isInternalDisplay && displayId != mActiveDisplayId) {
- ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ if (!display->isPoweredOn()) {
+ ALOGV("%s(%s): Display is powered off", __func__, to_string(displayId).c_str());
continue;
}
@@ -3964,7 +3911,7 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest
setDesiredActiveMode(std::move(request));
} else {
ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
- to_string(display->getId()).c_str());
+ to_string(displayId).c_str());
}
}
}
@@ -4448,9 +4395,13 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC
(flushState.queueProcessTime - transaction.postTime) >
std::chrono::nanoseconds(4s).count()) {
mTransactionHandler
- .onTransactionQueueStalled(transaction.id, listener,
- "Buffer processing hung up due to stuck "
- "fence. Indicates GPU hang");
+ .onTransactionQueueStalled(transaction.id,
+ {.pid = layer->getOwnerPid(),
+ .layerId = static_cast<uint32_t>(
+ layer->getSequence()),
+ .layerName = layer->getDebugName(),
+ .bufferId = s.bufferData->getId(),
+ .frameNumber = s.bufferData->frameNumber});
}
ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName());
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
@@ -4543,9 +4494,12 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC
(flushState.queueProcessTime - transaction.postTime) >
std::chrono::nanoseconds(4s).count()) {
mTransactionHandler
- .onTransactionQueueStalled(transaction.id, listener,
- "Buffer processing hung up due to stuck "
- "fence. Indicates GPU hang");
+ .onTransactionQueueStalled(transaction.id,
+ {.pid = layer->ownerPid.val(),
+ .layerId = layer->id,
+ .layerName = layer->name,
+ .bufferId = s.bufferData->getId(),
+ .frameNumber = s.bufferData->frameNumber});
}
ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
@@ -5373,6 +5327,9 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f
if (what & layer_state_t::eSidebandStreamChanged) {
if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eDataspaceChanged) {
+ if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eBufferChanged) {
std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt;
frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
@@ -5547,7 +5504,6 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurface
status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle,
sp<Layer>* outLayer) {
- args.textureName = getNewTexture();
*outLayer = getFactory().createBufferStateLayer(args);
*handle = (*outLayer)->getHandle();
return NO_ERROR;
@@ -5572,6 +5528,8 @@ void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32
mDestroyedHandles.emplace_back(layerId);
}
+ mTransactionHandler.onLayerDestroyed(layerId);
+
Mutex::Autolock lock(mStateLock);
markLayerPendingRemovalLocked(layer);
layer->onHandleDestroyed();
@@ -5699,18 +5657,22 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
// Turn off the display
if (displayId == mActiveDisplayId) {
- if (setSchedFifo(false) != NO_ERROR) {
- ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
- strerror(errno));
- }
- if (setSchedAttr(false) != NO_ERROR) {
- ALOGW("Failed set uclamp.min after powering off active display: %s",
- strerror(errno));
- }
+ if (const auto display = getActivatableDisplay()) {
+ onActiveDisplayChangedLocked(activeDisplay.get(), *display);
+ } else {
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
+ strerror(errno));
+ }
+ if (setSchedAttr(false) != NO_ERROR) {
+ ALOGW("Failed set uclamp.min after powering off active display: %s",
+ strerror(errno));
+ }
- if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
- mScheduler->disableHardwareVsync(displayId, true);
- mScheduler->enableSyntheticVsync();
+ if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
+ mScheduler->disableHardwareVsync(displayId, true);
+ mScheduler->enableSyntheticVsync();
+ }
}
}
@@ -5788,6 +5750,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
{"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
{"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
{"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
+ {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
{"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy)},
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
@@ -5913,7 +5876,7 @@ void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result)
const auto name = String8(args[1]);
mCurrentState.traverseInZOrder([&](Layer* layer) {
- if (layer->getName() == name.string()) {
+ if (layer->getName() == name.c_str()) {
layer->dumpFrameStats(result);
}
});
@@ -5924,7 +5887,7 @@ void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
const auto name = clearAll ? String8() : String8(args[1]);
mCurrentState.traverse([&](Layer* layer) {
- if (clearAll || layer->getName() == name.string()) {
+ if (clearAll || layer->getName() == name.c_str()) {
layer->clearFrameStats();
}
});
@@ -6076,7 +6039,6 @@ void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args,
void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
StringAppendF(&result, "Device supports wide color: %d\n", mSupportsWideColor);
- StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
StringAppendF(&result, "DisplayColorSetting: %s\n",
decodeDisplayColorSetting(mDisplayColorSetting).c_str());
@@ -6097,6 +6059,14 @@ void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
result.append("\n");
}
+void SurfaceFlinger::dumpHdrInfo(std::string& result) const {
+ for (const auto& [displayId, listener] : mHdrLayerInfoListeners) {
+ StringAppendF(&result, "HDR events for display %" PRIu64 "\n", displayId.value);
+ listener->dump(result);
+ result.append("\n");
+ }
+}
+
LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
std::unordered_set<uint64_t> stackIdsToSkip;
@@ -6132,6 +6102,13 @@ google::protobuf::RepeatedPtrField<DisplayProto> SurfaceFlinger::dumpDisplayProt
displayProto->set_id(display->getId().value);
displayProto->set_name(display->getDisplayName());
displayProto->set_layer_stack(display->getLayerStack().id);
+
+ if (!display->isVirtual()) {
+ const auto dpi = display->refreshRateSelector().getActiveMode().modePtr->getDpi();
+ displayProto->set_dpi_x(dpi.x);
+ displayProto->set_dpi_y(dpi.y);
+ }
+
LayerProtoHelper::writeSizeToProto(display->getWidth(), display->getHeight(),
[&]() { return displayProto->mutable_size(); });
LayerProtoHelper::writeToProto(display->getLayerStackSpaceRect(), [&]() {
@@ -6254,6 +6231,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp
result.append("\nWide-Color information:\n");
dumpWideColorInfo(result);
+ dumpHdrInfo(result);
+
colorizer.bold(result);
result.append("Sync configuration: ");
colorizer.reset(result);
@@ -6527,9 +6506,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1043 are currently used for backdoors. The code
+ // Numbers from 1000 to 1044 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1043) {
+ if (code >= 1000 && code <= 1044) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -6744,8 +6723,6 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32());
switch (setting) {
case DisplayColorSetting::kManaged:
- reply->writeBool(useColorManagement);
- break;
case DisplayColorSetting::kUnmanaged:
reply->writeBool(true);
break;
@@ -6778,7 +6755,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
}
// Is device color managed?
case 1030: {
- reply->writeBool(useColorManagement);
+ // ColorDisplayManager stil calls this
+ reply->writeBool(true);
return NO_ERROR;
}
// Override default composition data space
@@ -7008,6 +6986,73 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
future.wait();
return NO_ERROR;
}
+
+ case 1044: { // Enable/Disable mirroring from one display to another
+ /*
+ * Mirror one display onto another.
+ * Ensure the source and destination displays are on.
+ * Commands:
+ * 0: Mirror one display to another
+ * 1: Disable mirroring to a previously mirrored display
+ * 2: Disable mirroring on previously mirrored displays
+ *
+ * Ex:
+ * Get the display ids:
+ * adb shell dumpsys SurfaceFlinger --display-id
+ * Mirror first display to the second:
+ * adb shell service call SurfaceFlinger 1044 i64 0 i64 4619827677550801152 i64
+ * 4619827677550801153
+ * Stop mirroring:
+ * adb shell service call SurfaceFlinger 1044 i64 1
+ */
+
+ int64_t arg0 = data.readInt64();
+
+ switch (arg0) {
+ case 0: {
+ // Mirror arg1 to arg2
+ int64_t arg1 = data.readInt64();
+ int64_t arg2 = data.readInt64();
+ // Enable mirroring for one display
+ const auto display1id = DisplayId::fromValue(arg1);
+ auto mirrorRoot = SurfaceComposerClient::getDefault()->mirrorDisplay(
+ display1id.value());
+ auto id2 = DisplayId::fromValue<PhysicalDisplayId>(arg2);
+ const auto token2 = getPhysicalDisplayToken(*id2);
+ ui::LayerStack layerStack;
+ {
+ Mutex::Autolock lock(mStateLock);
+ sp<DisplayDevice> display = getDisplayDeviceLocked(token2);
+ layerStack = display->getLayerStack();
+ }
+ SurfaceComposerClient::Transaction t;
+ t.setDisplayLayerStack(token2, layerStack);
+ t.setLayer(mirrorRoot, INT_MAX); // Top-most layer
+ t.setLayerStack(mirrorRoot, layerStack);
+ t.apply();
+
+ mMirrorMapForDebug.emplace_or_replace(arg2, mirrorRoot);
+ break;
+ }
+
+ case 1: {
+ // Disable mirroring for arg1
+ int64_t arg1 = data.readInt64();
+ mMirrorMapForDebug.erase(arg1);
+ break;
+ }
+
+ case 2: {
+ // Disable mirroring for all displays
+ mMirrorMapForDebug.clear();
+ break;
+ }
+
+ default:
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+ }
}
}
return err;
@@ -7249,16 +7294,27 @@ ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, const DisplayD
} // namespace
-status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) {
+static void invokeScreenCaptureError(const status_t status,
+ const sp<IScreenCaptureListener>& captureListener) {
+ ScreenCaptureResults captureResults;
+ captureResults.fenceResult = base::unexpected(status);
+ captureListener->onScreenCaptureCompleted(captureResults);
+}
+
+void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
status_t validate = validateScreenshotPermissions(args);
if (validate != OK) {
- return validate;
+ invokeScreenCaptureError(validate, captureListener);
+ return;
}
- if (!args.displayToken) return BAD_VALUE;
+ if (!args.displayToken) {
+ invokeScreenCaptureError(BAD_VALUE, captureListener);
+ return;
+ }
wp<const DisplayDevice> displayWeak;
ui::LayerStack layerStack;
@@ -7267,7 +7323,10 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
{
Mutex::Autolock lock(mStateLock);
sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
- if (!display) return NAME_NOT_FOUND;
+ if (!display) {
+ invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
+ return;
+ }
displayWeak = display;
layerStack = display->getLayerStack();
@@ -7282,7 +7341,8 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
excludeLayerIds.emplace(excludeLayer);
} else {
ALOGW("Invalid layer handle passed as excludeLayer to captureDisplay");
- return NAME_NOT_FOUND;
+ invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
+ return;
}
}
}
@@ -7305,14 +7365,12 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
}
- auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
- args.pixelFormat, args.allowProtected, args.grayscale,
- captureListener);
- return fenceStatus(future.get());
+ captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat,
+ args.allowProtected, args.grayscale, captureListener);
}
-status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
- const sp<IScreenCaptureListener>& captureListener) {
+void SurfaceFlinger::captureDisplay(DisplayId displayId,
+ const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
wp<const DisplayDevice> displayWeak;
ui::Size size;
@@ -7321,7 +7379,8 @@ status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
- return NAME_NOT_FOUND;
+ invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
+ return;
}
displayWeak = display;
@@ -7349,25 +7408,25 @@ status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
if (captureListener == nullptr) {
ALOGE("capture screen must provide a capture listener callback");
- return BAD_VALUE;
+ invokeScreenCaptureError(BAD_VALUE, captureListener);
+ return;
}
constexpr bool kAllowProtected = false;
constexpr bool kGrayscale = false;
- auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size,
- ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale,
- captureListener);
- return fenceStatus(future.get());
+ captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size,
+ ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale, captureListener);
}
-status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) {
+void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
status_t validate = validateScreenshotPermissions(args);
if (validate != OK) {
- return validate;
+ invokeScreenCaptureError(validate, captureListener);
+ return;
}
ui::Size reqSize;
@@ -7385,13 +7444,15 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
parent = LayerHandle::getLayer(args.layerHandle);
if (parent == nullptr) {
ALOGE("captureLayers called with an invalid or removed parent");
- return NAME_NOT_FOUND;
+ invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
+ return;
}
if (!canCaptureBlackoutContent &&
parent->getDrawingState().flags & layer_state_t::eLayerSecure) {
ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
- return PERMISSION_DENIED;
+ invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
+ return;
}
Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getDrawingState());
@@ -7408,7 +7469,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) {
// Error out if the layer has no source bounds (i.e. they are boundless) and a source
// crop was not specified, or an invalid frame scale was provided.
- return BAD_VALUE;
+ invokeScreenCaptureError(BAD_VALUE, captureListener);
+ return;
}
reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
@@ -7418,7 +7480,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
excludeLayerIds.emplace(excludeLayer);
} else {
ALOGW("Invalid layer handle passed as excludeLayer to captureLayers");
- return NAME_NOT_FOUND;
+ invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
+ return;
}
}
} // mStateLock
@@ -7426,7 +7489,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
// really small crop or frameScale
if (reqSize.width <= 0 || reqSize.height <= 0) {
ALOGW("Failed to captureLayes: crop or scale too small");
- return BAD_VALUE;
+ invokeScreenCaptureError(BAD_VALUE, captureListener);
+ return;
}
bool childrenOnly = args.childrenOnly;
@@ -7490,26 +7554,27 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
if (captureListener == nullptr) {
ALOGE("capture screen must provide a capture listener callback");
- return BAD_VALUE;
+ invokeScreenCaptureError(BAD_VALUE, captureListener);
+ return;
}
- auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
- args.pixelFormat, args.allowProtected, args.grayscale,
- captureListener);
- return fenceStatus(future.get());
+ captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat,
+ args.allowProtected, args.grayscale, captureListener);
}
-ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
- RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
- ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale,
- const sp<IScreenCaptureListener>& captureListener) {
+void SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
+ GetLayerSnapshotsFunction getLayerSnapshots,
+ ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
+ bool allowProtected, bool grayscale,
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) {
ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32
") that exceeds render target size limit.",
bufferSize.getWidth(), bufferSize.getHeight());
- return ftl::yield<FenceResult>(base::unexpected(BAD_VALUE)).share();
+ invokeScreenCaptureError(BAD_VALUE, captureListener);
+ return;
}
// Loop over all visible layers to see whether there's any protected layer. A protected layer is
@@ -7549,14 +7614,16 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
// Otherwise an irreponsible process may cause an SF crash by allocating
// too much.
ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
- return ftl::yield<FenceResult>(base::unexpected(bufferStatus)).share();
+ invokeScreenCaptureError(bufferStatus, captureListener);
+ return;
}
const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
- return captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture,
- false /* regionSampling */, grayscale, captureListener);
+ auto fence = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture,
+ false /* regionSampling */, grayscale, captureListener);
+ fence.get();
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
@@ -7735,7 +7802,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
.displayBrightnessNits = displayBrightnessNits,
.targetBrightness = targetBrightness,
.regionSampling = regionSampling,
- .treat170mAsSrgb = mTreat170mAsSrgb});
+ .treat170mAsSrgb = mTreat170mAsSrgb,
+ .dimInGammaSpaceForEnhancedScreenshots =
+ mDimInGammaSpaceForEnhancedScreenshots});
const float colorSaturation = grayscale ? 0 : 1;
compositionengine::CompositionRefreshArgs refreshArgs{
@@ -7760,7 +7829,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
const bool renderEngineIsThreaded = [&]() {
using Type = renderengine::RenderEngine::RenderEngineType;
const auto type = mRenderEngine->getRenderEngineType();
- return type == Type::THREADED || type == Type::SKIA_GL_THREADED;
+ return type == Type::SKIA_GL_THREADED;
}();
auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share()
: ftl::yield(present()).share();
@@ -7860,6 +7929,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
const sp<DisplayDevice>& display,
const scheduler::RefreshRateSelector::PolicyVariant& policy) {
const auto displayId = display->getPhysicalId();
+ ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
Mutex::Autolock lock(mStateLock);
@@ -7880,13 +7950,11 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
break;
}
- const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
- .transform(&PhysicalDisplay::isInternal)
- .value_or(false);
-
- if (isInternalDisplay && displayId != mActiveDisplayId) {
- // The policy will be be applied when the display becomes active.
- ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ // TODO(b/255635711): Apply the policy once the display is powered on, which is currently only
+ // done for the internal display that becomes active on fold/unfold. For now, assume that DM
+ // always powers on the secondary (internal or external) display before setting its policy.
+ if (!display->isPoweredOn()) {
+ ALOGV("%s(%s): Display is powered off", __func__, to_string(displayId).c_str());
return NO_ERROR;
}
@@ -8099,19 +8167,22 @@ void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
for (const auto& [id, display] : mPhysicalDisplays) {
if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
- if (setByHwc) {
- const auto status =
- getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
- if (status != NO_ERROR) {
- ALOGE("Error updating the refresh rate changed callback debug enabled");
- return;
- }
- }
-
if (const auto device = getDisplayDeviceLocked(id)) {
- device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
- mRefreshRateOverlayRenderRate,
- mRefreshRateOverlayShowInMiddle);
+ const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD(
+ kMainThreadContext) {
+ device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
+ mRefreshRateOverlayRenderRate,
+ mRefreshRateOverlayShowInMiddle);
+ };
+ enableOverlay(setByHwc);
+ if (setByHwc) {
+ const auto status =
+ getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+ if (status != NO_ERROR) {
+ ALOGE("Error updating the refresh rate changed callback debug enabled");
+ enableOverlay(/*setByHwc*/ false);
+ }
+ }
}
}
}
@@ -8231,6 +8302,20 @@ void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDispl
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
}
+sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const {
+ if (mPhysicalDisplays.size() == 1) return nullptr;
+
+ // TODO(b/255635821): Choose the pacesetter display, considering both internal and external
+ // displays. For now, pick the other internal display, assuming a dual-display foldable.
+ return findDisplay([this](const DisplayDevice& display) REQUIRES(mStateLock) {
+ const auto idOpt = PhysicalDisplayId::tryCast(display.getId());
+ return idOpt && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
+ mPhysicalDisplays.get(*idOpt)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+ });
+}
+
void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
const DisplayDevice& activeDisplay) {
ATRACE_CALL();
@@ -8249,7 +8334,9 @@ void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveD
resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
+ // TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
+
mScheduler->setPacesetterDisplay(mActiveDisplayId);
onActiveDisplaySizeChanged(activeDisplay);
@@ -8277,6 +8364,12 @@ status_t SurfaceFlinger::removeWindowInfosListener(
return NO_ERROR;
}
+status_t SurfaceFlinger::getStalledTransactionInfo(
+ int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result) {
+ result = mTransactionHandler.getStalledTransactionInfo(pid);
+ return NO_ERROR;
+}
+
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
BufferData& bufferData, const char* layerName, uint64_t transactionId) {
if (bufferData.buffer &&
@@ -8552,7 +8645,9 @@ SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> la
.excludeLayerIds = std::move(excludeLayerIds),
.supportedLayerGenericMetadata =
getHwComposer().getSupportedLayerGenericMetadata(),
- .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+ .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(),
+ .skipRoundCornersWhenProtected =
+ !getRenderEngine().supportsProtectedContent()};
mLayerSnapshotBuilder.update(args);
auto getLayerSnapshotsFn =
@@ -8587,7 +8682,9 @@ SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t u
.excludeLayerIds = std::move(excludeLayerIds),
.supportedLayerGenericMetadata =
getHwComposer().getSupportedLayerGenericMetadata(),
- .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+ .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(),
+ .skipRoundCornersWhenProtected =
+ !getRenderEngine().supportsProtectedContent()};
mLayerSnapshotBuilder.update(args);
auto getLayerSnapshotsFn =
@@ -8780,33 +8877,35 @@ binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId,
outInfo->secure = info.secure;
outInfo->installOrientation = static_cast<gui::Rotation>(info.installOrientation);
- gui::DeviceProductInfo dinfo;
- std::optional<DeviceProductInfo> dpi = info.deviceProductInfo;
- dinfo.name = std::move(dpi->name);
- dinfo.manufacturerPnpId =
- std::vector<uint8_t>(dpi->manufacturerPnpId.begin(), dpi->manufacturerPnpId.end());
- dinfo.productId = dpi->productId;
- dinfo.relativeAddress =
- std::vector<uint8_t>(dpi->relativeAddress.begin(), dpi->relativeAddress.end());
- if (const auto* model =
- std::get_if<DeviceProductInfo::ModelYear>(&dpi->manufactureOrModelDate)) {
- gui::DeviceProductInfo::ModelYear modelYear;
- modelYear.year = model->year;
- dinfo.manufactureOrModelDate.set<Tag::modelYear>(modelYear);
- } else if (const auto* manufacture = std::get_if<DeviceProductInfo::ManufactureYear>(
- &dpi->manufactureOrModelDate)) {
- gui::DeviceProductInfo::ManufactureYear date;
- date.modelYear.year = manufacture->year;
- dinfo.manufactureOrModelDate.set<Tag::manufactureYear>(date);
- } else if (const auto* manufacture = std::get_if<DeviceProductInfo::ManufactureWeekAndYear>(
- &dpi->manufactureOrModelDate)) {
- gui::DeviceProductInfo::ManufactureWeekAndYear date;
- date.manufactureYear.modelYear.year = manufacture->year;
- date.week = manufacture->week;
- dinfo.manufactureOrModelDate.set<Tag::manufactureWeekAndYear>(date);
- }
-
- outInfo->deviceProductInfo = dinfo;
+ if (const std::optional<DeviceProductInfo> dpi = info.deviceProductInfo) {
+ gui::DeviceProductInfo dinfo;
+ dinfo.name = std::move(dpi->name);
+ dinfo.manufacturerPnpId = std::vector<uint8_t>(dpi->manufacturerPnpId.begin(),
+ dpi->manufacturerPnpId.end());
+ dinfo.productId = dpi->productId;
+ dinfo.relativeAddress =
+ std::vector<uint8_t>(dpi->relativeAddress.begin(), dpi->relativeAddress.end());
+ if (const auto* model =
+ std::get_if<DeviceProductInfo::ModelYear>(&dpi->manufactureOrModelDate)) {
+ gui::DeviceProductInfo::ModelYear modelYear;
+ modelYear.year = model->year;
+ dinfo.manufactureOrModelDate.set<Tag::modelYear>(modelYear);
+ } else if (const auto* manufacture = std::get_if<DeviceProductInfo::ManufactureYear>(
+ &dpi->manufactureOrModelDate)) {
+ gui::DeviceProductInfo::ManufactureYear date;
+ date.modelYear.year = manufacture->year;
+ dinfo.manufactureOrModelDate.set<Tag::manufactureYear>(date);
+ } else if (const auto* manufacture =
+ std::get_if<DeviceProductInfo::ManufactureWeekAndYear>(
+ &dpi->manufactureOrModelDate)) {
+ gui::DeviceProductInfo::ManufactureWeekAndYear date;
+ date.manufactureYear.modelYear.year = manufacture->year;
+ date.week = manufacture->week;
+ dinfo.manufactureOrModelDate.set<Tag::manufactureWeekAndYear>(date);
+ }
+
+ outInfo->deviceProductInfo = dinfo;
+ }
}
return binderStatusFromStatusT(status);
}
@@ -8993,28 +9092,28 @@ binder::Status SurfaceComposerAIDL::setGameContentType(const sp<IBinder>& displa
binder::Status SurfaceComposerAIDL::captureDisplay(
const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
- status_t status = mFlinger->captureDisplay(args, captureListener);
- return binderStatusFromStatusT(status);
+ mFlinger->captureDisplay(args, captureListener);
+ return binderStatusFromStatusT(NO_ERROR);
}
binder::Status SurfaceComposerAIDL::captureDisplayById(
int64_t displayId, const sp<IScreenCaptureListener>& captureListener) {
- status_t status;
+ // status_t status;
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
- status = mFlinger->captureDisplay(*id, captureListener);
+ mFlinger->captureDisplay(*id, captureListener);
} else {
- status = PERMISSION_DENIED;
+ invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
}
- return binderStatusFromStatusT(status);
+ return binderStatusFromStatusT(NO_ERROR);
}
binder::Status SurfaceComposerAIDL::captureLayers(
const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
- status_t status = mFlinger->captureLayers(args, captureListener);
- return binderStatusFromStatusT(status);
+ mFlinger->captureLayers(args, captureListener);
+ return binderStatusFromStatusT(NO_ERROR);
}
binder::Status SurfaceComposerAIDL::overrideHdrTypes(const sp<IBinder>& display,
@@ -9061,11 +9160,6 @@ binder::Status SurfaceComposerAIDL::getLayerDebugInfo(std::vector<gui::LayerDebu
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getColorManagement(bool* outGetColorManagement) {
- status_t status = mFlinger->getColorManagement(outGetColorManagement);
- return binderStatusFromStatusT(status);
-}
-
binder::Status SurfaceComposerAIDL::getCompositionPreference(gui::CompositionPreference* outPref) {
ui::Dataspace dataspace;
ui::PixelFormat pixelFormat;
@@ -9380,6 +9474,28 @@ binder::Status SurfaceComposerAIDL::removeWindowInfosListener(
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::getStalledTransactionInfo(
+ int pid, std::optional<gui::StalledTransactionInfo>* outInfo) {
+ const int callingPid = IPCThreadState::self()->getCallingPid();
+ const int callingUid = IPCThreadState::self()->getCallingUid();
+ if (!checkPermission(sAccessSurfaceFlinger, callingPid, callingUid)) {
+ return binderStatusFromStatusT(PERMISSION_DENIED);
+ }
+
+ std::optional<TransactionHandler::StalledTransactionInfo> stalledTransactionInfo;
+ status_t status = mFlinger->getStalledTransactionInfo(pid, stalledTransactionInfo);
+ if (stalledTransactionInfo) {
+ gui::StalledTransactionInfo result;
+ result.layerName = String16{stalledTransactionInfo->layerName.c_str()},
+ result.bufferId = stalledTransactionInfo->bufferId,
+ result.frameNumber = stalledTransactionInfo->frameNumber,
+ outInfo->emplace(std::move(result));
+ } else {
+ outInfo->reset();
+ }
+ return binderStatusFromStatusT(status);
+}
+
status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) {
if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d1b6660063..4d17fa7d2a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -236,9 +236,6 @@ public:
static uint32_t maxGraphicsWidth;
static uint32_t maxGraphicsHeight;
- // Indicate if device wants color management on its display.
- static const constexpr bool useColorManagement = true;
-
static bool useContextPriority;
// The data space and pixel format that SurfaceFlinger expects hardware composer
@@ -280,13 +277,6 @@ public:
// The CompositionEngine encapsulates all composition related interfaces and actions.
compositionengine::CompositionEngine& getCompositionEngine() const;
- // Obtains a name from the texture pool, or, if the pool is empty, posts a
- // synchronous message to the main thread to obtain one on the fly
- uint32_t getNewTexture();
-
- // utility function to delete a texture on the main thread
- void deleteTextureAsync(uint32_t texture);
-
renderengine::RenderEngine& getRenderEngine() const;
void onLayerFirstRef(Layer*);
@@ -327,6 +317,11 @@ public:
// on this behavior to increase contrast for some media sources.
bool mTreat170mAsSrgb = false;
+ // If true, then screenshots with an enhanced render intent will dim in gamma space.
+ // The purpose is to ensure that screenshots appear correct during system animations for devices
+ // that require that dimming must occur in gamma space.
+ bool mDimInGammaSpaceForEnhancedScreenshots = false;
+
// Allows to ignore physical orientation provided through hwc API in favour of
// 'ro.surface_flinger.primary_display_orientation'.
// TODO(b/246793311): Clean up a temporary property
@@ -535,9 +530,9 @@ private:
EventRegistrationFlags eventRegistration = {},
const sp<IBinder>& layerHandle = nullptr);
- status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
- status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
- status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
+ void captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
+ void captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ void captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
@@ -567,7 +562,6 @@ private:
const std::vector<ui::Hdr>& hdrTypes);
status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
- status_t getColorManagement(bool* outGetColorManagement) const;
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
ui::Dataspace* outWideColorGamutDataspace,
ui::PixelFormat* outWideColorGamutPixelFormat) const;
@@ -622,6 +616,9 @@ private:
status_t removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) const;
+ status_t getStalledTransactionInfo(
+ int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result);
+
// Implements IBinder::DeathRecipient.
void binderDied(const wp<IBinder>& who) override;
@@ -638,7 +635,8 @@ private:
// ICompositor overrides:
void configure() override REQUIRES(kMainThreadContext);
- bool commit(const scheduler::FrameTarget&) override REQUIRES(kMainThreadContext);
+ bool commit(PhysicalDisplayId pacesetterId, const scheduler::FrameTargets&) override
+ REQUIRES(kMainThreadContext);
CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
const scheduler::FrameTargeters&) override
REQUIRES(kMainThreadContext);
@@ -684,11 +682,10 @@ private:
REQUIRES(mStateLock);
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
- // Sets the active mode and a new refresh rate in SF.
- void updateInternalStateWithChangedMode() REQUIRES(mStateLock, kMainThreadContext);
- // Calls to setActiveMode on the main thread if there is a pending mode change
- // that needs to be applied.
- void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock, kMainThreadContext);
+
+ void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
+ void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
+
void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
// Called when active mode is no longer is progress
void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
@@ -836,10 +833,9 @@ private:
// Boot animation, on/off animations and screen capture
void startBootAnim();
- ftl::SharedFuture<FenceResult> captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction,
- ui::Size bufferSize, ui::PixelFormat,
- bool allowProtected, bool grayscale,
- const sp<IScreenCaptureListener>&);
+ void captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, ui::Size bufferSize,
+ ui::PixelFormat, bool allowProtected, bool grayscale,
+ const sp<IScreenCaptureListener>&);
ftl::SharedFuture<FenceResult> captureScreenCommon(
RenderAreaFuture, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
@@ -942,7 +938,8 @@ private:
template <typename Predicate>
sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) {
const auto it = std::find_if(mDisplays.begin(), mDisplays.end(),
- [&](const auto& pair) { return p(*pair.second); });
+ [&](const auto& pair)
+ REQUIRES(mStateLock) { return p(*pair.second); });
return it == mDisplays.end() ? nullptr : it->second;
}
@@ -1011,7 +1008,9 @@ private:
const DisplayDeviceState& drawingState)
REQUIRES(mStateLock, kMainThreadContext);
- void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
+ void dispatchDisplayHotplugEvent(PhysicalDisplayId, bool connected);
+ void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&)
+ REQUIRES(mStateLock);
/*
* VSYNC
@@ -1053,6 +1052,9 @@ private:
VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
void releaseVirtualDisplay(VirtualDisplayId);
+ // Returns a display other than `mActiveDisplayId` that can be activated, if any.
+ sp<DisplayDevice> getActivatableDisplay() const REQUIRES(mStateLock, kMainThreadContext);
+
void onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
const DisplayDevice& activeDisplay)
REQUIRES(mStateLock, kMainThreadContext);
@@ -1084,6 +1086,7 @@ private:
void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock);
void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
+ void dumpHdrInfo(std::string& result) const REQUIRES(mStateLock);
LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
void dumpOffscreenLayersProto(LayersProto& layersProto,
@@ -1206,6 +1209,8 @@ private:
// latched.
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
+ std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames;
+
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
// Sorted list of layers that were composed during previous frame. This is used to
@@ -1268,13 +1273,6 @@ private:
TransactionCallbackInvoker mTransactionCallbackInvoker;
- // We maintain a pool of pre-generated texture names to hand out to avoid
- // layer creation needing to run on the main thread (which it would
- // otherwise need to do to access RenderEngine).
- std::mutex mTexturePoolMutex;
- uint32_t mTexturePoolSize = 0;
- std::vector<uint32_t> mTexturePool;
-
std::atomic<size_t> mNumLayers = 0;
// to linkToDeath
@@ -1303,7 +1301,6 @@ private:
ui::Dataspace mDefaultCompositionDataspace;
ui::Dataspace mWideColorGamutCompositionDataspace;
- ui::Dataspace mColorSpaceAgnosticDataspace;
std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
std::atomic<int> mNumTrustedPresentationListeners = 0;
@@ -1331,9 +1328,6 @@ private:
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
- // below flags are set by main thread only
- bool mSetActiveModePending = false;
-
bool mLumaSampling = true;
sp<RegionSamplingThread> mRegionSamplingThread;
sp<FpsReporter> mFpsReporter;
@@ -1444,6 +1438,10 @@ private:
// WindowInfo ids visible during the last commit.
std::unordered_set<int32_t> mVisibleWindowIds;
+
+ // Mirroring
+ // Map of displayid to mirrorRoot
+ ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1505,7 +1503,6 @@ public:
const std::vector<int32_t>& hdrTypes) override;
binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override;
binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) override;
- binder::Status getColorManagement(bool* outGetColorManagement) override;
binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override;
binder::Status getDisplayedContentSamplingAttributes(
const sp<IBinder>& display, gui::ContentSamplingAttributes* outAttrs) override;
@@ -1557,6 +1554,8 @@ public:
gui::WindowInfosListenerInfo* outInfo) override;
binder::Status removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) override;
+ binder::Status getStalledTransactionInfo(int pid,
+ std::optional<gui::StalledTransactionInfo>* outInfo);
private:
static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 96c8b54005..66c8f33fe5 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -227,14 +227,6 @@ int32_t wcg_composition_pixel_format(PixelFormat defaultValue) {
return static_cast<int32_t>(defaultValue);
}
-int64_t color_space_agnostic_dataspace(Dataspace defaultValue) {
- auto temp = SurfaceFlingerProperties::color_space_agnostic_dataspace();
- if (temp.has_value()) {
- return *temp;
- }
- return static_cast<int64_t>(defaultValue);
-}
-
bool refresh_rate_switching(bool defaultValue) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 951f8f8cb3..a08042009b 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -71,9 +71,6 @@ int64_t wcg_composition_dataspace(
int32_t wcg_composition_pixel_format(
android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
-int64_t color_space_agnostic_dataspace(
- android::hardware::graphics::common::V1_2::Dataspace defaultValue);
-
bool refresh_rate_switching(bool defaultValue);
int32_t set_idle_timer_ms(int32_t defaultValue);
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index b7115207ac..55127349eb 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -16,12 +16,13 @@
},
{
"name": "CtsGraphicsTestCases",
+ // flaky on mixed gsi builds
"options": [
{
- "include-filter": "android.graphics.cts.VulkanPreTransformTest"
+ "exclude-filter": "android.graphics.cts.CameraGpuTest#testCameraImageCaptureAndRendering"
},
{
- "include-filter": "android.graphics.cts.FrameRateOverrideTest"
+ "exclude-filter": "android.graphics.cts.AnimatorLeakTest#testPauseResume"
}
]
},
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index ecdeabe528..b92d50b423 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -27,12 +27,13 @@
#include <utils/Trace.h>
#include "LayerTracing.h"
-#include "RingBuffer.h"
+#include "TransactionRingBuffer.h"
namespace android {
LayerTracing::LayerTracing()
- : mBuffer(std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>()) {}
+ : mBuffer(std::make_unique<TransactionRingBuffer<LayersTraceFileProto, LayersTraceProto>>()) {
+}
LayerTracing::~LayerTracing() = default;
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index 40b0fbee18..7c0d23d5b6 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -30,7 +30,7 @@ using namespace android::surfaceflinger;
namespace android {
template <typename FileProto, typename EntryProto>
-class RingBuffer;
+class TransactionRingBuffer;
class SurfaceFlinger;
@@ -71,7 +71,7 @@ private:
uint32_t mFlags = TRACE_INPUT;
mutable std::mutex mTraceLock;
bool mEnabled GUARDED_BY(mTraceLock) = false;
- std::unique_ptr<RingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer
+ std::unique_ptr<TransactionRingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer
GUARDED_BY(mTraceLock);
size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = 20 * 1024 * 1024;
};
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/TransactionRingBuffer.h
index b41c65b10e..e6f85ca4a5 100644
--- a/services/surfaceflinger/Tracing/RingBuffer.h
+++ b/services/surfaceflinger/Tracing/TransactionRingBuffer.h
@@ -32,7 +32,7 @@ namespace android {
class SurfaceFlinger;
template <typename FileProto, typename EntryProto>
-class RingBuffer {
+class TransactionRingBuffer {
public:
size_t size() const { return mSizeInBytes; }
size_t used() const { return mUsedInBytes; }
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index a59dc6e7dc..31bca5fc66 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -30,8 +30,8 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/Update.h"
#include "LocklessStack.h"
-#include "RingBuffer.h"
#include "TransactionProtoParser.h"
+#include "TransactionRingBuffer.h"
using namespace android::surfaceflinger;
@@ -81,7 +81,7 @@ private:
static constexpr auto FILE_PATH = "/data/misc/wmtrace/transactions_trace.winscope";
mutable std::mutex mTraceLock;
- RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer
+ TransactionRingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer
GUARDED_BY(mTraceLock);
size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/services/surfaceflinger/Utils/RingBuffer.h
new file mode 100644
index 0000000000..198e7b2cf1
--- /dev/null
+++ b/services/surfaceflinger/Utils/RingBuffer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <array>
+
+namespace android::utils {
+
+template <class T, size_t SIZE>
+class RingBuffer {
+ RingBuffer(const RingBuffer&) = delete;
+ void operator=(const RingBuffer&) = delete;
+
+public:
+ RingBuffer() = default;
+ ~RingBuffer() = default;
+
+ constexpr size_t capacity() const { return SIZE; }
+
+ size_t size() const { return mCount; }
+
+ T& next() {
+ mHead = static_cast<size_t>(mHead + 1) % SIZE;
+ if (mCount < SIZE) {
+ mCount++;
+ }
+ return mBuffer[static_cast<size_t>(mHead)];
+ }
+
+ T& front() { return (*this)[0]; }
+
+ T& back() { return (*this)[size() - 1]; }
+
+ T& operator[](size_t index) {
+ return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
+ }
+
+ const T& operator[](size_t index) const {
+ return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
+ }
+
+ void clear() {
+ mCount = 0;
+ mHead = -1;
+ }
+
+private:
+ std::array<T, SIZE> mBuffer;
+ int mHead = -1;
+ size_t mCount = 0;
+};
+
+} // namespace android::utils
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index b2d4131b5d..ed8bb7fc93 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -144,9 +144,6 @@ void SurfaceFlingerFuzzer::invokeFlinger() {
mFlinger->scheduleRepaint();
mFlinger->scheduleSample();
- uint32_t texture = mFlinger->getNewTexture();
- mFlinger->deleteTextureAsync(texture);
-
sp<IBinder> handle = defaultServiceManager()->checkService(
String16(mFdp.ConsumeRandomLengthString().c_str()));
LayerHandle::getLayer(handle);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 28ac664ba3..ca1af6ed84 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -286,7 +286,7 @@ public:
private:
// ICompositor overrides:
void configure() override {}
- bool commit(const scheduler::FrameTarget&) override { return false; }
+ bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { return false; }
CompositeResultsPerDisplay composite(PhysicalDisplayId,
const scheduler::FrameTargeters&) override {
return {};
diff --git a/services/surfaceflinger/layerproto/display.proto b/services/surfaceflinger/layerproto/display.proto
index c8cd9266a7..64de775b8b 100644
--- a/services/surfaceflinger/layerproto/display.proto
+++ b/services/surfaceflinger/layerproto/display.proto
@@ -35,4 +35,8 @@ message DisplayProto {
TransformProto transform = 6;
bool is_virtual = 7;
+
+ double dpi_x = 8;
+
+ double dpi_y = 9;
}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 5cac086c21..be29be4ef4 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -199,6 +199,7 @@ prop {
# useColorManagement indicates whether SurfaceFlinger should manage color
# by switching to appropriate color mode automatically depending on the
# Dataspace of the surfaces on screen.
+# DEPRECATED: SurfaceFlinger is always color managed.
prop {
api_name: "use_color_management"
type: Boolean
@@ -207,7 +208,7 @@ prop {
prop_name: "ro.surface_flinger.use_color_management"
}
-# The following four propertiess define:
+# The following four properties define:
# Returns the default data space and pixel format that SurfaceFlinger
# expects to receive and output as well as the wide color gamut data space
# and pixel format for wide color gamut surfaces.
@@ -276,7 +277,7 @@ prop {
# The variable works only when useColorManagement is specified. If
# unspecified, the data space follows what SurfaceFlinger expects for
# surfaces when useColorManagement is specified.
-
+# DEPRECATED: do not use
prop {
api_name: "color_space_agnostic_dataspace"
type: Long
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index b8068f79a4..2b1834d8e4 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1479,15 +1479,11 @@ TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
matrix[2][2] = 0.11;
// degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
+ ColorTransformHelper::DegammaColor(expected);
ColorTransformHelper::applyMatrix(expected, matrix);
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
+ ColorTransformHelper::GammaColor(expected);
const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
uint8_t(expected.b * 255), 255};
@@ -1537,15 +1533,11 @@ TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
matrix[2][2] = 0.11;
// degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
+ ColorTransformHelper::DegammaColor(expected);
ColorTransformHelper::applyMatrix(expected, matrix);
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
+ ColorTransformHelper::GammaColor(expected);
const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
uint8_t(expected.b * 255), 255};
@@ -1608,16 +1600,12 @@ TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
matrixParent[2][2] = 0.10;
// degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
+ ColorTransformHelper::DegammaColor(expected);
ColorTransformHelper::applyMatrix(expected, matrixChild);
ColorTransformHelper::applyMatrix(expected, matrixParent);
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
+ ColorTransformHelper::GammaColor(expected);
const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
uint8_t(expected.b * 255), 255};
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index badd5bebbc..2bdb8a452d 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -47,9 +47,6 @@ protected:
ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- binder::Status status = sf->getColorManagement(&mColorManagementUsed);
- ASSERT_NO_FATAL_FAILURE(gui::aidl_utils::statusTFromBinderStatus(status));
-
mCaptureArgs.displayToken = mDisplay;
}
@@ -282,7 +279,6 @@ protected:
const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
sp<SurfaceControl> mBlackBgSurface;
- bool mColorManagementUsed;
DisplayCaptureArgs mCaptureArgs;
ScreenCaptureResults mCaptureResults;
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index cbd54e7aa9..03de8d0b6d 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -184,6 +184,35 @@ TEST_F(LayerTransactionTest, BufferTakesPriorityOverColor) {
}
}
+TEST_F(LayerTransactionTest, CommitCallbackCalledOnce) {
+ auto callCount = 0;
+ auto commitCallback =
+ [&callCount](void* /* context */, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) mutable {
+ callCount++;
+ };
+
+ // Create two transactions that both contain the same callback id.
+ Transaction t1;
+ t1.addTransactionCommittedCallback(commitCallback, nullptr);
+ Parcel parcel;
+ t1.writeToParcel(&parcel);
+ parcel.setDataPosition(0);
+ Transaction t2;
+ t2.readFromParcel(&parcel);
+
+ // Apply the two transactions. There is a race here as we can't guarantee that the two
+ // transactions will be applied within the same SurfaceFlinger commit. If the transactions are
+ // applied within the same commit then we verify that callback ids are deduplicated within a
+ // single commit. Otherwise, we verify that commit callbacks are deduplicated across separate
+ // commits.
+ t1.apply();
+ t2.apply(/*synchronous=*/true);
+
+ ASSERT_EQ(callCount, 1);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index fa5fa956ed..3dd33b9957 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -148,6 +148,7 @@ cc_defaults {
defaults: [
"android.hardware.graphics.common-ndk_static",
"android.hardware.graphics.composer3-ndk_static",
+ "android.hardware.power-ndk_static",
"librenderengine_deps",
],
static_libs: [
@@ -161,7 +162,6 @@ cc_defaults {
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
"libaidlcommonsupport",
"libcompositionengine_mocks",
"libcompositionengine",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index e8a9cfe9d7..bdbf243dd4 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -31,8 +31,6 @@
#include <gui/LayerMetadata.h>
#include <log/log.h>
#include <renderengine/mock/FakeExternalTexture.h>
-#include <renderengine/mock/Framebuffer.h>
-#include <renderengine/mock/Image.h>
#include <renderengine/mock/RenderEngine.h>
#include <system/window.h>
#include <utils/String8.h>
@@ -599,8 +597,6 @@ struct BaseLayerProperties {
const renderengine::LayerSettings layer = layerSettings.back();
EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
- EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
- EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
EXPECT_EQ(false, layer.source.buffer.isOpaque);
EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
@@ -876,15 +872,11 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> {
using FlingerLayerType = sp<Layer>;
static FlingerLayerType createLayer(CompositionTest* test) {
- test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID);
-
- FlingerLayerType layer =
- Base::template createLayerWithFactory<Layer>(test, [test]() {
- LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
- LayerProperties::LAYER_FLAGS, LayerMetadata());
- args.textureName = test->mFlinger.mutableTexturePool().back();
- return sp<Layer>::make(args);
- });
+ FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
+ LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
+ LayerProperties::LAYER_FLAGS, LayerMetadata());
+ return sp<Layer>::make(args);
+ });
LayerProperties::setupLayerState(test, layer);
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 60ad7a3a03..2d87ddd488 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -78,7 +78,7 @@ TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
// Setting another mode should be cached but return None
@@ -86,7 +86,7 @@ TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
}
@@ -105,7 +105,7 @@ TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
hal::VsyncPeriodChangeConstraints constraints{
@@ -136,7 +136,7 @@ NO_THREAD_SAFETY_ANALYSIS {
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
hal::VsyncPeriodChangeConstraints constraints{
@@ -154,7 +154,7 @@ NO_THREAD_SAFETY_ANALYSIS {
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
index 1c9aee7443..d30d5b8223 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
@@ -99,8 +99,7 @@ void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
}
void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
- auto c = layer->getDrawingState();
- layer->commitTransaction(c);
+ layer->commitTransaction();
}
namespace {
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index e475b843c9..f64ba2a439 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -17,6 +17,8 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <gui/fake/BufferData.h>
+
#include "Client.h" // temporarily needed for LayerCreationArgs
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHierarchy.h"
@@ -333,6 +335,43 @@ protected:
mLifecycleManager.applyTransactions(transactions);
}
+ void setRoundedCorners(uint32_t id, float radius) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.cornerRadius = radius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eBufferChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().externalTexture = texture;
+ transactions.back().states.front().state.bufferData =
+ std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(),
+ texture->getHeight(), texture->getPixelFormat(),
+ texture->getUsage());
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void setDataspace(uint32_t id, ui::Dataspace dataspace) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.dataspace = dataspace;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index a581d5b6ff..84c37759ba 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -17,11 +17,14 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <renderengine/mock/FakeExternalTexture.h>
+
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
#include "FrontEnd/LayerSnapshotBuilder.h"
#include "Layer.h"
#include "LayerHierarchyTest.h"
+#include "ui/GraphicTypes.h"
#define UPDATE_AND_VERIFY(BUILDER, ...) \
({ \
@@ -68,12 +71,17 @@ protected:
setColor(id);
}
- void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges,
- const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) {
+ void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) {
if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
mHierarchyBuilder.update(mLifecycleManager.getLayers(),
mLifecycleManager.getDestroyedLayers());
}
+ args.root = mHierarchyBuilder.getHierarchy();
+ actualBuilder.update(args);
+ }
+
+ void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges,
+ const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) {
LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
.layerLifecycleManager = mLifecycleManager,
.includeMetadata = false,
@@ -83,7 +91,7 @@ protected:
.supportsBlur = true,
.supportedLayerGenericMetadata = {},
.genericLayerMetadataKeyMap = {}};
- actualBuilder.update(args);
+ update(actualBuilder, args);
// rebuild layer snapshots from scratch and verify that it matches the updated state.
LayerSnapshotBuilder expectedBuilder(args);
@@ -596,4 +604,56 @@ TEST_F(LayerSnapshotTest, framerate) {
scheduler::LayerInfo::FrameRateCompatibility::Default);
}
+TEST_F(LayerSnapshotTest, translateDataspace) {
+ setDataspace(1, ui::Dataspace::UNKNOWN);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->dataspace, ui::Dataspace::V0_SRGB);
+}
+
+TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) {
+ setRoundedCorners(1, 42.f);
+ setRoundedCorners(2, 42.f);
+ setCrop(1, Rect{1000, 1000});
+ setCrop(2, Rect{1000, 1000});
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners());
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 42.f);
+ EXPECT_TRUE(getSnapshot({.id = 2})->roundedCorner.hasRoundedCorners());
+
+ // add a buffer with the protected bit, check rounded corners are not set when
+ // skipRoundCornersWhenProtected == true
+ setBuffer(1,
+ std::make_shared<
+ renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ 1ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_PROTECTED /*usage*/));
+
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = false,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {},
+ .skipRoundCornersWhenProtected = true};
+ update(mSnapshotBuilder, args);
+ EXPECT_FALSE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners());
+ // layer 2 doesn't have a buffer and should be unaffected
+ EXPECT_TRUE(getSnapshot({.id = 2})->roundedCorner.hasRoundedCorners());
+
+ // remove protected bit, check rounded corners are set
+ setBuffer(1,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ 2ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ update(mSnapshotBuilder, args);
+ EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners());
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 42.f);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 1dcf222834..9aa089f900 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -35,7 +35,7 @@ using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
struct NoOpCompositor final : ICompositor {
void configure() override {}
- bool commit(const scheduler::FrameTarget&) override { return false; }
+ bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { return false; }
CompositeResultsPerDisplay composite(PhysicalDisplayId,
const scheduler::FrameTargeters&) override {
return {};
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 646d9cc0a0..0397b9936f 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -222,6 +222,7 @@ protected:
makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
static inline const DisplayModes kModes_1_5_10 = makeModes(kMode1, kMode5, kMode10);
+ static inline const DisplayModes kModes_60_90_120 = makeModes(kMode60, kMode90, kMode120);
// This is a typical TV configuration.
static inline const DisplayModes kModes_24_25_30_50_60_Frac =
@@ -1413,7 +1414,9 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) {
ss << "ExplicitDefault " << desired;
lr.name = ss.str();
- EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps());
+ const auto bestFps = selector.getBestFrameRateMode(layers)->getFps();
+ EXPECT_EQ(expected, bestFps)
+ << "expected " << expected << " for " << desired << " but got " << bestFps;
}
}
@@ -1422,7 +1425,7 @@ TEST_P(RefreshRateSelectorTest,
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
- // Test that 23.976 will choose 24 if 23.976 is not supported
+ // Test that 23.976 will prefer 60 over 59.94 and 30
{
auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60,
kMode60Frac),
@@ -1431,7 +1434,7 @@ TEST_P(RefreshRateSelectorTest,
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.desiredRefreshRate = 23.976_Hz;
lr.name = "ExplicitExactOrMultiple 23.976 Hz";
- EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 24 will choose 23.976 if 24 is not supported
@@ -1456,13 +1459,13 @@ TEST_P(RefreshRateSelectorTest,
EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
}
- // Test that 29.97 will choose 30 if 59.94 is not supported
+ // Test that 29.97 will choose 60 if 59.94 is not supported
{
auto selector = createSelector(makeModes(kMode30, kMode60), kModeId60);
lr.desiredRefreshRate = 29.97_Hz;
lr.name = "ExplicitExactOrMultiple 29.97 Hz";
- EXPECT_EQ(kModeId30, selector.getBestFrameRateMode(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 59.94 will choose 60 if 59.94 is not supported
@@ -2516,6 +2519,71 @@ TEST_P(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
}
+TEST_P(RefreshRateSelectorTest, test23976Chooses120) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "23.976 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 23.976_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test23976Chooses60IfThresholdIs120) {
+ auto selector =
+ createSelector(kModes_60_90_120, kModeId120, {.frameRateMultipleThreshold = 120});
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "23.976 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 23.976_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test25Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "25 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 25.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test2997Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "29.97 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 29.97_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test50Chooses120) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "50 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 50.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test50Chooses60IfThresholdIs120) {
+ auto selector =
+ createSelector(kModes_60_90_120, kModeId120, {.frameRateMultipleThreshold = 120});
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "50 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 50.00_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, test5994Chooses60) {
+ auto selector = createSelector(kModes_60_90_120, kModeId120);
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "59.94 ExplicitExactOrMultiple";
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].desiredRefreshRate = 59.94_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
@@ -3057,5 +3125,69 @@ TEST_P(RefreshRateSelectorTest, frameRateIsLowerThanMinSupported) {
{DisplayModeId(kModeId60), kLowerThanMin, kLowerThanMin}));
}
+// b/296079213
+TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_120) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ const FpsRange only120 = {120_Hz, 120_Hz};
+ const FpsRange allRange = {0_Hz, 120_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId120, {only120, allRange}, {allRange, allRange}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "30Hz ExplicitExactOrMultiple";
+ layers[0].desiredRefreshRate = 30_Hz;
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode120, 30_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
+
+ const FpsRange only90 = {90_Hz, 90_Hz};
+ const FpsRange allRange = {0_Hz, 90_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId90, {only90, allRange}, {allRange, allRange}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "30Hz ExplicitExactOrMultiple";
+ layers[0].desiredRefreshRate = 30_Hz;
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode90, 30_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_90_NonDivisor) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
+
+ const FpsRange only90 = {90_Hz, 90_Hz};
+ const FpsRange allRange = {0_Hz, 90_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId90, {only90, allRange}, {allRange, allRange}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "60Hz ExplicitExactOrMultiple";
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 44ab569bd1..a1e4e25c06 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -85,8 +85,7 @@ void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
void SetFrameRateTest::commitTransaction() {
for (auto layer : mLayers) {
- auto c = layer->getDrawingState();
- layer->commitTransaction(c);
+ layer->commitTransaction();
}
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 1cc9ba40bd..dbf0cd8772 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -91,7 +91,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
const auto& display = getCurrentDisplayState(displayToken);
EXPECT_TRUE(display.isVirtual());
EXPECT_FALSE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
+ EXPECT_EQ(name.c_str(), display.displayName);
// --------------------------------------------------------------------
// Cleanup conditions
@@ -123,7 +123,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
const auto& display = getCurrentDisplayState(displayToken);
EXPECT_TRUE(display.isVirtual());
EXPECT_TRUE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
+ EXPECT_EQ(name.c_str(), display.displayName);
// --------------------------------------------------------------------
// Cleanup conditions
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 703bdda694..24eb31821a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -67,10 +67,36 @@ public:
.WillByDefault(Return(true));
}
+ static constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
+ static constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+
+ auto injectOuterDisplay() {
+ constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+
+ constexpr bool kIsPrimary = false;
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
+ kIsPrimary)
+ .setHwcDisplayId(kOuterDisplayHwcId)
+ .setPowerMode(hal::PowerMode::OFF)
+ .inject(&mFlinger, mComposer);
+
+ mOuterDisplay = mFakeDisplayInjector.injectInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setPowerMode(hal::PowerMode::OFF);
+ injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
+ kModeId120);
+ },
+ {.displayId = kOuterDisplayId,
+ .hwcDisplayId = kOuterDisplayHwcId,
+ .isPrimary = kIsPrimary});
+
+ return std::forward_as_tuple(mDisplay, mOuterDisplay);
+ }
+
protected:
void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>);
- sp<DisplayDevice> mDisplay;
+ sp<DisplayDevice> mDisplay, mOuterDisplay;
mock::EventThread* mAppEventThread;
static constexpr DisplayModeId kModeId60{0};
@@ -328,32 +354,16 @@ MATCHER_P(ModeSettledTo, modeId, "") {
return true;
}
-TEST_F(DisplayModeSwitchingTest, multiDisplay) {
- constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
- constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
-
- constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
+ const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
- constexpr bool kIsPrimary = false;
- TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
- kIsPrimary)
- .setHwcDisplayId(kOuterDisplayHwcId)
- .inject(&mFlinger, mComposer);
-
- const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
- [&](FakeDisplayDeviceInjector& injector) {
- injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
- kModeId120);
- },
- {.displayId = kOuterDisplayId,
- .hwcDisplayId = kOuterDisplayHwcId,
- .isPrimary = kIsPrimary});
-
- const auto& innerDisplay = mDisplay;
+ EXPECT_TRUE(innerDisplay->isPoweredOn());
+ EXPECT_FALSE(outerDisplay->isPoweredOn());
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ // Only the inner display is powered on.
mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
@@ -388,6 +398,10 @@ TEST_F(DisplayModeSwitchingTest, multiDisplay) {
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ innerDisplay->setPowerMode(hal::PowerMode::OFF);
+ outerDisplay->setPowerMode(hal::PowerMode::ON);
+
+ // Only the outer display is powered on.
mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
@@ -409,5 +423,107 @@ TEST_F(DisplayModeSwitchingTest, multiDisplay) {
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
}
+TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
+ const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
+
+ EXPECT_TRUE(innerDisplay->isPoweredOn());
+ EXPECT_FALSE(outerDisplay->isPoweredOn());
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ outerDisplay->setPowerMode(hal::PowerMode::ON);
+
+ // Both displays are powered on.
+ mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kOuterDisplayHwcId,
+ hal::HWConfigId(kModeId60.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+}
+
+TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) {
+ const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
+
+ EXPECT_TRUE(innerDisplay->isPoweredOn());
+ EXPECT_FALSE(outerDisplay->isPoweredOn());
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ outerDisplay->setPowerMode(hal::PowerMode::ON);
+
+ // Both displays are powered on.
+ mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+
+ // Power off the outer display before the mode has been set.
+ outerDisplay->setPowerMode(hal::PowerMode::OFF);
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index bd2344c60d..ed8d909e63 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -55,13 +55,17 @@ struct FoldableTest : DisplayTransactionTest {
sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
};
-TEST_F(FoldableTest, foldUnfold) {
+TEST_F(FoldableTest, promotesPacesetterOnBoot) {
// When the device boots, the inner display should be the pacesetter.
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
// ...and should still be after powering on.
mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnFoldUnfold) {
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
// The outer display should become the pacesetter after folding.
mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
@@ -72,6 +76,10 @@ TEST_F(FoldableTest, foldUnfold) {
mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) {
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
// The inner display should stay the pacesetter if both are powered on.
// TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
@@ -81,6 +89,28 @@ TEST_F(FoldableTest, foldUnfold) {
// The outer display should become the pacesetter if designated.
mFlinger.scheduler()->setPacesetterDisplay(kOuterDisplayId);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+ // The inner display should become the pacesetter if designated.
+ mFlinger.scheduler()->setPacesetterDisplay(kInnerDisplayId);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOff) {
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+ // The outer display should become the pacesetter if the inner display powers off.
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+ // The outer display should stay the pacesetter if both are powered on.
+ // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+ // The inner display should become the pacesetter if the outer display powers off.
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
}
TEST_F(FoldableTest, doesNotRequestHardwareVsyncIfPoweredOff) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index f3c9d0dd44..151b178c01 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -180,7 +180,7 @@ public:
private:
// ICompositor overrides:
void configure() override {}
- bool commit(const scheduler::FrameTarget&) override { return false; }
+ bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { return false; }
CompositeResultsPerDisplay composite(PhysicalDisplayId,
const scheduler::FrameTargeters&) override {
return {};
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9b3a893409..02fa4153ed 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -386,10 +386,19 @@ public:
.sfWorkDuration = 10ms},
*mScheduler->getVsyncSchedule());
- mFlinger->commit(frameTargeter.target());
+ scheduler::FrameTargets targets;
+ scheduler::FrameTargeters targeters;
+
+ for (const auto& [id, display] :
+ FTL_FAKE_GUARD(mFlinger->mStateLock, mFlinger->mPhysicalDisplays)) {
+ targets.try_emplace(id, &frameTargeter.target());
+ targeters.try_emplace(id, &frameTargeter);
+ }
+
+ mFlinger->commit(displayId, targets);
if (composite) {
- mFlinger->composite(displayId, ftl::init::map(displayId, &frameTargeter));
+ mFlinger->composite(displayId, targeters);
}
}
@@ -622,7 +631,6 @@ public:
auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
- auto& mutableTexturePool() { return mFlinger->mTexturePool; }
auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; }
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 644b8c70c6..1f2a1edb77 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -306,6 +306,47 @@ public:
~FakeExternalTexture() = default;
};
+TEST_F(TransactionApplicationTest, ApplyTokensUseDifferentQueues) {
+ auto applyToken1 = sp<BBinder>::make();
+ auto applyToken2 = sp<BBinder>::make();
+
+ // Transaction 1 has a buffer with an unfired fence. It should not be ready to be applied.
+ TransactionState transaction1;
+ transaction1.applyToken = applyToken1;
+ transaction1.id = 42069;
+ transaction1.states.emplace_back();
+ transaction1.states[0].state.what |= layer_state_t::eBufferChanged;
+ transaction1.states[0].state.bufferData =
+ std::make_shared<fake::BufferData>(/* bufferId */ 1, /* width */ 1, /* height */ 1,
+ /* pixelFormat */ 0, /* outUsage */ 0);
+ transaction1.states[0].externalTexture =
+ std::make_shared<FakeExternalTexture>(*transaction1.states[0].state.bufferData);
+ transaction1.states[0].state.surface =
+ sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
+ ->getHandle();
+ auto fence = sp<mock::MockFence>::make();
+ EXPECT_CALL(*fence, getStatus()).WillRepeatedly(Return(Fence::Status::Unsignaled));
+ transaction1.states[0].state.bufferData->acquireFence = std::move(fence);
+ transaction1.states[0].state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
+ transaction1.isAutoTimestamp = true;
+
+ // Transaction 2 should be ready to be applied.
+ TransactionState transaction2;
+ transaction2.applyToken = applyToken2;
+ transaction2.id = 2;
+ transaction2.isAutoTimestamp = true;
+
+ mFlinger.setTransactionStateInternal(transaction1);
+ mFlinger.setTransactionStateInternal(transaction2);
+ mFlinger.flushTransactionQueues();
+ auto transactionQueues = mFlinger.getPendingTransactionQueue();
+
+ // Transaction 1 is still in its queue.
+ EXPECT_EQ(transactionQueues[applyToken1].size(), 1u);
+ // Transaction 2 has been dequeued.
+ EXPECT_EQ(transactionQueues[applyToken2].size(), 0u);
+}
+
class LatchUnsignaledTest : public TransactionApplicationTest {
public:
void TearDown() override {
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 764d19be0b..00b5bf0506 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -61,10 +61,7 @@ public:
return sp<Layer>::make(args);
}
- void commitTransaction(Layer* layer) {
- auto c = layer->getDrawingState();
- layer->commitTransaction(c);
- }
+ void commitTransaction(Layer* layer) { layer->commitTransaction(); }
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index e2c64917dc..caa265fcd9 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -60,10 +60,7 @@ public:
return sp<Layer>::make(args);
}
- void commitTransaction(Layer* layer) {
- auto c = layer->getDrawingState();
- layer->commitTransaction(c);
- }
+ void commitTransaction(Layer* layer) { layer->commitTransaction(); }
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 809966f9d6..71a2d2b9a6 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -25,7 +25,6 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/Update.h"
#include "Tracing/LayerTracing.h"
-#include "Tracing/RingBuffer.h"
#include "Tracing/TransactionTracing.h"
using namespace android::surfaceflinger;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index 2b9520fca7..364618d61a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -23,6 +23,7 @@
using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::SessionMode;
using android::binder::Status;
using namespace aidl::android::hardware::power;
@@ -45,6 +46,7 @@ public:
(override));
MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
index ef9cd9bc43..4cfdd58c70 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
@@ -19,5 +19,7 @@
#include <scheduler/FrameRateMode.h>
// Use a C style macro to keep the line numbers printed in gtest
-#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \
- EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode))
+#define EXPECT_FRAME_RATE_MODE(_modePtr, _fps, _mode) \
+ EXPECT_EQ((scheduler::FrameRateMode{(_fps), (_modePtr)}), (_mode)) \
+ << "Expected " << (_fps) << " (" << (_modePtr)->getFps() << ") but was " \
+ << (_mode).fps << " (" << (_mode).modePtr->getFps() << ")"
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index f297da5511..1675584f5a 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -170,7 +170,7 @@ public:
String8 err(String8::format("pixel @ (%3d, %3d): "
"expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
- EXPECT_EQ(String8(), err) << err.string();
+ EXPECT_EQ(String8(), err) << err.c_str();
}
}
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
index b033adbd05..7e382a3841 100644
--- a/services/vibratorservice/TEST_MAPPING
+++ b/services/vibratorservice/TEST_MAPPING
@@ -1,7 +1,27 @@
{
"presubmit": [
{
+ "name": "libvibratorservice_test",
+ "options": [
+ // TODO(b/293603710): Fix flakiness
+ {
+ "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleRunsOnlyAfterDelay"
+ },
+ // TODO(b/293623689): Fix flakiness
+ {
+ "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleMultipleCallbacksRunsInDelayOrder"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
"name": "libvibratorservice_test"
}
+ ],
+ "imports": [
+ {
+ "path": "cts/tests/vibrator"
+ }
]
}
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
index f2870b021d..7eda9ef0c7 100644
--- a/services/vibratorservice/VibratorCallbackScheduler.cpp
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -29,8 +29,11 @@ bool DelayedCallback::isExpired() const {
return mExpiration <= std::chrono::steady_clock::now();
}
-DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
- return mExpiration;
+std::chrono::milliseconds DelayedCallback::getWaitForExpirationDuration() const {
+ std::chrono::milliseconds delta = std::chrono::duration_cast<std::chrono::milliseconds>(
+ mExpiration - std::chrono::steady_clock::now());
+ // Return zero if this is already expired.
+ return delta > delta.zero() ? delta : delta.zero();
}
void DelayedCallback::run() const {
@@ -88,7 +91,9 @@ void CallbackScheduler::loop() {
mCondition.wait(mMutex);
} else {
// Wait until next callback expires, or a new one is scheduled.
- mCondition.wait_until(mMutex, mQueue.top().getExpiration());
+ // Use the monotonic steady clock to wait for the measured delay interval via wait_for
+ // instead of using a wall clock via wait_until.
+ mCondition.wait_for(mMutex, mQueue.top().getWaitForExpirationDuration());
}
}
}
diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
index 2c194b5526..c8ec15d414 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
@@ -30,15 +30,13 @@ namespace vibrator {
// Wrapper for a callback to be executed after a delay.
class DelayedCallback {
public:
- using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;
-
DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
: mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
~DelayedCallback() = default;
void run() const;
bool isExpired() const;
- Timestamp getExpiration() const;
+ std::chrono::milliseconds getWaitForExpirationDuration() const;
// Compare by expiration time, where A < B when A expires first.
bool operator<(const DelayedCallback& other) const;
@@ -46,7 +44,9 @@ public:
private:
std::function<void()> mCallback;
- Timestamp mExpiration;
+ // Use a steady monotonic clock to calculate the duration until expiration.
+ // This clock is not related to wall clock time and is most suitable for measuring intervals.
+ std::chrono::time_point<std::chrono::steady_clock> mExpiration;
};
// Schedules callbacks to be executed after a delay.
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
index 4c0910a75e..426cd426f7 100644
--- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -38,6 +38,9 @@ using namespace testing;
// -------------------------------------------------------------------------------------------------
+// Delay allowed for the scheduler to process callbacks during this test.
+static const auto TEST_TIMEOUT = 50ms;
+
class VibratorCallbackSchedulerTest : public Test {
public:
void SetUp() override {
@@ -67,46 +70,57 @@ protected:
return std::vector<int32_t>(mExpiredCallbacks);
}
- bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) {
- time_point<steady_clock> expiration = steady_clock::now() + timeout;
- while (steady_clock::now() < expiration) {
+ int32_t waitForCallbacks(int32_t callbackCount, milliseconds timeout) {
+ time_point<steady_clock> expirationTime = steady_clock::now() + timeout + TEST_TIMEOUT;
+ int32_t expiredCallbackCount = 0;
+ while (steady_clock::now() < expirationTime) {
std::lock_guard<std::mutex> lock(mMutex);
- if (callbackCount <= mExpiredCallbacks.size()) {
- return true;
+ expiredCallbackCount = mExpiredCallbacks.size();
+ if (callbackCount <= expiredCallbackCount) {
+ return expiredCallbackCount;
+ }
+ auto currentTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(
+ expirationTime - steady_clock::now());
+ if (currentTimeout > currentTimeout.zero()) {
+ // Use the monotonic steady clock to wait for the requested timeout via wait_for
+ // instead of using a wall clock via wait_until.
+ mCondition.wait_for(mMutex, currentTimeout);
}
- mCondition.wait_until(mMutex, expiration);
}
- return false;
+ return expiredCallbackCount;
}
};
// -------------------------------------------------------------------------------------------------
TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) {
- mScheduler->schedule(createCallback(1), 15ms);
+ time_point<steady_clock> startTime = steady_clock::now();
+ mScheduler->schedule(createCallback(1), 50ms);
- // Not triggered before delay.
- ASSERT_FALSE(waitForCallbacks(1, 10ms));
- ASSERT_TRUE(getExpiredCallbacks().empty());
+ ASSERT_EQ(1, waitForCallbacks(1, 50ms));
+ time_point<steady_clock> callbackTime = steady_clock::now();
- ASSERT_TRUE(waitForCallbacks(1, 10ms));
+ // Callback happened at least 50ms after the beginning of the test.
+ ASSERT_TRUE(startTime + 50ms <= callbackTime);
ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1));
}
TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) {
- mScheduler->schedule(createCallback(1), 10ms);
- mScheduler->schedule(createCallback(2), 5ms);
- mScheduler->schedule(createCallback(3), 1ms);
+ // Schedule first callbacks long enough that all 3 will be scheduled together and run in order.
+ mScheduler->schedule(createCallback(1), 50ms);
+ mScheduler->schedule(createCallback(2), 40ms);
+ mScheduler->schedule(createCallback(3), 10ms);
- ASSERT_TRUE(waitForCallbacks(3, 15ms));
+ ASSERT_EQ(3, waitForCallbacks(3, 50ms));
ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
}
TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
- mScheduler->schedule(createCallback(1), 5ms);
+ // Schedule callback long enough that scheduler will be destroyed while it's still scheduled.
+ mScheduler->schedule(createCallback(1), 50ms);
mScheduler.reset(nullptr);
- // Should time out waiting for callback to run.
- ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ // Should timeout waiting for callback to run.
+ ASSERT_EQ(0, waitForCallbacks(1, 50ms));
ASSERT_TRUE(getExpiredCallbacks().empty());
}
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
index 523f890ce7..d0a9da1ff5 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -113,7 +113,7 @@ status_t VirtualTouchpadService::dump(
static_cast<long>(client_pid_));
touchpad_->dumpInternal(result);
}
- write(fd, result.string(), result.size());
+ write(fd, result.c_str(), result.size());
return OK;
}
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 40cf9fbd67..e78f47003b 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -55,7 +55,12 @@ extern "C" {
* This version of the extension is largely designed to clean up the mix of
* GrallocUsage and GrallocUsage2
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
+ *
+ * This version of the extension cleans up a bug introduced in version 9
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -69,6 +74,8 @@ extern "C" {
VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \
VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3)
+#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 4)
/* clang-format off */
typedef enum VkSwapchainImageUsageFlagBitsANDROID {
@@ -152,6 +159,23 @@ typedef struct {
VkImageUsageFlags imageUsage;
} VkGrallocUsageInfoANDROID;
+/*
+ * struct VkGrallocUsageInfo2ANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * format: value specifying the format the image will be created with
+ * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ * swapchainImageUsage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ */
+typedef struct {
+ VkStructureType sType;
+ const void* pNext;
+ VkFormat format;
+ VkImageUsageFlags imageUsage;
+ VkSwapchainImageUsageFlagsANDROID swapchainImageUsage;
+} VkGrallocUsageInfo2ANDROID;
+
/* DEPRECATED in SPEC_VERSION 6 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
VkDevice device,
@@ -168,12 +192,18 @@ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(
uint64_t* grallocConsumerUsage,
uint64_t* grallocProducerUsage);
-/* ADDED in SPEC_VERSION 9 */
+/* DEPRECATED in SPEC_VERSION 10 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)(
VkDevice device,
const VkGrallocUsageInfoANDROID* grallocUsageInfo,
uint64_t* grallocUsage);
+/* ADDED in SPEC_VERSION 10 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage4ANDROID)(
+ VkDevice device,
+ const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage);
+
typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
VkDevice device,
VkImage image,
@@ -208,13 +238,20 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
uint64_t* grallocProducerUsage
);
-/* ADDED in SPEC_VERSION 9 */
+/* DEPRECATED in SPEC_VERSION 10 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID(
VkDevice device,
const VkGrallocUsageInfoANDROID* grallocUsageInfo,
uint64_t* grallocUsage
);
+/* ADDED in SPEC_VERSION 10 */
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage4ANDROID(
+ VkDevice device,
+ const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage
+);
+
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
VkDevice device,
VkImage image,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 273cdd547e..bdba27e6a1 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -763,6 +763,17 @@ void CreateInfoWrapper::FilterExtension(const char* name) {
continue;
}
+ // Ignore duplicate extensions (see: b/288929054)
+ bool duplicate_entry = false;
+ for (uint32_t j = 0; j < filter.name_count; j++) {
+ if (strcmp(name, filter.names[j]) == 0) {
+ duplicate_entry = true;
+ break;
+ }
+ }
+ if (duplicate_entry == true)
+ continue;
+
filter.names[filter.name_count++] = name;
if (ext_bit != ProcHook::EXTENSION_UNKNOWN) {
if (ext_bit == ProcHook::ANDROID_native_buffer)
@@ -1422,13 +1433,15 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice,
if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
!data->driver.GetSwapchainGrallocUsageANDROID &&
!data->driver.GetSwapchainGrallocUsage2ANDROID &&
- !data->driver.GetSwapchainGrallocUsage3ANDROID) {
+ !data->driver.GetSwapchainGrallocUsage3ANDROID &&
+ !data->driver.GetSwapchainGrallocUsage4ANDROID) {
ALOGE(
"Driver's implementation of ANDROID_native_buffer is broken;"
" must expose at least one of "
"vkGetSwapchainGrallocUsageANDROID or "
"vkGetSwapchainGrallocUsage2ANDROID or "
- "vkGetSwapchainGrallocUsage3ANDROID");
+ "vkGetSwapchainGrallocUsage3ANDROID or "
+ "vkGetSwapchainGrallocUsage4ANDROID");
data->driver.DestroyDevice(dev, pAllocator);
FreeDeviceData(data, data_allocator);
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 798af5c6a6..8f090083f8 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -512,6 +512,13 @@ const ProcHook g_proc_hooks[] = {
nullptr,
},
{
+ "vkGetSwapchainGrallocUsage4ANDROID",
+ ProcHook::DEVICE,
+ ProcHook::ANDROID_native_buffer,
+ nullptr,
+ nullptr,
+ },
+ {
"vkGetSwapchainGrallocUsageANDROID",
ProcHook::DEVICE,
ProcHook::ANDROID_native_buffer,
@@ -692,6 +699,7 @@ bool InitDriverTable(VkDevice dev,
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID);
+ INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage4ANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
// clang-format on
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 31ba04ba1f..4527214c3f 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -128,6 +128,7 @@ struct DeviceDriverTable {
PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID;
+ PFN_vkGetSwapchainGrallocUsage4ANDROID GetSwapchainGrallocUsage4ANDROID;
PFN_vkAcquireImageANDROID AcquireImageANDROID;
PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
// clang-format on
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index a14fed222a..d059f8fe4c 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -23,6 +23,7 @@
#include <dlfcn.h>
#include <string.h>
#include <sys/prctl.h>
+#include <unistd.h>
#include <mutex>
#include <string>
@@ -362,6 +363,7 @@ template <typename Functor>
void ForEachFileInZip(const std::string& zipname,
const std::string& dir_in_zip,
Functor functor) {
+ static const size_t kPageSize = getpagesize();
int32_t err;
ZipArchiveHandle zip = nullptr;
if ((err = OpenArchive(zipname.c_str(), &zip)) != 0) {
@@ -389,7 +391,7 @@ void ForEachFileInZip(const std::string& zipname,
// the APK. Loading still may fail for other reasons, but this at least
// lets us avoid failed-to-load log messages in the typical case of
// compressed and/or unaligned libraries.
- if (entry.method != kCompressStored || entry.offset % PAGE_SIZE != 0)
+ if (entry.method != kCompressStored || entry.offset % kPageSize != 0)
continue;
functor(filename);
}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 07b95694d5..bffbe9d8d5 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1576,7 +1576,47 @@ VkResult CreateSwapchainKHR(VkDevice device,
void* usage_info_pNext = nullptr;
VkImageCompressionControlEXT image_compression = {};
uint64_t native_usage = 0;
- if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+ if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
+ VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType =
+ VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+ gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage4ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
VkGrallocUsageInfoANDROID gralloc_usage_info = {};
gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index f998b1ad18..2e87f1718b 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -959,6 +959,17 @@ VkResult GetSwapchainGrallocUsage3ANDROID(
return VK_SUCCESS;
}
+VkResult GetSwapchainGrallocUsage4ANDROID(
+ VkDevice,
+ const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage) {
+ // The null driver never reads or writes the gralloc buffer
+ ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__,
+ grallocUsageInfo->format);
+ *grallocUsage = 0;
+ return VK_SUCCESS;
+}
+
VkResult AcquireImageANDROID(VkDevice,
VkImage,
int fence,
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 0cb7bd3185..935535f5bf 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -262,6 +262,7 @@ const NameProc kInstanceProcs[] = {
{"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
{"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
{"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))},
+ {"vkGetSwapchainGrallocUsage4ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage4ANDROID>(GetSwapchainGrallocUsage4ANDROID))},
{"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
{"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 5c7fea0fa8..fb3bd05e07 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -210,6 +210,7 @@ VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescripto
VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage4ANDROID(VkDevice device, const VkGrallocUsageInfo2ANDROID* grallocUsageInfo, uint64_t* grallocUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index c25c6cbda0..866c1b7b75 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -70,6 +70,7 @@ _OPTIONAL_COMMANDS = [
'vkGetSwapchainGrallocUsageANDROID',
'vkGetSwapchainGrallocUsage2ANDROID',
'vkGetSwapchainGrallocUsage3ANDROID',
+ 'vkGetSwapchainGrallocUsage4ANDROID',
]
# Dict for mapping dispatch table to a type.