summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/arect/Android.bp27
-rw-r--r--libs/arect/MODULE_LICENSE_APACHE20
-rw-r--r--libs/arect/NOTICE190
-rw-r--r--libs/arect/include/android/rect.h62
-rw-r--r--libs/binder/Android.bp8
-rw-r--r--libs/binder/Binder.cpp7
-rw-r--r--libs/binder/IActivityManager.cpp65
-rw-r--r--libs/binder/IMediaResourceMonitor.cpp2
-rw-r--r--libs/binder/IMemory.cpp7
-rw-r--r--libs/binder/IPCThreadState.cpp5
-rw-r--r--libs/binder/IResultReceiver.cpp2
-rw-r--r--libs/binder/IServiceManager.cpp8
-rw-r--r--libs/binder/IShellCallback.cpp86
-rw-r--r--libs/binder/IpPrefix.cpp174
-rw-r--r--libs/binder/MemoryHeapBase.cpp4
-rw-r--r--libs/binder/Parcel.cpp184
-rw-r--r--libs/binder/PersistableBundle.cpp17
-rw-r--r--libs/binder/ProcessInfoService.cpp34
-rw-r--r--libs/binder/ProcessState.cpp36
-rw-r--r--libs/binder/Value.cpp418
-rw-r--r--libs/binder/include/binder/AppOpsManager.h3
-rw-r--r--libs/binder/include/binder/IActivityManager.h42
-rw-r--r--libs/binder/include/binder/IBinder.h3
-rw-r--r--libs/binder/include/binder/IInterface.h18
-rw-r--r--libs/binder/include/binder/IPCThreadState.h1
-rw-r--r--libs/binder/include/binder/IShellCallback.h55
-rw-r--r--libs/binder/include/binder/IpPrefix.h88
-rw-r--r--libs/binder/include/binder/Map.h39
-rw-r--r--libs/binder/include/binder/Parcel.h48
-rw-r--r--libs/binder/include/binder/ProcessInfoService.h17
-rw-r--r--libs/binder/include/binder/ProcessState.h10
-rw-r--r--libs/binder/include/binder/SafeInterface.h705
-rw-r--r--libs/binder/include/binder/Value.h186
-rw-r--r--libs/binder/include/private/binder/ParcelValTypes.h45
-rw-r--r--libs/binder/include/private/binder/Static.h1
-rw-r--r--libs/binder/tests/Android.bp34
-rw-r--r--libs/binder/tests/binderLibTest.cpp77
-rw-r--r--libs/binder/tests/binderSafeInterfaceTest.cpp819
-rw-r--r--libs/binder/tests/binderThroughputTest.cpp39
-rw-r--r--libs/binder/tests/binderValueTypeTest.cpp111
-rw-r--r--libs/binder/tests/schd-dbg.cpp43
-rw-r--r--libs/gui/Android.bp53
-rw-r--r--libs/gui/BitTube.cpp129
-rw-r--r--libs/gui/BufferItem.cpp15
-rw-r--r--libs/gui/BufferItemConsumer.cpp18
-rw-r--r--libs/gui/BufferQueue.cpp23
-rw-r--r--libs/gui/BufferQueueConsumer.cpp27
-rw-r--r--libs/gui/BufferQueueCore.cpp56
-rw-r--r--libs/gui/BufferQueueProducer.cpp239
-rw-r--r--libs/gui/ConsumerBase.cpp35
-rw-r--r--libs/gui/CpuConsumer.cpp2
-rw-r--r--libs/gui/DisplayEventReceiver.cpp20
-rw-r--r--libs/gui/FrameTimestamps.cpp701
-rw-r--r--libs/gui/GLConsumer.cpp29
-rw-r--r--libs/gui/GraphicBufferAlloc.cpp55
-rw-r--r--libs/gui/IConsumerListener.cpp171
-rw-r--r--libs/gui/IDisplayEventConnection.cpp104
-rw-r--r--libs/gui/IGraphicBufferAlloc.cpp133
-rw-r--r--libs/gui/IGraphicBufferConsumer.cpp529
-rw-r--r--libs/gui/IGraphicBufferProducer.cpp343
-rw-r--r--libs/gui/ISurfaceComposer.cpp166
-rw-r--r--libs/gui/ISurfaceComposerClient.cpp200
-rw-r--r--libs/gui/LayerState.cpp15
-rw-r--r--libs/gui/Surface.cpp541
-rw-r--r--libs/gui/SurfaceComposerClient.cpp201
-rw-r--r--libs/gui/SurfaceControl.cpp61
-rw-r--r--libs/gui/bufferqueue/1.0/B2HProducerListener.cpp46
-rw-r--r--libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp1234
-rw-r--r--libs/gui/include/private/gui/BitTube.h97
-rw-r--r--libs/gui/tests/Android.bp10
-rw-r--r--libs/gui/tests/BufferItemConsumer_test.cpp206
-rw-r--r--libs/gui/tests/BufferQueue_test.cpp211
-rw-r--r--libs/gui/tests/CpuConsumer_test.cpp2
-rw-r--r--libs/gui/tests/DummyConsumer.h6
-rw-r--r--libs/gui/tests/FillBuffer.cpp2
-rw-r--r--libs/gui/tests/IGraphicBufferProducer_test.cpp53
-rw-r--r--libs/gui/tests/Malicious.cpp202
-rw-r--r--libs/gui/tests/SRGB_test.cpp486
-rw-r--r--libs/gui/tests/StreamSplitter_test.cpp12
-rw-r--r--libs/gui/tests/SurfaceTextureClient_test.cpp1
-rw-r--r--libs/gui/tests/SurfaceTextureFBO_test.cpp4
-rw-r--r--libs/gui/tests/SurfaceTextureGL_test.cpp8
-rw-r--r--libs/gui/tests/Surface_test.cpp1337
-rw-r--r--libs/gui/view/Surface.cpp97
-rw-r--r--libs/hwc2on1adapter/Android.bp72
-rw-r--r--libs/hwc2on1adapter/CleanSpec.mk52
-rw-r--r--libs/hwc2on1adapter/HWC2On1Adapter.cpp2620
-rw-r--r--libs/hwc2on1adapter/MiniFence.cpp42
-rw-r--r--libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h738
-rw-r--r--libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h59
-rw-r--r--libs/input/Android.bp6
-rw-r--r--libs/input/InputDevice.cpp21
-rw-r--r--libs/input/Keyboard.cpp1
-rw-r--r--libs/math/Android.bp21
-rw-r--r--libs/math/MODULE_LICENSE_APACHE20
-rw-r--r--libs/math/NOTICE190
-rw-r--r--libs/math/include/math/TMatHelpers.h644
-rw-r--r--libs/math/include/math/TQuatHelpers.h300
-rw-r--r--libs/math/include/math/TVecHelpers.h608
-rw-r--r--libs/math/include/math/half.h212
-rw-r--r--libs/math/include/math/mat2.h377
-rw-r--r--libs/math/include/math/mat3.h440
-rw-r--r--libs/math/include/math/mat4.h586
-rw-r--r--libs/math/include/math/quat.h192
-rw-r--r--libs/math/include/math/scalar.h44
-rw-r--r--libs/math/include/math/vec2.h125
-rw-r--r--libs/math/include/math/vec3.h131
-rw-r--r--libs/math/include/math/vec4.h128
-rw-r--r--libs/math/tests/Android.bp39
-rw-r--r--libs/math/tests/half_test.cpp96
-rw-r--r--libs/math/tests/mat_test.cpp692
-rw-r--r--libs/math/tests/quat_test.cpp301
-rw-r--r--libs/math/tests/vec_test.cpp (renamed from libs/ui/tests/vec_test.cpp)119
-rw-r--r--libs/nativewindow/AHardwareBuffer.cpp443
-rw-r--r--libs/nativewindow/ANativeWindow.cpp225
-rw-r--r--libs/nativewindow/Android.bp66
-rw-r--r--libs/nativewindow/MODULE_LICENSE_APACHE20
-rw-r--r--libs/nativewindow/NOTICE190
-rw-r--r--libs/nativewindow/include/android/hardware_buffer.h246
-rw-r--r--libs/nativewindow/include/android/native_window.h198
-rw-r--r--libs/nativewindow/include/private/android/AHardwareBufferHelpers.h60
-rw-r--r--libs/nativewindow/include/system/window.h972
-rw-r--r--libs/nativewindow/include/vndk/hardware_buffer.h74
-rw-r--r--libs/nativewindow/include/vndk/window.h412
-rw-r--r--libs/nativewindow/libnativewindow.map.txt26
-rw-r--r--libs/nativewindow/tests/AHardwareBufferTest.cpp108
-rw-r--r--libs/nativewindow/tests/Android.bp26
-rw-r--r--libs/nativewindow/tests/c_compatibility.c22
-rw-r--r--libs/sensor/Android.bp61
-rw-r--r--libs/sensor/BitTube.cpp174
-rw-r--r--libs/sensor/ISensorEventConnection.cpp (renamed from libs/gui/ISensorEventConnection.cpp)26
-rw-r--r--libs/sensor/ISensorServer.cpp (renamed from libs/gui/ISensorServer.cpp)74
-rw-r--r--libs/sensor/Sensor.cpp (renamed from libs/gui/Sensor.cpp)51
-rw-r--r--libs/sensor/SensorEventQueue.cpp (renamed from libs/gui/SensorEventQueue.cpp)15
-rw-r--r--libs/sensor/SensorManager.cpp (renamed from libs/gui/SensorManager.cpp)128
-rw-r--r--libs/sensor/include/sensor/BitTube.h90
-rw-r--r--libs/sensor/include/sensor/ISensorEventConnection.h59
-rw-r--r--libs/sensor/include/sensor/ISensorServer.h70
-rw-r--r--libs/sensor/include/sensor/Sensor.h140
-rw-r--r--libs/sensor/include/sensor/SensorEventQueue.h108
-rw-r--r--libs/sensor/include/sensor/SensorManager.h94
-rw-r--r--libs/sensor/tests/Android.bp29
-rw-r--r--libs/sensor/tests/Sensor_test.cpp (renamed from libs/gui/tests/Sensor_test.cpp)2
-rw-r--r--libs/ui/Android.bp36
-rw-r--r--libs/ui/ColorSpace.cpp417
-rw-r--r--libs/ui/DebugUtils.cpp187
-rw-r--r--libs/ui/Fence.cpp13
-rw-r--r--libs/ui/FenceTime.cpp367
-rw-r--r--libs/ui/Gralloc1.cpp402
-rw-r--r--libs/ui/Gralloc1On0Adapter.cpp479
-rw-r--r--libs/ui/Gralloc2.cpp253
-rw-r--r--libs/ui/GraphicBuffer.cpp202
-rw-r--r--libs/ui/GraphicBufferAllocator.cpp106
-rw-r--r--libs/ui/GraphicBufferMapper.cpp204
-rw-r--r--libs/ui/GraphicsEnv.cpp (renamed from libs/gui/GraphicsEnv.cpp)43
-rw-r--r--libs/ui/HdrCapabilities.cpp90
-rw-r--r--libs/ui/PixelFormat.cpp6
-rw-r--r--libs/ui/tests/Android.bp10
-rw-r--r--libs/ui/tests/colorspace_test.cpp183
-rw-r--r--libs/ui/tests/mat_test.cpp139
-rw-r--r--libs/ui/tools/Android.bp35
-rw-r--r--libs/ui/tools/lutgen.cpp196
-rw-r--r--libs/vr/.clang-format5
-rw-r--r--libs/vr/Android.bp3
-rw-r--r--libs/vr/CPPLINT.cfg2
-rw-r--r--libs/vr/libbroadcastring/Android.bp35
-rw-r--r--libs/vr/libbroadcastring/broadcast_ring_test.cc866
-rw-r--r--libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h668
-rw-r--r--libs/vr/libbufferhub/Android.bp58
-rw-r--r--libs/vr/libbufferhub/buffer_hub_client.cpp412
-rw-r--r--libs/vr/libbufferhub/buffer_hub_rpc.cpp9
-rw-r--r--libs/vr/libbufferhub/bufferhub_tests.cpp226
-rw-r--r--libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h313
-rw-r--r--libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h241
-rw-r--r--libs/vr/libbufferhub/include/private/dvr/ion_buffer.h96
-rw-r--r--libs/vr/libbufferhub/include/private/dvr/native_buffer.h176
-rw-r--r--libs/vr/libbufferhub/ion_buffer.cpp240
-rw-r--r--libs/vr/libbufferhubqueue/Android.bp59
-rw-r--r--libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp635
-rw-r--r--libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp643
-rw-r--r--libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h456
-rw-r--r--libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h183
-rw-r--r--libs/vr/libbufferhubqueue/tests/Android.bp48
-rw-r--r--libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp421
-rw-r--r--libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp512
-rw-r--r--libs/vr/libdisplay/Android.bp66
-rw-r--r--libs/vr/libdisplay/display_client.cpp210
-rw-r--r--libs/vr/libdisplay/display_manager_client.cpp78
-rw-r--r--libs/vr/libdisplay/display_protocol.cpp13
-rw-r--r--libs/vr/libdisplay/include/CPPLINT.cfg1
-rw-r--r--libs/vr/libdisplay/include/dvr/dvr_display_types.h65
-rw-r--r--libs/vr/libdisplay/include/private/dvr/display_client.h91
-rw-r--r--libs/vr/libdisplay/include/private/dvr/display_manager_client.h51
-rw-r--r--libs/vr/libdisplay/include/private/dvr/display_protocol.h283
-rw-r--r--libs/vr/libdisplay/include/private/dvr/vsync_client.h69
-rw-r--r--libs/vr/libdisplay/system/CPPLINT.cfg1
-rw-r--r--libs/vr/libdisplay/vsync_client.cpp76
-rw-r--r--libs/vr/libdvr/Android.bp89
-rw-r--r--libs/vr/libdvr/dvr_api.cpp47
-rw-r--r--libs/vr/libdvr/dvr_buffer.cpp202
-rw-r--r--libs/vr/libdvr/dvr_buffer_queue.cpp204
-rw-r--r--libs/vr/libdvr/dvr_display_manager.cpp304
-rw-r--r--libs/vr/libdvr/dvr_hardware_composer_client.cpp237
-rw-r--r--libs/vr/libdvr/dvr_internal.h70
-rw-r--r--libs/vr/libdvr/dvr_surface.cpp182
-rw-r--r--libs/vr/libdvr/dvr_vsync.cpp33
-rw-r--r--libs/vr/libdvr/exported_apis.lds9
-rw-r--r--libs/vr/libdvr/include/CPPLINT.cfg1
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_api.h312
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_api_entries.h135
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_buffer.h104
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_buffer_queue.h44
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_deleter.h86
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_display_manager.h140
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h107
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h50
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_surface.h91
-rw-r--r--libs/vr/libdvr/include/dvr/dvr_vsync.h26
-rw-r--r--libs/vr/libdvr/tests/Android.bp53
-rw-r--r--libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp217
-rw-r--r--libs/vr/libdvr/tests/dvr_display_manager-test.cpp579
-rw-r--r--libs/vr/libdvr/tests/dvr_named_buffer-test.cpp160
-rw-r--r--libs/vr/libdvrcommon/Android.bp71
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/benchmark.h86
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/clock_ns.h84
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/debug.h37
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/eigen.h57
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h64
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/field_of_view.h95
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/log_helpers.h64
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h26
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/numeric.h175
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/ortho.h31
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/pose.h118
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/range.h43
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h99
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h124
-rw-r--r--libs/vr/libdvrcommon/include/private/dvr/types.h51
-rw-r--r--libs/vr/libdvrcommon/tests/numeric_test.cpp67
-rw-r--r--libs/vr/libdvrcommon/tests/pose_test.cpp154
-rw-r--r--libs/vr/libpdx/Android.bp60
-rw-r--r--libs/vr/libpdx/client.cpp287
-rw-r--r--libs/vr/libpdx/client_tests.cpp567
-rw-r--r--libs/vr/libpdx/encoder_performance_test.cpp515
-rw-r--r--libs/vr/libpdx/mock_tests.cpp20
-rw-r--r--libs/vr/libpdx/private/pdx/channel_handle.h123
-rw-r--r--libs/vr/libpdx/private/pdx/client.h301
-rw-r--r--libs/vr/libpdx/private/pdx/client_channel.h58
-rw-r--r--libs/vr/libpdx/private/pdx/client_channel_factory.h21
-rw-r--r--libs/vr/libpdx/private/pdx/file_handle.h141
-rw-r--r--libs/vr/libpdx/private/pdx/message_reader.h38
-rw-r--r--libs/vr/libpdx/private/pdx/message_writer.h40
-rw-r--r--libs/vr/libpdx/private/pdx/mock_client_channel.h56
-rw-r--r--libs/vr/libpdx/private/pdx/mock_client_channel_factory.h19
-rw-r--r--libs/vr/libpdx/private/pdx/mock_message_reader.h27
-rw-r--r--libs/vr/libpdx/private/pdx/mock_message_writer.h35
-rw-r--r--libs/vr/libpdx/private/pdx/mock_service_dispatcher.h24
-rw-r--r--libs/vr/libpdx/private/pdx/mock_service_endpoint.h74
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/argument_encoder.h184
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/array_wrapper.h111
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h177
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h43
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h47
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/encoding.h616
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/enumeration.h65
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/find_replace.h45
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/function_traits.h61
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/macros.h148
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/message_buffer.h22
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/payload.h157
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h38
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/remote_method.h473
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/remote_method_type.h73
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/sequence.h56
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/serializable.h150
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/serialization.h1998
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/string_wrapper.h129
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h134
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/type_operators.h195
-rw-r--r--libs/vr/libpdx/private/pdx/rpc/variant.h686
-rw-r--r--libs/vr/libpdx/private/pdx/service.h742
-rw-r--r--libs/vr/libpdx/private/pdx/service_dispatcher.h79
-rw-r--r--libs/vr/libpdx/private/pdx/service_endpoint.h144
-rw-r--r--libs/vr/libpdx/private/pdx/status.h180
-rw-r--r--libs/vr/libpdx/private/pdx/trace.h35
-rw-r--r--libs/vr/libpdx/private/pdx/utility.h367
-rw-r--r--libs/vr/libpdx/serialization_tests.cpp2505
-rw-r--r--libs/vr/libpdx/service.cpp684
-rw-r--r--libs/vr/libpdx/service_tests.cpp807
-rw-r--r--libs/vr/libpdx/status.cpp15
-rw-r--r--libs/vr/libpdx/status_tests.cpp125
-rw-r--r--libs/vr/libpdx/thread_local_buffer_tests.cpp117
-rw-r--r--libs/vr/libpdx/variant_tests.cpp1101
-rw-r--r--libs/vr/libpdx_default_transport/Android.bp70
-rw-r--r--libs/vr/libpdx_default_transport/pdx_benchmarks.cpp1090
-rw-r--r--libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h91
-rw-r--r--libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h17
-rw-r--r--libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h17
-rw-r--r--libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h17
-rw-r--r--libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h17
-rw-r--r--libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h16
-rw-r--r--libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h17
-rw-r--r--libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h17
-rw-r--r--libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h17
-rw-r--r--libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h17
-rw-r--r--libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h16
-rw-r--r--libs/vr/libpdx_default_transport/servicetool.cpp244
-rw-r--r--libs/vr/libpdx_uds/Android.bp57
-rw-r--r--libs/vr/libpdx_uds/channel_event_set.cpp115
-rw-r--r--libs/vr/libpdx_uds/channel_manager.cpp44
-rw-r--r--libs/vr/libpdx_uds/client_channel.cpp293
-rw-r--r--libs/vr/libpdx_uds/client_channel_factory.cpp160
-rw-r--r--libs/vr/libpdx_uds/client_channel_tests.cpp162
-rw-r--r--libs/vr/libpdx_uds/ipc_helper.cpp534
-rw-r--r--libs/vr/libpdx_uds/ipc_helper_tests.cpp365
-rw-r--r--libs/vr/libpdx_uds/private/uds/channel_event_set.h62
-rw-r--r--libs/vr/libpdx_uds/private/uds/channel_manager.h40
-rw-r--r--libs/vr/libpdx_uds/private/uds/client_channel.h85
-rw-r--r--libs/vr/libpdx_uds/private/uds/client_channel_factory.h36
-rw-r--r--libs/vr/libpdx_uds/private/uds/ipc_helper.h214
-rw-r--r--libs/vr/libpdx_uds/private/uds/service_dispatcher.h55
-rw-r--r--libs/vr/libpdx_uds/private/uds/service_endpoint.h167
-rw-r--r--libs/vr/libpdx_uds/remote_method_tests.cpp951
-rw-r--r--libs/vr/libpdx_uds/service_dispatcher.cpp202
-rw-r--r--libs/vr/libpdx_uds/service_endpoint.cpp772
-rw-r--r--libs/vr/libpdx_uds/service_framework_tests.cpp688
-rw-r--r--libs/vr/libperformance/Android.bp41
-rw-r--r--libs/vr/libperformance/include/CPPLINT.cfg1
-rw-r--r--libs/vr/libperformance/include/dvr/performance_client_api.h59
-rw-r--r--libs/vr/libperformance/include/private/dvr/performance_client.h36
-rw-r--r--libs/vr/libperformance/include/private/dvr/performance_rpc.h37
-rw-r--r--libs/vr/libperformance/performance_client.cpp119
-rw-r--r--libs/vr/libperformance/performance_rpc.cpp9
-rw-r--r--libs/vr/libvr_manager/Android.bp36
-rw-r--r--libs/vr/libvr_manager/include/private/dvr/trusted_uids.h33
-rw-r--r--libs/vr/libvr_manager/trusted_uids.cpp51
-rw-r--r--libs/vr/libvr_manager/vr_manager.cpp141
-rw-r--r--libs/vr/libvrflinger/Android.bp78
-rw-r--r--libs/vr/libvrflinger/acquired_buffer.cpp100
-rw-r--r--libs/vr/libvrflinger/acquired_buffer.h82
-rw-r--r--libs/vr/libvrflinger/display_manager_service.cpp155
-rw-r--r--libs/vr/libvrflinger/display_manager_service.h77
-rw-r--r--libs/vr/libvrflinger/display_service.cpp265
-rw-r--r--libs/vr/libvrflinger/display_service.h125
-rw-r--r--libs/vr/libvrflinger/display_surface.cpp457
-rw-r--r--libs/vr/libvrflinger/display_surface.h185
-rw-r--r--libs/vr/libvrflinger/epoll_event_dispatcher.cpp142
-rw-r--r--libs/vr/libvrflinger/epoll_event_dispatcher.h63
-rw-r--r--libs/vr/libvrflinger/hardware_composer.cpp1134
-rw-r--r--libs/vr/libvrflinger/hardware_composer.h457
-rw-r--r--libs/vr/libvrflinger/hwc_types.h300
-rw-r--r--libs/vr/libvrflinger/include/dvr/vr_flinger.h60
-rw-r--r--libs/vr/libvrflinger/vr_flinger.cpp151
-rw-r--r--libs/vr/libvrflinger/vsync_service.cpp213
-rw-r--r--libs/vr/libvrflinger/vsync_service.h106
-rw-r--r--libs/vr/libvrsensor/Android.bp48
-rw-r--r--libs/vr/libvrsensor/include/CPPLINT.cfg1
-rw-r--r--libs/vr/libvrsensor/include/dvr/pose_client.h205
-rw-r--r--libs/vr/libvrsensor/include/private/dvr/latency_model.h29
-rw-r--r--libs/vr/libvrsensor/include/private/dvr/pose-ipc.h28
-rw-r--r--libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h43
-rw-r--r--libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h17
-rw-r--r--libs/vr/libvrsensor/include/private/dvr/sensor_client.h37
-rw-r--r--libs/vr/libvrsensor/include/private/dvr/sensor_constants.h23
-rw-r--r--libs/vr/libvrsensor/latency_model.cpp24
-rw-r--r--libs/vr/libvrsensor/pose_client.cpp338
-rw-r--r--libs/vr/libvrsensor/sensor_client.cpp79
367 files changed, 63836 insertions, 3581 deletions
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
new file mode 100644
index 0000000000..0d25176f98
--- /dev/null
+++ b/libs/arect/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2017 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.
+
+ndk_headers {
+ name: "libarect_headers",
+ from: "include/android",
+ to: "android",
+ srcs: ["include/android/*.h"],
+ license: "NOTICE",
+}
+
+cc_library_static {
+ name: "libarect",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
diff --git a/libs/arect/MODULE_LICENSE_APACHE2 b/libs/arect/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/libs/arect/MODULE_LICENSE_APACHE2
diff --git a/libs/arect/NOTICE b/libs/arect/NOTICE
new file mode 100644
index 0000000000..c5b1efa7aa
--- /dev/null
+++ b/libs/arect/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libs/arect/include/android/rect.h b/libs/arect/include/android/rect.h
new file mode 100644
index 0000000000..80741c0442
--- /dev/null
+++ b/libs/arect/include/android/rect.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file rect.h
+ */
+
+#ifndef ANDROID_RECT_H
+#define ANDROID_RECT_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * {@link ARect} is a struct that represents a rectangular window area.
+ *
+ * It is used with {@link
+ * ANativeActivityCallbacks::onContentRectChanged} event callback and
+ * ANativeWindow_lock() function.
+ */
+typedef struct ARect {
+#ifdef __cplusplus
+ typedef int32_t value_type;
+#endif
+ /** left position */
+ int32_t left;
+ /** top position */
+ int32_t top;
+ /** left position */
+ int32_t right;
+ /** bottom position */
+ int32_t bottom;
+} ARect;
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_RECT_H
+
+/** @} */
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 087718eaa8..66ba7b312b 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -40,6 +40,7 @@ cc_library {
"BpBinder.cpp",
"BufferedTextOutput.cpp",
"Debug.cpp",
+ "IActivityManager.cpp",
"IAppOpsCallback.cpp",
"IAppOpsService.cpp",
"IBatteryStats.cpp",
@@ -51,6 +52,7 @@ cc_library {
"IProcessInfoService.cpp",
"IResultReceiver.cpp",
"IServiceManager.cpp",
+ "IShellCallback.cpp",
"MemoryBase.cpp",
"MemoryDealer.cpp",
"MemoryHeapBase.cpp",
@@ -62,6 +64,8 @@ cc_library {
"Static.cpp",
"Status.cpp",
"TextOutput.cpp",
+ "IpPrefix.cpp",
+ "Value.cpp",
],
cflags: [
@@ -90,6 +94,10 @@ cc_library {
"libbinder_headers",
],
+ export_include_dirs: [
+ "include",
+ ],
+
clang: true,
sanitize: {
misc_undefined: ["integer"],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 7ce2a318a9..890ef30ec7 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -21,6 +21,7 @@
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
#include <binder/IResultReceiver.h>
+#include <binder/IShellCallback.h>
#include <binder/Parcel.h>
#include <stdio.h>
@@ -62,7 +63,8 @@ bool IBinder::checkSubclass(const void* /*subclassID*/) const
status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err,
- Vector<String16>& args, const sp<IResultReceiver>& resultReceiver)
+ Vector<String16>& args, const sp<IShellCallback>& callback,
+ const sp<IResultReceiver>& resultReceiver)
{
Parcel send;
Parcel reply;
@@ -74,6 +76,7 @@ status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int e
for (size_t i = 0; i < numArgs; i++) {
send.writeString16(args[i]);
}
+ send.writeStrongBinder(callback != NULL ? IInterface::asBinder(callback) : NULL);
send.writeStrongBinder(resultReceiver != NULL ? IInterface::asBinder(resultReceiver) : NULL);
return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);
}
@@ -232,6 +235,8 @@ status_t BBinder::onTransact(
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(data.readString16());
}
+ sp<IShellCallback> shellCallback = IShellCallback::asInterface(
+ data.readStrongBinder());
sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(
data.readStrongBinder());
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
new file mode 100644
index 0000000000..50a8b28aae
--- /dev/null
+++ b/libs/binder/IActivityManager.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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 <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IActivityManager.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class BpActivityManager : public BpInterface<IActivityManager>
+{
+public:
+ explicit BpActivityManager(const sp<IBinder>& impl)
+ : BpInterface<IActivityManager>(impl)
+ {
+ }
+
+ virtual int openContentUri(const String16& stringUri)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeString16(stringUri);
+ status_t ret = remote()->transact(OPEN_CONTENT_URI_TRANSACTION, data, & reply);
+ int fd = -1;
+ if (ret == NO_ERROR) {
+ int32_t exceptionCode = reply.readExceptionCode();
+ if (!exceptionCode) {
+ // Success is indicated here by a nonzero int followed by the fd;
+ // failure by a zero int with no data following.
+ if (reply.readInt32() != 0) {
+ fd = fcntl(reply.readParcelFileDescriptor(), F_DUPFD_CLOEXEC, 0);
+ }
+ } else {
+ // An exception was thrown back; fall through to return failure
+ ALOGD("openContentUri(%s) caught exception %d\n",
+ String8(stringUri).string(), exceptionCode);
+ }
+ }
+ return fd;
+ }
+};
+
+// ------------------------------------------------------------------------------------
+
+IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager");
+
+}; // namespace android
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 4800f5ba61..77e3d239bc 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -25,7 +25,7 @@ namespace android {
class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
public:
- BpMediaResourceMonitor(const sp<IBinder>& impl)
+ explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
: BpInterface<IMediaResourceMonitor>(impl) {}
virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 6b5b1afc00..5c1a4f41e0 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "IMemory"
#include <atomic>
+#include <stdatomic.h>
+
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
@@ -28,6 +30,7 @@
#include <binder/IMemory.h>
#include <binder/Parcel.h>
#include <log/log.h>
+
#include <utils/CallStack.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
@@ -287,7 +290,7 @@ void BpMemoryHeap::assertMapped() const
mBase = heap->mBase;
mSize = heap->mSize;
mOffset = heap->mOffset;
- int fd = dup(heap->mHeapId.load(memory_order_relaxed));
+ int fd = fcntl(heap->mHeapId.load(memory_order_relaxed), F_DUPFD_CLOEXEC, 0);
ALOGE_IF(fd==-1, "cannot dup fd=%d",
heap->mHeapId.load(memory_order_relaxed));
mHeapId.store(fd, memory_order_release);
@@ -322,7 +325,7 @@ void BpMemoryHeap::assertReallyMapped() const
Mutex::Autolock _l(mLock);
if (mHeapId.load(memory_order_relaxed) == -1) {
- int fd = dup( parcel_fd );
+ int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0);
ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
parcel_fd, size, err, strerror(errno));
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index d0cd8f2f22..e8329613ab 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -338,6 +338,11 @@ void IPCThreadState::disableBackgroundScheduling(bool disable)
gDisableBackgroundScheduling = disable;
}
+bool IPCThreadState::backgroundSchedulingDisabled()
+{
+ return gDisableBackgroundScheduling;
+}
+
sp<ProcessState> IPCThreadState::process()
{
return mProcess;
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index 2a22b69957..646809e089 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -31,7 +31,7 @@ namespace android {
class BpResultReceiver : public BpInterface<IResultReceiver>
{
public:
- BpResultReceiver(const sp<IBinder>& impl)
+ explicit BpResultReceiver(const sp<IBinder>& impl)
: BpInterface<IResultReceiver>(impl)
{
}
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 3aeff2eda3..c7a0f43a9d 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -23,6 +23,7 @@
#include <binder/Parcel.h>
#include <utils/String8.h>
#include <utils/SystemClock.h>
+#include <utils/CallStack.h>
#include <private/binder/Static.h>
@@ -136,7 +137,12 @@ public:
unsigned n;
for (n = 0; n < 5; n++){
if (n > 0) {
- ALOGI("Waiting for service %s...", String8(name).string());
+ if (!strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder")) {
+ ALOGI("Waiting for vendor service %s...", String8(name).string());
+ CallStack stack(LOG_TAG);
+ } else {
+ ALOGI("Waiting for service %s...", String8(name).string());
+ }
sleep(1);
}
sp<IBinder> svc = checkService(name);
diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp
new file mode 100644
index 0000000000..c793df3266
--- /dev/null
+++ b/libs/binder/IShellCallback.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ShellCallback"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IShellCallback.h>
+
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpShellCallback : public BpInterface<IShellCallback>
+{
+public:
+ explicit BpShellCallback(const sp<IBinder>& impl)
+ : BpInterface<IShellCallback>(impl)
+ {
+ }
+
+ virtual int openOutputFile(const String16& path, const String16& seLinuxContext) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IShellCallback::getInterfaceDescriptor());
+ data.writeString16(path);
+ data.writeString16(seLinuxContext);
+ remote()->transact(OP_OPEN_OUTPUT_FILE, data, &reply, 0);
+ reply.readExceptionCode();
+ int fd = reply.readParcelFileDescriptor();
+ return fd >= 0 ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd;
+
+ }
+};
+
+IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback");
+
+// ----------------------------------------------------------------------
+
+status_t BnShellCallback::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case OP_OPEN_OUTPUT_FILE: {
+ CHECK_INTERFACE(IShellCallback, data, reply);
+ String16 path(data.readString16());
+ String16 seLinuxContext(data.readString16());
+ int fd = openOutputFile(path, seLinuxContext);
+ if (reply != NULL) {
+ reply->writeNoException();
+ if (fd >= 0) {
+ reply->writeInt32(1);
+ reply->writeParcelFileDescriptor(fd, true);
+ } else {
+ reply->writeInt32(0);
+ }
+ } else if (fd >= 0) {
+ close(fd);
+ }
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp
new file mode 100644
index 0000000000..3a8a63c46e
--- /dev/null
+++ b/libs/binder/IpPrefix.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IpPrefix"
+
+#include <binder/IpPrefix.h>
+#include <vector>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+using android::BAD_TYPE;
+using android::BAD_VALUE;
+using android::NO_ERROR;
+using android::Parcel;
+using android::status_t;
+using android::UNEXPECTED_NULL;
+using namespace ::android::binder;
+
+namespace android {
+
+namespace net {
+
+#define RETURN_IF_FAILED(calledOnce) \
+ { \
+ status_t returnStatus = calledOnce; \
+ if (returnStatus) { \
+ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+ return returnStatus; \
+ } \
+ }
+
+status_t IpPrefix::writeToParcel(Parcel* parcel) const {
+ /*
+ * Keep implementation in sync with writeToParcel() in
+ * frameworks/base/core/java/android/net/IpPrefix.java.
+ */
+ std::vector<uint8_t> byte_vector;
+
+ if (mIsIpv6) {
+ const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr);
+ byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
+ } else {
+ const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr);
+ byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
+ }
+
+ RETURN_IF_FAILED(parcel->writeByteVector(byte_vector));
+ RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength)));
+
+ return NO_ERROR;
+}
+
+status_t IpPrefix::readFromParcel(const Parcel* parcel) {
+ /*
+ * Keep implementation in sync with readFromParcel() in
+ * frameworks/base/core/java/android/net/IpPrefix.java.
+ */
+ std::vector<uint8_t> byte_vector;
+
+ RETURN_IF_FAILED(parcel->readByteVector(&byte_vector));
+ RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength));
+
+ if (byte_vector.size() == 16) {
+ mIsIpv6 = true;
+ memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr));
+
+ } else if (byte_vector.size() == 4) {
+ mIsIpv6 = false;
+ memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr));
+
+ } else {
+ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const
+{
+ return mUnion.mIn6Addr;
+}
+
+const struct in_addr& IpPrefix::getAddressAsInAddr() const
+{
+ return mUnion.mInAddr;
+}
+
+bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const
+{
+ if (isIpv6()) {
+ *addr = mUnion.mIn6Addr;
+ return true;
+ }
+ return false;
+}
+
+bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const
+{
+ if (isIpv4()) {
+ *addr = mUnion.mInAddr;
+ return true;
+ }
+ return false;
+}
+
+bool IpPrefix::isIpv6() const
+{
+ return mIsIpv6;
+}
+
+bool IpPrefix::isIpv4() const
+{
+ return !mIsIpv6;
+}
+
+int32_t IpPrefix::getPrefixLength() const
+{
+ return mPrefixLength;
+}
+
+void IpPrefix::setAddress(const struct in6_addr& addr)
+{
+ mUnion.mIn6Addr = addr;
+ mIsIpv6 = true;
+}
+
+void IpPrefix::setAddress(const struct in_addr& addr)
+{
+ mUnion.mInAddr = addr;
+ mIsIpv6 = false;
+}
+
+void IpPrefix::setPrefixLength(int32_t prefix)
+{
+ mPrefixLength = prefix;
+}
+
+bool operator==(const IpPrefix& lhs, const IpPrefix& rhs)
+{
+ if (lhs.mIsIpv6 != rhs.mIsIpv6) {
+ return false;
+ }
+
+ if (lhs.mPrefixLength != rhs.mPrefixLength) {
+ return false;
+ }
+
+ if (lhs.mIsIpv6) {
+ return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr));
+ }
+
+ return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr));
+}
+
+} // namespace net
+
+} // namespace android
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index aed0134327..03f00be6a2 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -26,9 +26,9 @@
#include <unistd.h>
#include <binder/MemoryHeapBase.h>
-#include <log/log.h>
#include <cutils/ashmem.h>
#include <cutils/atomic.h>
+#include <log/log.h>
namespace android {
@@ -82,7 +82,7 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t off
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- mapfd(dup(fd), size, offset);
+ mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
}
status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index d753eb5fa8..aec8f107a3 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -37,6 +37,7 @@
#include <binder/ProcessState.h>
#include <binder/Status.h>
#include <binder/TextOutput.h>
+#include <binder/Value.h>
#include <cutils/ashmem.h>
#include <utils/Debug.h>
@@ -210,7 +211,14 @@ status_t flatten_binder(const sp<ProcessState>& /*proc*/,
{
flat_binder_object obj;
- obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
+ /* minimum priority for all nodes is nice 0 */
+ obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ } else {
+ /* minimum priority for all nodes is MAX_NICE(19) */
+ obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ }
+
if (binder != NULL) {
IBinder *local = binder->localBinder();
if (!local) {
@@ -539,7 +547,7 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
// If this is a file descriptor, we need to dup it so the
// new Parcel now owns its own fd, and can declare that we
// officially know we have fds.
- flat->handle = dup(flat->handle);
+ flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
flat->cookie = 1;
mHasFds = mFdsKnown = true;
if (!mAllowFds) {
@@ -552,6 +560,14 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
return err;
}
+int Parcel::compareData(const Parcel& other) {
+ size_t size = dataSize();
+ if (size != other.dataSize()) {
+ return size < other.dataSize() ? -1 : 1;
+ }
+ return memcmp(data(), other.data(), size);
+}
+
bool Parcel::allowFds() const
{
return mAllowFds;
@@ -1106,6 +1122,10 @@ status_t Parcel::writeParcelable(const Parcelable& parcelable) {
return parcelable.writeToParcel(this);
}
+status_t Parcel::writeValue(const binder::Value& value) {
+ return value.writeToParcel(this);
+}
+
status_t Parcel::writeNativeHandle(const native_handle* handle)
{
if (!handle || handle->version != sizeof(native_handle))
@@ -1142,7 +1162,7 @@ status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
status_t Parcel::writeDupFileDescriptor(int fd)
{
- int dupFd = dup(fd);
+ int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (dupFd < 0) {
return -errno;
}
@@ -1153,6 +1173,12 @@ status_t Parcel::writeDupFileDescriptor(int fd)
return err;
}
+status_t Parcel::writeParcelFileDescriptor(int fd, bool takeOwnership)
+{
+ writeInt32(0);
+ return writeFileDescriptor(fd, takeOwnership);
+}
+
status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) {
return writeDupFileDescriptor(fd.get());
}
@@ -1324,6 +1350,120 @@ status_t Parcel::writeNoException()
return status.writeToParcel(this);
}
+status_t Parcel::writeMap(const ::android::binder::Map& map_in)
+{
+ using ::std::map;
+ using ::android::binder::Value;
+ using ::android::binder::Map;
+
+ Map::const_iterator iter;
+ status_t ret;
+
+ ret = writeInt32(map_in.size());
+
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ for (iter = map_in.begin(); iter != map_in.end(); ++iter) {
+ ret = writeValue(Value(iter->first));
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ ret = writeValue(iter->second);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+status_t Parcel::writeNullableMap(const std::unique_ptr<binder::Map>& map)
+{
+ if (map == NULL) {
+ return writeInt32(-1);
+ }
+
+ return writeMap(*map.get());
+}
+
+status_t Parcel::readMap(::android::binder::Map* map_out)const
+{
+ using ::std::map;
+ using ::android::String16;
+ using ::android::String8;
+ using ::android::binder::Value;
+ using ::android::binder::Map;
+
+ status_t ret = NO_ERROR;
+ int32_t count;
+
+ ret = readInt32(&count);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ if (count < 0) {
+ ALOGE("readMap: Unexpected count: %d", count);
+ return (count == -1)
+ ? UNEXPECTED_NULL
+ : BAD_VALUE;
+ }
+
+ map_out->clear();
+
+ while (count--) {
+ Map::key_type key;
+ Value value;
+
+ ret = readValue(&value);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ if (!value.getString(&key)) {
+ ALOGE("readMap: Key type not a string (parcelType = %d)", value.parcelType());
+ return BAD_VALUE;
+ }
+
+ ret = readValue(&value);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
+ (*map_out)[key] = value;
+ }
+
+ return ret;
+}
+
+status_t Parcel::readNullableMap(std::unique_ptr<binder::Map>* map) const
+{
+ const size_t start = dataPosition();
+ int32_t count;
+ status_t status = readInt32(&count);
+ map->reset();
+
+ if (status != OK || count == -1) {
+ return status;
+ }
+
+ setDataPosition(start);
+ map->reset(new binder::Map());
+
+ status = readMap(map->get());
+
+ if (status != OK) {
+ map->reset();
+ }
+
+ return status;
+}
+
+
+
void Parcel::remove(size_t /*start*/, size_t /*amt*/)
{
LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
@@ -1427,13 +1567,13 @@ status_t readByteVectorInternal(const Parcel* parcel,
return status;
}
- const void* data = parcel->readInplace(size);
+ T* data = const_cast<T*>(reinterpret_cast<const T*>(parcel->readInplace(size)));
if (!data) {
status = BAD_VALUE;
return status;
}
- val->resize(size);
- memcpy(val->data(), data, size);
+ val->reserve(size);
+ val->insert(val->end(), data, data + size);
return status;
}
@@ -1944,6 +2084,10 @@ status_t Parcel::readParcelable(Parcelable* parcelable) const {
return parcelable->readFromParcel(this);
}
+status_t Parcel::readValue(binder::Value* value) const {
+ return value->readFromParcel(this);
+}
+
int32_t Parcel::readExceptionCode() const
{
binder::Status status;
@@ -1966,7 +2110,7 @@ native_handle* Parcel::readNativeHandle() const
}
for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {
- h->data[i] = dup(readFileDescriptor());
+ h->data[i] = fcntl(readFileDescriptor(), F_DUPFD_CLOEXEC, 0);
if (h->data[i] < 0) {
for (int j = 0; j < i; j++) {
close(h->data[j]);
@@ -1984,7 +2128,6 @@ native_handle* Parcel::readNativeHandle() const
return h;
}
-
int Parcel::readFileDescriptor() const
{
const flat_binder_object* flat = readObject(true);
@@ -1996,6 +2139,17 @@ int Parcel::readFileDescriptor() const
return BAD_TYPE;
}
+int Parcel::readParcelFileDescriptor() const
+{
+ int32_t hasComm = readInt32();
+ int fd = readFileDescriptor();
+ if (hasComm != 0) {
+ // skip
+ readFileDescriptor();
+ }
+ return fd;
+}
+
status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const
{
int got = readFileDescriptor();
@@ -2004,7 +2158,7 @@ status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const
return BAD_TYPE;
}
- val->reset(dup(got));
+ val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0));
if (val->get() < 0) {
return BAD_VALUE;
@@ -2078,11 +2232,15 @@ status_t Parcel::read(FlattenableHelperInterface& val) const
status_t err = NO_ERROR;
for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
- fds[i] = dup(this->readFileDescriptor());
- if (fds[i] < 0) {
+ int fd = this->readFileDescriptor();
+ if (fd < 0 || ((fds[i] = fcntl(fd, F_DUPFD_CLOEXEC, 0)) < 0)) {
err = BAD_VALUE;
- ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
- i, fds[i], fd_count, strerror(errno));
+ ALOGE("fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
+ i, fds[i], fd_count, strerror(fd < 0 ? -fd : errno));
+ // Close all the file descriptors that were dup-ed.
+ for (size_t j=0; j<i ;j++) {
+ close(fds[j]);
+ }
}
}
diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp
index e7078baa82..d617b5a179 100644
--- a/libs/binder/PersistableBundle.cpp
+++ b/libs/binder/PersistableBundle.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "PersistableBundle"
#include <binder/PersistableBundle.h>
+#include <private/binder/ParcelValTypes.h>
#include <limits>
@@ -35,27 +36,13 @@ using android::UNEXPECTED_NULL;
using std::map;
using std::set;
using std::vector;
+using namespace ::android::binder;
enum {
// Keep in sync with BUNDLE_MAGIC in frameworks/base/core/java/android/os/BaseBundle.java.
BUNDLE_MAGIC = 0x4C444E42,
};
-enum {
- // Keep in sync with frameworks/base/core/java/android/os/Parcel.java.
- VAL_STRING = 0,
- VAL_INTEGER = 1,
- VAL_LONG = 6,
- VAL_DOUBLE = 8,
- VAL_BOOLEAN = 9,
- VAL_STRINGARRAY = 14,
- VAL_INTARRAY = 18,
- VAL_LONGARRAY = 19,
- VAL_BOOLEANARRAY = 23,
- VAL_PERSISTABLEBUNDLE = 25,
- VAL_DOUBLEARRAY = 28,
-};
-
namespace {
template <typename T>
bool getValue(const android::String16& key, T* out, const map<android::String16, T>& map) {
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index fb2864355d..8939d9c9b2 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -57,6 +57,40 @@ status_t ProcessInfoService::getProcessStatesImpl(size_t length, /*in*/ int32_t*
return TIMED_OUT;
}
+status_t ProcessInfoService::getProcessStatesScoresImpl(size_t length,
+ /*in*/ int32_t* pids, /*out*/ int32_t* states,
+ /*out*/ int32_t *scores) {
+ status_t err = NO_ERROR;
+ sp<IProcessInfoService> pis;
+ mProcessInfoLock.lock();
+ pis = mProcessInfoService;
+ mProcessInfoLock.unlock();
+
+ for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) {
+
+ if (pis != NULL) {
+ err = pis->getProcessStatesAndOomScoresFromPids(length,
+ /*in*/ pids, /*out*/ states, /*out*/ scores);
+ if (err == NO_ERROR) return NO_ERROR; // success
+ if (IInterface::asBinder(pis)->isBinderAlive()) return err;
+ }
+ sleep(1);
+
+ mProcessInfoLock.lock();
+ if (pis == mProcessInfoService) {
+ updateBinderLocked();
+ }
+ pis = mProcessInfoService;
+ mProcessInfoLock.unlock();
+ }
+
+ ALOGW("%s: Could not retrieve process states and scores "
+ "from ProcessInfoService after %d retries.", __FUNCTION__,
+ BINDER_ATTEMPT_LIMIT);
+
+ return TIMED_OUT;
+}
+
void ProcessInfoService::updateBinderLocked() {
const sp<IServiceManager> sm(defaultServiceManager());
if (sm != NULL) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 7fcd8710a3..11dd5258a7 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -71,7 +71,22 @@ sp<ProcessState> ProcessState::self()
if (gProcess != NULL) {
return gProcess;
}
- gProcess = new ProcessState;
+ gProcess = new ProcessState("/dev/binder");
+ return gProcess;
+}
+
+sp<ProcessState> ProcessState::initWithDriver(const char* driver)
+{
+ Mutex::Autolock _l(gProcessMutex);
+ if (gProcess != NULL) {
+ // Allow for initWithDriver to be called repeatedly with the same
+ // driver.
+ if (!strcmp(gProcess->getDriverName().c_str(), driver)) {
+ return gProcess;
+ }
+ LOG_ALWAYS_FATAL("ProcessState was already initialized.");
+ }
+ gProcess = new ProcessState(driver);
return gProcess;
}
@@ -353,9 +368,13 @@ void ProcessState::giveThreadPoolName() {
androidSetThreadName( makeBinderThreadName().string() );
}
-static int open_driver()
+String8 ProcessState::getDriverName() {
+ return mDriverName;
+}
+
+static int open_driver(const char *driver)
{
- int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
+ int fd = open(driver, O_RDWR | O_CLOEXEC);
if (fd >= 0) {
int vers = 0;
status_t result = ioctl(fd, BINDER_VERSION, &vers);
@@ -365,7 +384,8 @@ static int open_driver()
fd = -1;
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
- ALOGE("Binder driver protocol does not match user space protocol!");
+ ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
+ vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
close(fd);
fd = -1;
}
@@ -375,13 +395,14 @@ static int open_driver()
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
} else {
- ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
+ ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
}
return fd;
}
-ProcessState::ProcessState()
- : mDriverFD(open_driver())
+ProcessState::ProcessState(const char *driver)
+ : mDriverName(String8(driver))
+ , mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
@@ -402,6 +423,7 @@ ProcessState::ProcessState()
ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
+ mDriverName.clear();
}
}
diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp
new file mode 100644
index 0000000000..fd1dfd5ada
--- /dev/null
+++ b/libs/binder/Value.cpp
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Value"
+
+#include <binder/Value.h>
+
+#include <limits>
+
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <binder/Map.h>
+#include <private/binder/ParcelValTypes.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+using android::BAD_TYPE;
+using android::BAD_VALUE;
+using android::NO_ERROR;
+using android::UNEXPECTED_NULL;
+using android::Parcel;
+using android::sp;
+using android::status_t;
+using std::map;
+using std::set;
+using std::vector;
+using android::binder::Value;
+using android::IBinder;
+using android::os::PersistableBundle;
+using namespace android::binder;
+
+// ====================================================================
+
+#define RETURN_IF_FAILED(calledOnce) \
+ do { \
+ status_t returnStatus = calledOnce; \
+ if (returnStatus) { \
+ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+ return returnStatus; \
+ } \
+ } while(false)
+
+// ====================================================================
+
+/* These `internal_type_ptr()` functions allow this
+ * class to work without C++ RTTI support. This technique
+ * only works properly when called directly from this file,
+ * but that is OK because that is the only place we will
+ * be calling them from. */
+template<class T> const void* internal_type_ptr()
+{
+ static const T *marker;
+ return (void*)&marker;
+}
+
+/* Allows the type to be specified by the argument
+ * instead of inside angle brackets. */
+template<class T> const void* internal_type_ptr(const T&)
+{
+ return internal_type_ptr<T>();
+}
+
+// ====================================================================
+
+namespace android {
+
+namespace binder {
+
+class Value::ContentBase {
+public:
+ virtual ~ContentBase() = default;
+ virtual const void* type_ptr() const = 0;
+ virtual ContentBase * clone() const = 0;
+ virtual bool operator==(const ContentBase& rhs) const = 0;
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+ virtual const std::type_info &type() const = 0;
+#endif
+
+ template<typename T> bool get(T* out) const;
+};
+
+/* This is the actual class that holds the value. */
+template<typename T> class Value::Content : public Value::ContentBase {
+public:
+ Content() = default;
+ Content(const T & value) : mValue(value) { }
+
+ virtual ~Content() = default;
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+ virtual const std::type_info &type() const override
+ {
+ return typeid(T);
+ }
+#endif
+
+ virtual const void* type_ptr() const override
+ {
+ return internal_type_ptr<T>();
+ }
+
+ virtual ContentBase * clone() const override
+ {
+ return new Content(mValue);
+ };
+
+ virtual bool operator==(const ContentBase& rhs) const override
+ {
+ if (type_ptr() != rhs.type_ptr()) {
+ return false;
+ }
+ return mValue == static_cast<const Content<T>* >(&rhs)->mValue;
+ }
+
+ T mValue;
+};
+
+template<typename T> bool Value::ContentBase::get(T* out) const
+{
+ if (internal_type_ptr(*out) != type_ptr())
+ {
+ return false;
+ }
+
+ *out = static_cast<const Content<T>*>(this)->mValue;
+
+ return true;
+}
+
+// ====================================================================
+
+Value::Value() : mContent(NULL)
+{
+}
+
+Value::Value(const Value& value)
+ : mContent(value.mContent ? value.mContent->clone() : NULL)
+{
+}
+
+Value::~Value()
+{
+ delete mContent;
+}
+
+bool Value::operator==(const Value& rhs) const
+{
+ const Value& lhs(*this);
+
+ if (lhs.empty() && rhs.empty()) {
+ return true;
+ }
+
+ if ( (lhs.mContent == NULL)
+ || (rhs.mContent == NULL)
+ ) {
+ return false;
+ }
+
+ return *lhs.mContent == *rhs.mContent;
+}
+
+Value& Value::swap(Value &rhs)
+{
+ std::swap(mContent, rhs.mContent);
+ return *this;
+}
+
+Value& Value::operator=(const Value& rhs)
+{
+ delete mContent;
+ mContent = rhs.mContent
+ ? rhs.mContent->clone()
+ : NULL;
+ return *this;
+}
+
+bool Value::empty() const
+{
+ return mContent == NULL;
+}
+
+void Value::clear()
+{
+ delete mContent;
+ mContent = NULL;
+}
+
+int32_t Value::parcelType() const
+{
+ const void* t_info(mContent ? mContent->type_ptr() : NULL);
+
+ if (t_info == internal_type_ptr<bool>()) return VAL_BOOLEAN;
+ if (t_info == internal_type_ptr<uint8_t>()) return VAL_BYTE;
+ if (t_info == internal_type_ptr<int32_t>()) return VAL_INTEGER;
+ if (t_info == internal_type_ptr<int64_t>()) return VAL_LONG;
+ if (t_info == internal_type_ptr<double>()) return VAL_DOUBLE;
+ if (t_info == internal_type_ptr<String16>()) return VAL_STRING;
+
+ if (t_info == internal_type_ptr<vector<bool>>()) return VAL_BOOLEANARRAY;
+ if (t_info == internal_type_ptr<vector<uint8_t>>()) return VAL_BYTEARRAY;
+ if (t_info == internal_type_ptr<vector<int32_t>>()) return VAL_INTARRAY;
+ if (t_info == internal_type_ptr<vector<int64_t>>()) return VAL_LONGARRAY;
+ if (t_info == internal_type_ptr<vector<double>>()) return VAL_DOUBLEARRAY;
+ if (t_info == internal_type_ptr<vector<String16>>()) return VAL_STRINGARRAY;
+
+ if (t_info == internal_type_ptr<Map>()) return VAL_MAP;
+ if (t_info == internal_type_ptr<PersistableBundle>()) return VAL_PERSISTABLEBUNDLE;
+
+ return VAL_NULL;
+}
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+const std::type_info& Value::type() const
+{
+ return mContent != NULL
+ ? mContent->type()
+ : typeid(void);
+}
+#endif // ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+
+#define DEF_TYPE_ACCESSORS(T, TYPENAME) \
+ bool Value::is ## TYPENAME() const \
+ { \
+ return mContent \
+ ? internal_type_ptr<T>() == mContent->type_ptr() \
+ : false; \
+ } \
+ bool Value::get ## TYPENAME(T* out) const \
+ { \
+ return mContent \
+ ? mContent->get(out) \
+ : false; \
+ } \
+ void Value::put ## TYPENAME(const T& in) \
+ { \
+ *this = in; \
+ } \
+ Value& Value::operator=(const T& rhs) \
+ { \
+ delete mContent; \
+ mContent = new Content< T >(rhs); \
+ return *this; \
+ } \
+ Value::Value(const T& value) \
+ : mContent(new Content< T >(value)) \
+ { }
+
+DEF_TYPE_ACCESSORS(bool, Boolean)
+DEF_TYPE_ACCESSORS(int8_t, Byte)
+DEF_TYPE_ACCESSORS(int32_t, Int)
+DEF_TYPE_ACCESSORS(int64_t, Long)
+DEF_TYPE_ACCESSORS(double, Double)
+DEF_TYPE_ACCESSORS(String16, String)
+
+DEF_TYPE_ACCESSORS(std::vector<bool>, BooleanVector)
+DEF_TYPE_ACCESSORS(std::vector<uint8_t>, ByteVector)
+DEF_TYPE_ACCESSORS(std::vector<int32_t>, IntVector)
+DEF_TYPE_ACCESSORS(std::vector<int64_t>, LongVector)
+DEF_TYPE_ACCESSORS(std::vector<double>, DoubleVector)
+DEF_TYPE_ACCESSORS(std::vector<String16>, StringVector)
+
+DEF_TYPE_ACCESSORS(::android::binder::Map, Map)
+DEF_TYPE_ACCESSORS(PersistableBundle, PersistableBundle)
+
+bool Value::getString(String8* out) const
+{
+ String16 val;
+ bool ret = getString(&val);
+ if (ret) {
+ *out = String8(val);
+ }
+ return ret;
+}
+
+bool Value::getString(::std::string* out) const
+{
+ String8 val;
+ bool ret = getString(&val);
+ if (ret) {
+ *out = val.string();
+ }
+ return ret;
+}
+
+status_t Value::writeToParcel(Parcel* parcel) const
+{
+ // This implementation needs to be kept in sync with the writeValue
+ // implementation in frameworks/base/core/java/android/os/Parcel.java
+
+#define BEGIN_HANDLE_WRITE() \
+ do { \
+ const void* t_info(mContent?mContent->type_ptr():NULL); \
+ if (false) { }
+#define HANDLE_WRITE_TYPE(T, TYPEVAL, TYPEMETHOD) \
+ else if (t_info == internal_type_ptr<T>()) { \
+ RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \
+ RETURN_IF_FAILED(parcel->TYPEMETHOD(static_cast<const Content<T>*>(mContent)->mValue)); \
+ }
+#define HANDLE_WRITE_PARCELABLE(T, TYPEVAL) \
+ else if (t_info == internal_type_ptr<T>()) { \
+ RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \
+ RETURN_IF_FAILED(static_cast<const Content<T>*>(mContent)->mValue.writeToParcel(parcel)); \
+ }
+#define END_HANDLE_WRITE() \
+ else { \
+ ALOGE("writeToParcel: Type not supported"); \
+ return BAD_TYPE; \
+ } \
+ } while (false);
+
+ BEGIN_HANDLE_WRITE()
+
+ HANDLE_WRITE_TYPE(bool, VAL_BOOLEAN, writeBool)
+ HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte)
+ HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte)
+ HANDLE_WRITE_TYPE(int32_t, VAL_INTEGER, writeInt32)
+ HANDLE_WRITE_TYPE(int64_t, VAL_LONG, writeInt64)
+ HANDLE_WRITE_TYPE(double, VAL_DOUBLE, writeDouble)
+ HANDLE_WRITE_TYPE(String16, VAL_STRING, writeString16)
+
+ HANDLE_WRITE_TYPE(vector<bool>, VAL_BOOLEANARRAY, writeBoolVector)
+ HANDLE_WRITE_TYPE(vector<uint8_t>, VAL_BYTEARRAY, writeByteVector)
+ HANDLE_WRITE_TYPE(vector<int8_t>, VAL_BYTEARRAY, writeByteVector)
+ HANDLE_WRITE_TYPE(vector<int32_t>, VAL_INTARRAY, writeInt32Vector)
+ HANDLE_WRITE_TYPE(vector<int64_t>, VAL_LONGARRAY, writeInt64Vector)
+ HANDLE_WRITE_TYPE(vector<double>, VAL_DOUBLEARRAY, writeDoubleVector)
+ HANDLE_WRITE_TYPE(vector<String16>, VAL_STRINGARRAY, writeString16Vector)
+
+ HANDLE_WRITE_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE)
+
+ END_HANDLE_WRITE()
+
+ return NO_ERROR;
+
+#undef BEGIN_HANDLE_WRITE
+#undef HANDLE_WRITE_TYPE
+#undef HANDLE_WRITE_PARCELABLE
+#undef END_HANDLE_WRITE
+}
+
+status_t Value::readFromParcel(const Parcel* parcel)
+{
+ // This implementation needs to be kept in sync with the readValue
+ // implementation in frameworks/base/core/java/android/os/Parcel.javai
+
+#define BEGIN_HANDLE_READ() \
+ switch(value_type) { \
+ default: \
+ ALOGE("readFromParcel: Parcel type %d is not supported", value_type); \
+ return BAD_TYPE;
+#define HANDLE_READ_TYPE(T, TYPEVAL, TYPEMETHOD) \
+ case TYPEVAL: \
+ mContent = new Content<T>(); \
+ RETURN_IF_FAILED(parcel->TYPEMETHOD(&static_cast<Content<T>*>(mContent)->mValue)); \
+ break;
+#define HANDLE_READ_PARCELABLE(T, TYPEVAL) \
+ case TYPEVAL: \
+ mContent = new Content<T>(); \
+ RETURN_IF_FAILED(static_cast<Content<T>*>(mContent)->mValue.readFromParcel(parcel)); \
+ break;
+#define END_HANDLE_READ() \
+ }
+
+ int32_t value_type = VAL_NULL;
+
+ delete mContent;
+ mContent = NULL;
+
+ RETURN_IF_FAILED(parcel->readInt32(&value_type));
+
+ BEGIN_HANDLE_READ()
+
+ HANDLE_READ_TYPE(bool, VAL_BOOLEAN, readBool)
+ HANDLE_READ_TYPE(int8_t, VAL_BYTE, readByte)
+ HANDLE_READ_TYPE(int32_t, VAL_INTEGER, readInt32)
+ HANDLE_READ_TYPE(int64_t, VAL_LONG, readInt64)
+ HANDLE_READ_TYPE(double, VAL_DOUBLE, readDouble)
+ HANDLE_READ_TYPE(String16, VAL_STRING, readString16)
+
+ HANDLE_READ_TYPE(vector<bool>, VAL_BOOLEANARRAY, readBoolVector)
+ HANDLE_READ_TYPE(vector<uint8_t>, VAL_BYTEARRAY, readByteVector)
+ HANDLE_READ_TYPE(vector<int32_t>, VAL_INTARRAY, readInt32Vector)
+ HANDLE_READ_TYPE(vector<int64_t>, VAL_LONGARRAY, readInt64Vector)
+ HANDLE_READ_TYPE(vector<double>, VAL_DOUBLEARRAY, readDoubleVector)
+ HANDLE_READ_TYPE(vector<String16>, VAL_STRINGARRAY, readString16Vector)
+
+ HANDLE_READ_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE)
+
+ END_HANDLE_READ()
+
+ return NO_ERROR;
+
+#undef BEGIN_HANDLE_READ
+#undef HANDLE_READ_TYPE
+#undef HANDLE_READ_PARCELABLE
+#undef END_HANDLE_READ
+}
+
+} // namespace binder
+
+} // namespace android
+
+/* vim: set ts=4 sw=4 tw=0 et :*/
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 042927c176..4212776e89 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -91,7 +91,8 @@ public:
OP_USE_SIP = 53,
OP_PROCESS_OUTGOING_CALLS = 54,
OP_USE_FINGERPRINT = 55,
- OP_BODY_SENSORS = 56
+ OP_BODY_SENSORS = 56,
+ OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
};
AppOpsManager();
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
new file mode 100644
index 0000000000..5ad218035a
--- /dev/null
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_IACTIVITY_MANAGER_H
+#define ANDROID_IACTIVITY_MANAGER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class IActivityManager : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ActivityManager)
+
+ virtual int openContentUri(const String16& /* stringUri */) = 0;
+
+ enum {
+ OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ------------------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IACTIVITY_MANAGER_H \ No newline at end of file
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 9097cb3bb9..2e6295787d 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -38,6 +38,7 @@ class BpBinder;
class IInterface;
class Parcel;
class IResultReceiver;
+class IShellCallback;
/**
* Base class and low-level protocol for a remotable object.
@@ -82,7 +83,7 @@ public:
virtual status_t pingBinder() = 0;
virtual status_t dump(int fd, const Vector<String16>& args) = 0;
static status_t shellCommand(const sp<IBinder>& target, int in, int out, int err,
- Vector<String16>& args,
+ Vector<String16>& args, const sp<IShellCallback>& callback,
const sp<IResultReceiver>& resultReceiver);
virtual status_t transact( uint32_t code,
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index be72d44759..0f1fe5b670 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -72,24 +72,24 @@ protected:
// ----------------------------------------------------------------------
#define DECLARE_META_INTERFACE(INTERFACE) \
- static const android::String16 descriptor; \
- static android::sp<I##INTERFACE> asInterface( \
- const android::sp<android::IBinder>& obj); \
- virtual const android::String16& getInterfaceDescriptor() const; \
+ static const ::android::String16 descriptor; \
+ static ::android::sp<I##INTERFACE> asInterface( \
+ const ::android::sp<::android::IBinder>& obj); \
+ virtual const ::android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE(); \
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
- const android::String16 I##INTERFACE::descriptor(NAME); \
- const android::String16& \
+ const ::android::String16 I##INTERFACE::descriptor(NAME); \
+ const ::android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
- android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
- const android::sp<android::IBinder>& obj) \
+ ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
+ const ::android::sp<::android::IBinder>& obj) \
{ \
- android::sp<I##INTERFACE> intr; \
+ ::android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 7b826d6dcd..245607e74e 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -83,6 +83,7 @@ public:
// in to it but doesn't want to acquire locks in its services while in
// the background.
static void disableBackgroundScheduling(bool disable);
+ bool backgroundSchedulingDisabled();
// Call blocks until the number of executing binder threads is less than
// the maximum number of binder threads threads allowed for this process.
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h
new file mode 100644
index 0000000000..fda9ee6ba7
--- /dev/null
+++ b/libs/binder/include/binder/IShellCallback.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_ISHELL_CALLBACK_H
+#define ANDROID_ISHELL_CALLBACK_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IShellCallback : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ShellCallback);
+
+ virtual int openOutputFile(const String16& path, const String16& seLinuxContext) = 0;
+
+ enum {
+ OP_OPEN_OUTPUT_FILE = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnShellCallback : public BnInterface<IShellCallback>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_ISHELL_CALLBACK_H
+
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
new file mode 100644
index 0000000000..96ebaac437
--- /dev/null
+++ b/libs/binder/include/binder/IpPrefix.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_IP_PREFIX_H
+#define ANDROID_IP_PREFIX_H
+
+#include <netinet/in.h>
+
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace net {
+
+/*
+ * C++ implementation of the Java class android.net.IpPrefix
+ */
+class IpPrefix : public Parcelable {
+public:
+ IpPrefix() = default;
+ virtual ~IpPrefix() = default;
+ IpPrefix(const IpPrefix& prefix) = default;
+
+ IpPrefix(const struct in6_addr& addr, int32_t plen):
+ mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { }
+
+ IpPrefix(const struct in_addr& addr, int32_t plen):
+ mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { }
+
+ bool getAddressAsIn6Addr(struct in6_addr* addr) const;
+ bool getAddressAsInAddr(struct in_addr* addr) const;
+
+ const struct in6_addr& getAddressAsIn6Addr() const;
+ const struct in_addr& getAddressAsInAddr() const;
+
+ bool isIpv6() const;
+ bool isIpv4() const;
+
+ int32_t getPrefixLength() const;
+
+ void setAddress(const struct in6_addr& addr);
+ void setAddress(const struct in_addr& addr);
+
+ void setPrefixLength(int32_t prefix);
+
+ friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs);
+
+ friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) {
+ return !(lhs == rhs);
+ }
+
+public:
+ // Overrides
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+private:
+ union InternalUnion {
+ InternalUnion() = default;
+ InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { };
+ InternalUnion(const struct in_addr &addr):mInAddr(addr) { };
+ struct in6_addr mIn6Addr;
+ struct in_addr mInAddr;
+ } mUnion;
+ int32_t mPrefixLength;
+ bool mIsIpv6;
+};
+
+} // namespace net
+
+} // namespace android
+
+#endif // ANDROID_IP_PREFIX_H
diff --git a/libs/binder/include/binder/Map.h b/libs/binder/include/binder/Map.h
new file mode 100644
index 0000000000..96a4f8a2a5
--- /dev/null
+++ b/libs/binder/include/binder/Map.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005 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 ANDROID_MAP_H
+#define ANDROID_MAP_H
+
+#include <map>
+#include <string>
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace binder {
+
+class Value;
+
+/**
+ * Convenience typedef for ::std::map<::std::string,::android::binder::Value>
+ */
+typedef ::std::map<::std::string, Value> Map;
+
+} // namespace binder
+} // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_MAP_H
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b0d53ef5c8..5d36526cb3 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -31,6 +31,7 @@
#include <binder/IInterface.h>
#include <binder/Parcelable.h>
+#include <binder/Map.h>
// ---------------------------------------------------------------------------
namespace android {
@@ -43,6 +44,10 @@ class ProcessState;
class String8;
class TextOutput;
+namespace binder {
+class Value;
+};
+
class Parcel {
friend class IPCThreadState;
public:
@@ -67,6 +72,8 @@ public:
status_t appendFrom(const Parcel *parcel,
size_t start, size_t len);
+ int compareData(const Parcel& other);
+
bool allowFds() const;
bool pushAllowFds(bool allowFds);
void restoreAllowFds(bool lastValue);
@@ -153,6 +160,8 @@ public:
template<typename T>
status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
template<typename T>
+ status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
+ template<typename T>
status_t writeParcelableVector(const std::vector<T>& val);
template<typename T>
@@ -160,6 +169,8 @@ public:
status_t writeParcelable(const Parcelable& parcelable);
+ status_t writeValue(const binder::Value& value);
+
template<typename T>
status_t write(const Flattenable<T>& val);
@@ -171,21 +182,29 @@ public:
template<typename T>
status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val);
+ status_t writeMap(const binder::Map& map);
+ status_t writeNullableMap(const std::unique_ptr<binder::Map>& map);
+
// Place a native_handle into the parcel (the native_handle's file-
// descriptors are dup'ed, so it is safe to delete the native_handle
// when this function returns).
// Doesn't take ownership of the native_handle.
status_t writeNativeHandle(const native_handle* handle);
-
+
// Place a file descriptor into the parcel. The given fd must remain
// valid for the lifetime of the parcel.
// The Parcel does not take ownership of the given fd unless you ask it to.
status_t writeFileDescriptor(int fd, bool takeOwnership = false);
-
+
// Place a file descriptor into the parcel. A dup of the fd is made, which
// will be closed once the parcel is destroyed.
status_t writeDupFileDescriptor(int fd);
+ // Place a Java "parcel file descriptor" into the parcel. The given fd must remain
+ // valid for the lifetime of the parcel.
+ // The Parcel does not take ownership of the given fd unless you ask it to.
+ status_t writeParcelFileDescriptor(int fd, bool takeOwnership = false);
+
// Place a file descriptor into the parcel. This will not affect the
// semantics of the smart file descriptor. A new descriptor will be
// created, and will be closed when the parcel is destroyed.
@@ -271,6 +290,8 @@ public:
template<typename T>
status_t readParcelable(std::unique_ptr<T>* parcelable) const;
+ status_t readValue(binder::Value* value) const;
+
template<typename T>
status_t readStrongBinder(sp<T>* val) const;
@@ -314,6 +335,9 @@ public:
template<typename T>
status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
+ status_t readMap(binder::Map* map)const;
+ status_t readNullableMap(std::unique_ptr<binder::Map>* map) const;
+
// Like Parcel.java's readExceptionCode(). Reads the first int32
// off of a Parcel's header, returning 0 or the negative error
// code on exceptions, but also deals with skipping over rich
@@ -332,6 +356,10 @@ public:
// in the parcel, which you do not own -- use dup() to get your own copy.
int readFileDescriptor() const;
+ // Retrieve a Java "parcel file descriptor" from the parcel. This returns the raw fd
+ // in the parcel, which you do not own -- use dup() to get your own copy.
+ int readParcelFileDescriptor() const;
+
// Retrieve a smart file descriptor from the parcel.
status_t readUniqueFileDescriptor(
base::unique_fd* val) const;
@@ -468,6 +496,8 @@ private:
#pragma clang diagnostic ignored "-Wweak-vtables"
#endif
+ // FlattenableHelperInterface and FlattenableHelper avoid generating a vtable entry in objects
+ // following Flattenable template/protocol.
class FlattenableHelperInterface {
protected:
~FlattenableHelperInterface() { }
@@ -482,6 +512,9 @@ private:
#pragma clang diagnostic pop
#endif
+ // Concrete implementation of FlattenableHelperInterface that delegates virtual calls to the
+ // specified class T implementing the Flattenable protocol. It "virtualizes" a compile-time
+ // protocol.
template<typename T>
class FlattenableHelper : public FlattenableHelperInterface {
friend class Parcel;
@@ -855,7 +888,16 @@ status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::un
return this->writeInt32(-1);
}
- return unsafeWriteTypedVector(*val, &Parcel::writeParcelable);
+ return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
+}
+
+template<typename T>
+status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) {
+ if (val.get() == nullptr) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
}
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include/binder/ProcessInfoService.h
index c5ead20676..0da61ee3cb 100644
--- a/libs/binder/include/binder/ProcessInfoService.h
+++ b/libs/binder/include/binder/ProcessInfoService.h
@@ -35,6 +35,8 @@ class ProcessInfoService : public Singleton<ProcessInfoService> {
ProcessInfoService();
status_t getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states);
+ status_t getProcessStatesScoresImpl(size_t length, /*in*/ int32_t* pids,
+ /*out*/ int32_t* states, /*out*/ int32_t *scores);
void updateBinderLocked();
static const int BINDER_ATTEMPT_LIMIT = 5;
@@ -55,6 +57,21 @@ public:
/*out*/ states);
}
+ /**
+ * For each PID in the given "pids" input array, write the current process state
+ * for that process into the "states" output array, or
+ * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID
+ * exists. OoM scores will also be written in the "scores" output array.
+ * Please also note that clients calling this method need to have
+ * "GET_PROCESS_STATE_AND_OOM_SCORE" permission.
+ *
+ * Returns NO_ERROR if this operation was successful, or a negative error code otherwise.
+ */
+ static status_t getProcessStatesScoresFromPids(size_t length, /*in*/ int32_t* pids,
+ /*out*/ int32_t* states, /*out*/ int32_t *scores) {
+ return ProcessInfoService::getInstance().getProcessStatesScoresImpl(
+ length, /*in*/ pids, /*out*/ states, /*out*/ scores);
+ }
};
// ----------------------------------------------------------------------
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index ae47bbf69d..f85c2612d4 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -36,6 +36,11 @@ class ProcessState : public virtual RefBase
public:
static sp<ProcessState> self();
static sp<ProcessState> selfOrNull();
+ /* initWithDriver() can be used to configure libbinder to use
+ * a different binder driver dev node. It must be called *before*
+ * any call to ProcessState::self(). /dev/binder remains the default.
+ */
+ static sp<ProcessState> initWithDriver(const char *driver);
void setContextObject(const sp<IBinder>& object);
sp<IBinder> getContextObject(const sp<IBinder>& caller);
@@ -65,12 +70,14 @@ public:
status_t setThreadPoolMaxThreadCount(size_t maxThreads);
void giveThreadPoolName();
+ String8 getDriverName();
+
ssize_t getKernelReferences(size_t count, uintptr_t* buf);
private:
friend class IPCThreadState;
- ProcessState();
+ ProcessState(const char* driver);
~ProcessState();
ProcessState(const ProcessState& o);
@@ -84,6 +91,7 @@ private:
handle_entry* lookupHandleLocked(int32_t handle);
+ String8 mDriverName;
int mDriverFD;
void* mVMStart;
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
new file mode 100644
index 0000000000..3bfd462807
--- /dev/null
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -0,0 +1,705 @@
+/*
+ * Copyright 2016 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 <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <cutils/compiler.h>
+
+// Set to 1 to enable CallStacks when logging errors
+#define SI_DUMP_CALLSTACKS 0
+#if SI_DUMP_CALLSTACKS
+#include <utils/CallStack.h>
+#endif
+
+#include <utils/NativeHandle.h>
+
+#include <functional>
+#include <type_traits>
+
+namespace android {
+namespace SafeInterface {
+
+// ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way
+class ParcelHandler {
+public:
+ explicit ParcelHandler(const char* logTag) : mLogTag(logTag) {}
+
+ // Specializations for types with dedicated handling in Parcel
+ status_t read(const Parcel& parcel, bool* b) const {
+ return callParcel("readBool", [&]() { return parcel.readBool(b); });
+ }
+ status_t write(Parcel* parcel, bool b) const {
+ return callParcel("writeBool", [&]() { return parcel->writeBool(b); });
+ }
+ template <typename E>
+ typename std::enable_if<std::is_enum<E>::value, status_t>::type read(const Parcel& parcel,
+ E* e) const {
+ typename std::underlying_type<E>::type u{};
+ status_t result = read(parcel, &u);
+ *e = static_cast<E>(u);
+ return result;
+ }
+ template <typename E>
+ typename std::enable_if<std::is_enum<E>::value, status_t>::type write(Parcel* parcel,
+ E e) const {
+ return write(parcel, static_cast<typename std::underlying_type<E>::type>(e));
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
+ const Parcel& parcel, T* t) const {
+ return callParcel("read(Flattenable)", [&]() { return parcel.read(*t); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write(
+ Parcel* parcel, const T& t) const {
+ return callParcel("write(Flattenable)", [&]() { return parcel->write(t); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
+ const Parcel& parcel, sp<T>* t) const {
+ *t = new T{};
+ return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write(
+ Parcel* parcel, const sp<T>& t) const {
+ return callParcel("write(sp<Flattenable>)", [&]() { return parcel->write(*(t.get())); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type read(
+ const Parcel& parcel, T* t) const {
+ return callParcel("read(LightFlattenable)", [&]() { return parcel.read(*t); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type write(
+ Parcel* parcel, const T& t) const {
+ return callParcel("write(LightFlattenable)", [&]() { return parcel->write(t); });
+ }
+ template <typename NH>
+ typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type read(
+ const Parcel& parcel, NH* nh) {
+ *nh = NativeHandle::create(parcel.readNativeHandle(), true);
+ return NO_ERROR;
+ }
+ template <typename NH>
+ typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type write(
+ Parcel* parcel, const NH& nh) {
+ return callParcel("write(sp<NativeHandle>)",
+ [&]() { return parcel->writeNativeHandle(nh->handle()); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read(
+ const Parcel& parcel, T* t) const {
+ return callParcel("readParcelable", [&]() { return parcel.readParcelable(t); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write(
+ Parcel* parcel, const T& t) const {
+ return callParcel("writeParcelable", [&]() { return parcel->writeParcelable(t); });
+ }
+ status_t read(const Parcel& parcel, String8* str) const {
+ return callParcel("readString8", [&]() { return parcel.readString8(str); });
+ }
+ status_t write(Parcel* parcel, const String8& str) const {
+ return callParcel("writeString8", [&]() { return parcel->writeString8(str); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type read(
+ const Parcel& parcel, sp<T>* pointer) const {
+ return callParcel("readNullableStrongBinder",
+ [&]() { return parcel.readNullableStrongBinder(pointer); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type write(
+ Parcel* parcel, const sp<T>& pointer) const {
+ return callParcel("writeStrongBinder",
+ [&]() { return parcel->writeStrongBinder(pointer); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type read(
+ const Parcel& parcel, sp<T>* pointer) const {
+ return callParcel("readNullableStrongBinder[IInterface]",
+ [&]() { return parcel.readNullableStrongBinder(pointer); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type write(
+ Parcel* parcel, const sp<T>& interface) const {
+ return write(parcel, IInterface::asBinder(interface));
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read(
+ const Parcel& parcel, std::vector<T>* v) const {
+ return callParcel("readParcelableVector", [&]() { return parcel.readParcelableVector(v); });
+ }
+ template <typename T>
+ typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write(
+ Parcel* parcel, const std::vector<T>& v) const {
+ return callParcel("writeParcelableVector",
+ [&]() { return parcel->writeParcelableVector(v); });
+ }
+
+ // Templates to handle integral types. We use a struct template to require that the called
+ // function exactly matches the signedness and size of the argument (e.g., the argument isn't
+ // silently widened).
+ template <bool isSigned, size_t size, typename I>
+ struct HandleInt;
+ template <typename I>
+ struct HandleInt<true, 4, I> {
+ static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+ return handler.callParcel("readInt32", [&]() { return parcel.readInt32(i); });
+ }
+ static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+ return handler.callParcel("writeInt32", [&]() { return parcel->writeInt32(i); });
+ }
+ };
+ template <typename I>
+ struct HandleInt<false, 4, I> {
+ static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+ return handler.callParcel("readUint32", [&]() { return parcel.readUint32(i); });
+ }
+ static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+ return handler.callParcel("writeUint32", [&]() { return parcel->writeUint32(i); });
+ }
+ };
+ template <typename I>
+ struct HandleInt<true, 8, I> {
+ static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+ return handler.callParcel("readInt64", [&]() { return parcel.readInt64(i); });
+ }
+ static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+ return handler.callParcel("writeInt64", [&]() { return parcel->writeInt64(i); });
+ }
+ };
+ template <typename I>
+ struct HandleInt<false, 8, I> {
+ static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
+ return handler.callParcel("readUint64", [&]() { return parcel.readUint64(i); });
+ }
+ static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
+ return handler.callParcel("writeUint64", [&]() { return parcel->writeUint64(i); });
+ }
+ };
+ template <typename I>
+ typename std::enable_if<std::is_integral<I>::value, status_t>::type read(const Parcel& parcel,
+ I* i) const {
+ return HandleInt<std::is_signed<I>::value, sizeof(I), I>::read(*this, parcel, i);
+ }
+ template <typename I>
+ typename std::enable_if<std::is_integral<I>::value, status_t>::type write(Parcel* parcel,
+ I i) const {
+ return HandleInt<std::is_signed<I>::value, sizeof(I), I>::write(*this, parcel, i);
+ }
+
+private:
+ const char* const mLogTag;
+
+ // Helper to encapsulate error handling while calling the various Parcel methods
+ template <typename Function>
+ status_t callParcel(const char* name, Function f) const {
+ status_t error = f();
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to %s, (%d: %s)", name, error, strerror(-error));
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ }
+ return error;
+ }
+};
+
+// Utility struct template which allows us to retrieve the types of the parameters of a member
+// function pointer
+template <typename T>
+struct ParamExtractor;
+template <typename Class, typename Return, typename... Params>
+struct ParamExtractor<Return (Class::*)(Params...)> {
+ using ParamTuple = std::tuple<Params...>;
+};
+template <typename Class, typename Return, typename... Params>
+struct ParamExtractor<Return (Class::*)(Params...) const> {
+ using ParamTuple = std::tuple<Params...>;
+};
+
+} // namespace SafeInterface
+
+template <typename Interface>
+class SafeBpInterface : public BpInterface<Interface> {
+protected:
+ SafeBpInterface(const sp<IBinder>& impl, const char* logTag)
+ : BpInterface<Interface>(impl), mLogTag(logTag) {}
+ ~SafeBpInterface() override = default;
+
+ // callRemote is used to invoke a synchronous procedure call over Binder
+ template <typename Method, typename TagType, typename... Args>
+ status_t callRemote(TagType tag, Args&&... args) const {
+ static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t");
+
+ // Verify that the arguments are compatible with the parameters
+ using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+ static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value,
+ "Invalid argument type");
+
+ // Write the input arguments to the data Parcel
+ Parcel data;
+ data.writeInterfaceToken(this->getInterfaceDescriptor());
+
+ status_t error = writeInputs(&data, std::forward<Args>(args)...);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by writeInputs
+ return error;
+ }
+
+ // Send the data Parcel to the remote and retrieve the reply parcel
+ Parcel reply;
+ error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ return error;
+ }
+
+ // Read the outputs from the reply Parcel into the output arguments
+ error = readOutputs(reply, std::forward<Args>(args)...);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by readOutputs
+ return error;
+ }
+
+ // Retrieve the result code from the reply Parcel
+ status_t result = NO_ERROR;
+ error = reply.readInt32(&result);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to obtain result");
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ return error;
+ }
+ return result;
+ }
+
+ // callRemoteAsync is used to invoke an asynchronous procedure call over Binder
+ template <typename Method, typename TagType, typename... Args>
+ void callRemoteAsync(TagType tag, Args&&... args) const {
+ static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t");
+
+ // Verify that the arguments are compatible with the parameters
+ using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+ static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value,
+ "Invalid argument type");
+
+ // Write the input arguments to the data Parcel
+ Parcel data;
+ data.writeInterfaceToken(this->getInterfaceDescriptor());
+ status_t error = writeInputs(&data, std::forward<Args>(args)...);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by writeInputs
+ return;
+ }
+
+ // There will be no data in the reply Parcel since the call is one-way
+ Parcel reply;
+ error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply,
+ IBinder::FLAG_ONEWAY);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ }
+ }
+
+private:
+ const char* const mLogTag;
+
+ // This struct provides information on whether the decayed types of the elements at Index in the
+ // tuple types T and U (that is, the types after stripping cv-qualifiers, removing references,
+ // and a few other less common operations) are the same
+ template <size_t Index, typename T, typename U>
+ struct DecayedElementsMatch {
+ private:
+ using FirstT = typename std::tuple_element<Index, T>::type;
+ using DecayedT = typename std::decay<FirstT>::type;
+ using FirstU = typename std::tuple_element<Index, U>::type;
+ using DecayedU = typename std::decay<FirstU>::type;
+
+ public:
+ static constexpr bool value = std::is_same<DecayedT, DecayedU>::value;
+ };
+
+ // When comparing whether the argument types match the parameter types, we first decay them (see
+ // DecayedElementsMatch) to avoid falsely flagging, say, T&& against T even though they are
+ // equivalent enough for our purposes
+ template <typename T, typename U>
+ struct ArgsMatchParams {};
+ template <typename... Args, typename... Params>
+ struct ArgsMatchParams<std::tuple<Args...>, std::tuple<Params...>> {
+ static_assert(sizeof...(Args) <= sizeof...(Params), "Too many arguments");
+ static_assert(sizeof...(Args) >= sizeof...(Params), "Not enough arguments");
+
+ private:
+ template <size_t Index>
+ static constexpr typename std::enable_if<(Index < sizeof...(Args)), bool>::type
+ elementsMatch() {
+ if (!DecayedElementsMatch<Index, std::tuple<Args...>, std::tuple<Params...>>::value) {
+ return false;
+ }
+ return elementsMatch<Index + 1>();
+ }
+ template <size_t Index>
+ static constexpr typename std::enable_if<(Index >= sizeof...(Args)), bool>::type
+ elementsMatch() {
+ return true;
+ }
+
+ public:
+ static constexpr bool value = elementsMatch<0>();
+ };
+
+ // Since we assume that pointer arguments are outputs, we can use this template struct to
+ // determine whether or not a given argument is fundamentally a pointer type and thus an output
+ template <typename T>
+ struct IsPointerIfDecayed {
+ private:
+ using Decayed = typename std::decay<T>::type;
+
+ public:
+ static constexpr bool value = std::is_pointer<Decayed>::value;
+ };
+
+ template <typename T>
+ typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type writeIfInput(
+ Parcel* data, T&& t) const {
+ return SafeInterface::ParcelHandler{mLogTag}.write(data, std::forward<T>(t));
+ }
+ template <typename T>
+ typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type writeIfInput(
+ Parcel* /*data*/, T&& /*t*/) const {
+ return NO_ERROR;
+ }
+
+ // This method iterates through all of the arguments, writing them to the data Parcel if they
+ // are an input (i.e., if they are not a pointer type)
+ template <typename T, typename... Remaining>
+ status_t writeInputs(Parcel* data, T&& t, Remaining&&... remaining) const {
+ status_t error = writeIfInput(data, std::forward<T>(t));
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by writeIfInput
+ return error;
+ }
+ return writeInputs(data, std::forward<Remaining>(remaining)...);
+ }
+ static status_t writeInputs(Parcel* /*data*/) { return NO_ERROR; }
+
+ template <typename T>
+ typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type readIfOutput(
+ const Parcel& reply, T&& t) const {
+ return SafeInterface::ParcelHandler{mLogTag}.read(reply, std::forward<T>(t));
+ }
+ template <typename T>
+ static typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type readIfOutput(
+ const Parcel& /*reply*/, T&& /*t*/) {
+ return NO_ERROR;
+ }
+
+ // Similar to writeInputs except that it reads output arguments from the reply Parcel
+ template <typename T, typename... Remaining>
+ status_t readOutputs(const Parcel& reply, T&& t, Remaining&&... remaining) const {
+ status_t error = readIfOutput(reply, std::forward<T>(t));
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by readIfOutput
+ return error;
+ }
+ return readOutputs(reply, std::forward<Remaining>(remaining)...);
+ }
+ static status_t readOutputs(const Parcel& /*data*/) { return NO_ERROR; }
+};
+
+template <typename Interface>
+class SafeBnInterface : public BnInterface<Interface> {
+public:
+ explicit SafeBnInterface(const char* logTag) : mLogTag(logTag) {}
+
+protected:
+ template <typename Method>
+ status_t callLocal(const Parcel& data, Parcel* reply, Method method) {
+ CHECK_INTERFACE(this, data, reply);
+
+ // Since we need to both pass inputs into the call as well as retrieve outputs, we create a
+ // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the
+ // outputs. When we ultimately call into the method, we will pass the addresses of the
+ // output arguments instead of their tuple members directly, but the storage will live in
+ // the tuple.
+ using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+ typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{};
+
+ // Read the inputs from the data Parcel into the argument tuple
+ status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by read
+ return error;
+ }
+
+ // Call the local method
+ status_t result = MethodCaller<ParamTuple>::call(this, method, &rawArgs);
+
+ // Extract the outputs from the argument tuple and write them into the reply Parcel
+ error = OutputWriter<ParamTuple>{mLogTag}.writeOutputs(reply, &rawArgs);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by write
+ return error;
+ }
+
+ // Return the result code in the reply Parcel
+ error = reply->writeInt32(result);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ ALOG(LOG_ERROR, mLogTag, "Failed to write result");
+#if SI_DUMP_CALLSTACKS
+ CallStack callStack(mLogTag);
+#endif
+ return error;
+ }
+ return NO_ERROR;
+ }
+
+ template <typename Method>
+ status_t callLocalAsync(const Parcel& data, Parcel* /*reply*/, Method method) {
+ // reply is not actually used by CHECK_INTERFACE
+ CHECK_INTERFACE(this, data, reply);
+
+ // Since we need to both pass inputs into the call as well as retrieve outputs, we create a
+ // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the
+ // outputs. When we ultimately call into the method, we will pass the addresses of the
+ // output arguments instead of their tuple members directly, but the storage will live in
+ // the tuple.
+ using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
+ typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{};
+
+ // Read the inputs from the data Parcel into the argument tuple
+ status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged by read
+ return error;
+ }
+
+ // Call the local method
+ MethodCaller<ParamTuple>::callVoid(this, method, &rawArgs);
+
+ // After calling, there is nothing more to do since asynchronous calls do not return a value
+ // to the caller
+ return NO_ERROR;
+ }
+
+private:
+ const char* const mLogTag;
+
+ // RemoveFirst strips the first element from a tuple.
+ // For example, given T = std::tuple<A, B, C>, RemoveFirst<T>::type = std::tuple<B, C>
+ template <typename T, typename... Args>
+ struct RemoveFirst;
+ template <typename T, typename... Args>
+ struct RemoveFirst<std::tuple<T, Args...>> {
+ using type = std::tuple<Args...>;
+ };
+
+ // RawConverter strips a tuple down to its fundamental types, discarding both pointers and
+ // references. This allows us to allocate storage for both input (non-pointer) arguments and
+ // output (pointer) arguments in one tuple.
+ // For example, given T = std::tuple<const A&, B*>, RawConverter<T>::type = std::tuple<A, B>
+ template <typename Unconverted, typename... Converted>
+ struct RawConverter;
+ template <typename Unconverted, typename... Converted>
+ struct RawConverter<std::tuple<Converted...>, Unconverted> {
+ private:
+ using ElementType = typename std::tuple_element<0, Unconverted>::type;
+ using Decayed = typename std::decay<ElementType>::type;
+ using WithoutPointer = typename std::remove_pointer<Decayed>::type;
+
+ public:
+ using type = typename RawConverter<std::tuple<Converted..., WithoutPointer>,
+ typename RemoveFirst<Unconverted>::type>::type;
+ };
+ template <typename... Converted>
+ struct RawConverter<std::tuple<Converted...>, std::tuple<>> {
+ using type = std::tuple<Converted...>;
+ };
+
+ // This provides a simple way to determine whether the indexed element of Args... is a pointer
+ template <size_t I, typename... Args>
+ struct ElementIsPointer {
+ private:
+ using ElementType = typename std::tuple_element<I, std::tuple<Args...>>::type;
+
+ public:
+ static constexpr bool value = std::is_pointer<ElementType>::value;
+ };
+
+ // This class iterates over the parameter types, and if a given parameter is an input
+ // (i.e., is not a pointer), reads the corresponding argument tuple element from the data Parcel
+ template <typename... Params>
+ class InputReader;
+ template <typename... Params>
+ class InputReader<std::tuple<Params...>> {
+ public:
+ explicit InputReader(const char* logTag) : mLogTag(logTag) {}
+
+ // Note that in this case (as opposed to in SafeBpInterface), we iterate using an explicit
+ // index (starting with 0 here) instead of using recursion and stripping the first element.
+ // This is because in SafeBpInterface we aren't actually operating on a real tuple, but are
+ // instead just using a tuple as a convenient container for variadic types, whereas here we
+ // can't modify the argument tuple without causing unnecessary copies or moves of the data
+ // contained therein.
+ template <typename RawTuple>
+ status_t readInputs(const Parcel& data, RawTuple* args) {
+ return dispatchArg<0>(data, args);
+ }
+
+ private:
+ const char* const mLogTag;
+
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type readIfInput(
+ const Parcel& data, RawTuple* args) {
+ return SafeInterface::ParcelHandler{mLogTag}.read(data, &std::get<I>(*args));
+ }
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type readIfInput(
+ const Parcel& /*data*/, RawTuple* /*args*/) {
+ return NO_ERROR;
+ }
+
+ // Recursively iterate through the arguments
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
+ const Parcel& data, RawTuple* args) {
+ status_t error = readIfInput<I>(data, args);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged in read
+ return error;
+ }
+ return dispatchArg<I + 1>(data, args);
+ }
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg(
+ const Parcel& /*data*/, RawTuple* /*args*/) {
+ return NO_ERROR;
+ }
+ };
+
+ // getForCall uses the types of the parameters to determine whether a given element of the
+ // argument tuple is an input, which should be passed directly into the call, or an output, for
+ // which its address should be passed into the call
+ template <size_t I, typename RawTuple, typename... Params>
+ static typename std::enable_if<
+ ElementIsPointer<I, Params...>::value,
+ typename std::tuple_element<I, std::tuple<Params...>>::type>::type
+ getForCall(RawTuple* args) {
+ return &std::get<I>(*args);
+ }
+ template <size_t I, typename RawTuple, typename... Params>
+ static typename std::enable_if<
+ !ElementIsPointer<I, Params...>::value,
+ typename std::tuple_element<I, std::tuple<Params...>>::type>::type&
+ getForCall(RawTuple* args) {
+ return std::get<I>(*args);
+ }
+
+ // This template class uses std::index_sequence and parameter pack expansion to call the given
+ // method using the elements of the argument tuple (after those arguments are passed through
+ // getForCall to get addresses instead of values for output arguments)
+ template <typename... Params>
+ struct MethodCaller;
+ template <typename... Params>
+ struct MethodCaller<std::tuple<Params...>> {
+ public:
+ // The calls through these to the helper methods are necessary to generate the
+ // std::index_sequences used to unpack the argument tuple into the method call
+ template <typename Class, typename MemberFunction, typename RawTuple>
+ static status_t call(Class* instance, MemberFunction function, RawTuple* args) {
+ return callHelper(instance, function, args, std::index_sequence_for<Params...>{});
+ }
+ template <typename Class, typename MemberFunction, typename RawTuple>
+ static void callVoid(Class* instance, MemberFunction function, RawTuple* args) {
+ callVoidHelper(instance, function, args, std::index_sequence_for<Params...>{});
+ }
+
+ private:
+ template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I>
+ static status_t callHelper(Class* instance, MemberFunction function, RawTuple* args,
+ std::index_sequence<I...> /*unused*/) {
+ return (instance->*function)(getForCall<I, RawTuple, Params...>(args)...);
+ }
+ template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I>
+ static void callVoidHelper(Class* instance, MemberFunction function, RawTuple* args,
+ std::index_sequence<I...> /*unused*/) {
+ (instance->*function)(getForCall<I, RawTuple, Params...>(args)...);
+ }
+ };
+
+ // This class iterates over the parameter types, and if a given parameter is an output
+ // (i.e., is a pointer), writes the corresponding argument tuple element into the reply Parcel
+ template <typename... Params>
+ struct OutputWriter;
+ template <typename... Params>
+ struct OutputWriter<std::tuple<Params...>> {
+ public:
+ explicit OutputWriter(const char* logTag) : mLogTag(logTag) {}
+
+ // See the note on InputReader::readInputs for why this differs from the arguably simpler
+ // RemoveFirst approach in SafeBpInterface
+ template <typename RawTuple>
+ status_t writeOutputs(Parcel* reply, RawTuple* args) {
+ return dispatchArg<0>(reply, args);
+ }
+
+ private:
+ const char* const mLogTag;
+
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type
+ writeIfOutput(Parcel* reply, RawTuple* args) {
+ return SafeInterface::ParcelHandler{mLogTag}.write(reply, std::get<I>(*args));
+ }
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type
+ writeIfOutput(Parcel* /*reply*/, RawTuple* /*args*/) {
+ return NO_ERROR;
+ }
+
+ // Recursively iterate through the arguments
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
+ Parcel* reply, RawTuple* args) {
+ status_t error = writeIfOutput<I>(reply, args);
+ if (CC_UNLIKELY(error != NO_ERROR)) {
+ // A message will have been logged in read
+ return error;
+ }
+ return dispatchArg<I + 1>(reply, args);
+ }
+ template <std::size_t I, typename RawTuple>
+ typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg(
+ Parcel* /*reply*/, RawTuple* /*args*/) {
+ return NO_ERROR;
+ }
+ };
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/Value.h b/libs/binder/include/binder/Value.h
new file mode 100644
index 0000000000..4dee3d86b0
--- /dev/null
+++ b/libs/binder/include/binder/Value.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015 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 ANDROID_VALUE_H
+#define ANDROID_VALUE_H
+
+#include <stdint.h>
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <binder/Map.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Parcel;
+
+namespace binder {
+
+/**
+ * A limited C++ generic type. The purpose of this class is to allow C++
+ * programs to make use of (or implement) Binder interfaces which make use
+ * the Java "Object" generic type (either via the use of the Map type or
+ * some other mechanism).
+ *
+ * This class only supports a limited set of types, but additional types
+ * may be easily added to this class in the future as needed---without
+ * breaking binary compatability.
+ *
+ * This class was written in such a way as to help avoid type errors by
+ * giving each type their own explicity-named accessor methods (rather than
+ * overloaded methods).
+ *
+ * When reading or writing this class to a Parcel, use the `writeValue()`
+ * and `readValue()` methods.
+ */
+class Value {
+public:
+ Value();
+ virtual ~Value();
+
+ Value& swap(Value &);
+
+ bool empty() const;
+
+ void clear();
+
+#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO
+ const std::type_info& type() const;
+#endif
+
+ int32_t parcelType() const;
+
+ bool operator==(const Value& rhs) const;
+ bool operator!=(const Value& rhs) const { return !this->operator==(rhs); }
+
+ Value(const Value& value);
+ Value(const bool& value);
+ Value(const int8_t& value);
+ Value(const int32_t& value);
+ Value(const int64_t& value);
+ Value(const double& value);
+ Value(const String16& value);
+ Value(const std::vector<bool>& value);
+ Value(const std::vector<uint8_t>& value);
+ Value(const std::vector<int32_t>& value);
+ Value(const std::vector<int64_t>& value);
+ Value(const std::vector<double>& value);
+ Value(const std::vector<String16>& value);
+ Value(const os::PersistableBundle& value);
+ Value(const binder::Map& value);
+
+ Value& operator=(const Value& rhs);
+ Value& operator=(const int8_t& rhs);
+ Value& operator=(const bool& rhs);
+ Value& operator=(const int32_t& rhs);
+ Value& operator=(const int64_t& rhs);
+ Value& operator=(const double& rhs);
+ Value& operator=(const String16& rhs);
+ Value& operator=(const std::vector<bool>& rhs);
+ Value& operator=(const std::vector<uint8_t>& rhs);
+ Value& operator=(const std::vector<int32_t>& rhs);
+ Value& operator=(const std::vector<int64_t>& rhs);
+ Value& operator=(const std::vector<double>& rhs);
+ Value& operator=(const std::vector<String16>& rhs);
+ Value& operator=(const os::PersistableBundle& rhs);
+ Value& operator=(const binder::Map& rhs);
+
+ void putBoolean(const bool& value);
+ void putByte(const int8_t& value);
+ void putInt(const int32_t& value);
+ void putLong(const int64_t& value);
+ void putDouble(const double& value);
+ void putString(const String16& value);
+ void putBooleanVector(const std::vector<bool>& value);
+ void putByteVector(const std::vector<uint8_t>& value);
+ void putIntVector(const std::vector<int32_t>& value);
+ void putLongVector(const std::vector<int64_t>& value);
+ void putDoubleVector(const std::vector<double>& value);
+ void putStringVector(const std::vector<String16>& value);
+ void putPersistableBundle(const os::PersistableBundle& value);
+ void putMap(const binder::Map& value);
+
+ bool getBoolean(bool* out) const;
+ bool getByte(int8_t* out) const;
+ bool getInt(int32_t* out) const;
+ bool getLong(int64_t* out) const;
+ bool getDouble(double* out) const;
+ bool getString(String16* out) const;
+ bool getBooleanVector(std::vector<bool>* out) const;
+ bool getByteVector(std::vector<uint8_t>* out) const;
+ bool getIntVector(std::vector<int32_t>* out) const;
+ bool getLongVector(std::vector<int64_t>* out) const;
+ bool getDoubleVector(std::vector<double>* out) const;
+ bool getStringVector(std::vector<String16>* out) const;
+ bool getPersistableBundle(os::PersistableBundle* out) const;
+ bool getMap(binder::Map* out) const;
+
+ bool isBoolean() const;
+ bool isByte() const;
+ bool isInt() const;
+ bool isLong() const;
+ bool isDouble() const;
+ bool isString() const;
+ bool isBooleanVector() const;
+ bool isByteVector() const;
+ bool isIntVector() const;
+ bool isLongVector() const;
+ bool isDoubleVector() const;
+ bool isStringVector() const;
+ bool isPersistableBundle() const;
+ bool isMap() const;
+
+ // String Convenience Adapters
+ // ---------------------------
+
+ Value(const String8& value): Value(String16(value)) { }
+ Value(const ::std::string& value): Value(String8(value.c_str())) { }
+ void putString(const String8& value) { return putString(String16(value)); }
+ void putString(const ::std::string& value) { return putString(String8(value.c_str())); }
+ Value& operator=(const String8& rhs) { return *this = String16(rhs); }
+ Value& operator=(const ::std::string& rhs) { return *this = String8(rhs.c_str()); }
+ bool getString(String8* out) const;
+ bool getString(::std::string* out) const;
+
+private:
+
+ // This allows ::android::Parcel to call the two methods below.
+ friend class ::android::Parcel;
+
+ // This is called by ::android::Parcel::writeValue()
+ status_t writeToParcel(Parcel* parcel) const;
+
+ // This is called by ::android::Parcel::readValue()
+ status_t readFromParcel(const Parcel* parcel);
+
+ template<typename T> class Content;
+ class ContentBase;
+
+ ContentBase* mContent;
+};
+
+} // namespace binder
+
+} // namespace android
+
+#endif // ANDROID_VALUE_H
diff --git a/libs/binder/include/private/binder/ParcelValTypes.h b/libs/binder/include/private/binder/ParcelValTypes.h
new file mode 100644
index 0000000000..666d22a57f
--- /dev/null
+++ b/libs/binder/include/private/binder/ParcelValTypes.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+namespace android {
+namespace binder {
+
+// Keep in sync with frameworks/base/core/java/android/os/Parcel.java.
+enum {
+ VAL_NULL = -1,
+ VAL_STRING = 0,
+ VAL_INTEGER = 1,
+ VAL_MAP = 2,
+ VAL_BUNDLE = 3,
+ VAL_PARCELABLE = 4,
+ VAL_SHORT = 5,
+ VAL_LONG = 6,
+ VAL_DOUBLE = 8,
+ VAL_BOOLEAN = 9,
+ VAL_BYTEARRAY = 13,
+ VAL_STRINGARRAY = 14,
+ VAL_IBINDER = 15,
+ VAL_INTARRAY = 18,
+ VAL_LONGARRAY = 19,
+ VAL_BYTE = 20,
+ VAL_SERIALIZABLE = 21,
+ VAL_BOOLEANARRAY = 23,
+ VAL_PERSISTABLEBUNDLE = 25,
+ VAL_DOUBLEARRAY = 28,
+};
+
+} // namespace binder
+} // namespace android
diff --git a/libs/binder/include/private/binder/Static.h b/libs/binder/include/private/binder/Static.h
index d104646804..3d10456a8d 100644
--- a/libs/binder/include/private/binder/Static.h
+++ b/libs/binder/include/private/binder/Static.h
@@ -20,7 +20,6 @@
#include <utils/threads.h>
#include <binder/IBinder.h>
-#include <binder/IMemory.h>
#include <binder/ProcessState.h>
#include <binder/IPermissionController.h>
#include <binder/IServiceManager.h>
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 4a841499ff..bef9505821 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -33,6 +33,15 @@ cc_test {
}
cc_test {
+ name: "binderValueTypeTest",
+ srcs: ["binderValueTypeTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+}
+
+cc_test {
name: "binderLibTest",
srcs: ["binderLibTest.cpp"],
shared_libs: [
@@ -78,3 +87,28 @@ cc_test {
"libbase",
],
}
+
+cc_test {
+ name: "binderSafeInterfaceTest",
+ srcs: ["binderSafeInterfaceTest.cpp"],
+
+ cppflags: [
+ "-Werror",
+ "-Weverything",
+ "-Wno-c++98-compat",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-global-constructors",
+ "-Wno-padded",
+ "-Wno-weak-vtables",
+ ],
+
+ cpp_std: "experimental",
+ gnu_extensions: false,
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 54e12b6f37..757291cd2a 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -45,6 +45,7 @@ enum BinderLibTestTranscationCode {
BINDER_LIB_TEST_ADD_SERVER,
BINDER_LIB_TEST_CALL_BACK,
BINDER_LIB_TEST_NOP_CALL_BACK,
+ BINDER_LIB_TEST_GET_SELF_TRANSACTION,
BINDER_LIB_TEST_GET_ID_TRANSACTION,
BINDER_LIB_TEST_INDIRECT_TRANSACTION,
BINDER_LIB_TEST_SET_ERROR_TRANSACTION,
@@ -56,6 +57,7 @@ enum BinderLibTestTranscationCode {
BINDER_LIB_TEST_EXIT_TRANSACTION,
BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
+ BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
};
pid_t start_server_process(int arg2)
@@ -389,7 +391,7 @@ TEST_F(BinderLibTest, IndirectGetId2)
ret = reply.readInt32(&count);
ASSERT_EQ(NO_ERROR, ret);
- EXPECT_EQ(ARRAY_SIZE(serverId), count);
+ EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
BinderLibTestBundle replyi(&reply);
@@ -439,7 +441,7 @@ TEST_F(BinderLibTest, IndirectGetId3)
ret = reply.readInt32(&count);
ASSERT_EQ(NO_ERROR, ret);
- EXPECT_EQ(ARRAY_SIZE(serverId), count);
+ EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
for (size_t i = 0; i < (size_t)count; i++) {
int32_t counti;
@@ -631,7 +633,7 @@ TEST_F(BinderLibTest, PassFile) {
}
ret = read(pipefd[0], buf, sizeof(buf));
- EXPECT_EQ(sizeof(buf), ret);
+ EXPECT_EQ(sizeof(buf), (size_t)ret);
EXPECT_EQ(write_value, buf[0]);
waitForReadData(pipefd[0], 5000); /* wait for other proccess to close pipe */
@@ -670,6 +672,62 @@ TEST_F(BinderLibTest, PromoteRemote) {
EXPECT_GE(ret, 0);
}
+TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
+ status_t ret;
+ Parcel data, reply;
+
+ ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply);
+ EXPECT_EQ(NO_ERROR, ret);
+
+ const flat_binder_object *fb = reply.readObject(false);
+ ASSERT_TRUE(fb != NULL);
+ EXPECT_EQ(fb->type, BINDER_TYPE_HANDLE);
+ EXPECT_EQ(ProcessState::self()->getStrongProxyForHandle(fb->handle), m_server);
+ EXPECT_EQ(fb->cookie, (binder_uintptr_t)0);
+ EXPECT_EQ(fb->binder >> 32, (binder_uintptr_t)0);
+}
+
+TEST_F(BinderLibTest, FreedBinder) {
+ status_t ret;
+
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != NULL);
+
+ __u32 freedHandle;
+ wp<IBinder> keepFreedBinder;
+ {
+ Parcel data, reply;
+ data.writeBool(false); /* request weak reference */
+ ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply);
+ ASSERT_EQ(NO_ERROR, ret);
+ struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data());
+ freedHandle = freed->handle;
+ /* Add a weak ref to the freed binder so the driver does not
+ * delete its reference to it - otherwise the transaction
+ * fails regardless of whether the driver is fixed.
+ */
+ keepFreedBinder = reply.readWeakBinder();
+ }
+ {
+ Parcel data, reply;
+ data.writeStrongBinder(server);
+ /* Replace original handle with handle to the freed binder */
+ struct flat_binder_object *strong = (struct flat_binder_object *)(data.data());
+ __u32 oldHandle = strong->handle;
+ strong->handle = freedHandle;
+ ret = server->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply);
+ /* Returns DEAD_OBJECT (-32) if target crashes and
+ * FAILED_TRANSACTION if the driver rejects the invalid
+ * object.
+ */
+ EXPECT_EQ((status_t)FAILED_TRANSACTION, ret);
+ /* Restore original handle so parcel destructor does not use
+ * the wrong handle.
+ */
+ strong->handle = oldHandle;
+ }
+}
+
class BinderLibTestService : public BBinder
{
public:
@@ -771,6 +829,9 @@ class BinderLibTestService : public BBinder
binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_SELF_TRANSACTION:
+ reply->writeStrongBinder(this);
+ return NO_ERROR;
case BINDER_LIB_TEST_GET_ID_TRANSACTION:
reply->writeInt32(m_id);
return NO_ERROR;
@@ -884,6 +945,16 @@ class BinderLibTestService : public BBinder
while (wait(NULL) != -1 || errno != ECHILD)
;
exit(EXIT_SUCCESS);
+ case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: {
+ bool strongRef = data.readBool();
+ sp<IBinder> binder = new BBinder();
+ if (strongRef) {
+ reply->writeStrongBinder(binder);
+ } else {
+ reply->writeWeakBinder(binder);
+ }
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
};
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
new file mode 100644
index 0000000000..6a16e2496d
--- /dev/null
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -0,0 +1,819 @@
+/*
+ * Copyright 2016 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 <binder/SafeInterface.h>
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/ProcessState.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#include <gtest/gtest.h>
+#pragma clang diagnostic pop
+
+#include <utils/LightRefBase.h>
+#include <utils/NativeHandle.h>
+
+#include <cutils/native_handle.h>
+
+#include <optional>
+
+#include <sys/eventfd.h>
+
+using namespace std::chrono_literals; // NOLINT - google-build-using-namespace
+
+namespace android {
+namespace tests {
+
+enum class TestEnum : uint32_t {
+ INVALID = 0,
+ INITIAL = 1,
+ FINAL = 2,
+};
+
+// This class serves two purposes:
+// 1) It ensures that the implementation doesn't require copying or moving the data (for
+// efficiency purposes)
+// 2) It tests that Parcelables can be passed correctly
+class NoCopyNoMove : public Parcelable {
+public:
+ NoCopyNoMove() = default;
+ explicit NoCopyNoMove(int32_t value) : mValue(value) {}
+ ~NoCopyNoMove() override = default;
+
+ // Not copyable
+ NoCopyNoMove(const NoCopyNoMove&) = delete;
+ NoCopyNoMove& operator=(const NoCopyNoMove&) = delete;
+
+ // Not movable
+ NoCopyNoMove(NoCopyNoMove&&) = delete;
+ NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;
+
+ // Parcelable interface
+ status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); }
+ status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); }
+
+ int32_t getValue() const { return mValue; }
+ void setValue(int32_t value) { mValue = value; }
+
+private:
+ int32_t mValue = 0;
+ uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded
+};
+
+struct TestFlattenable : Flattenable<TestFlattenable> {
+ TestFlattenable() = default;
+ explicit TestFlattenable(int32_t v) : value(v) {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const { return sizeof(value); }
+ size_t getFdCount() const { return 0; }
+ status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const {
+ FlattenableUtils::write(buffer, size, value);
+ return NO_ERROR;
+ }
+ status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+ FlattenableUtils::read(buffer, size, value);
+ return NO_ERROR;
+ }
+
+ int32_t value = 0;
+};
+
+struct TestLightFlattenable : LightFlattenablePod<TestLightFlattenable> {
+ TestLightFlattenable() = default;
+ explicit TestLightFlattenable(int32_t v) : value(v) {}
+ int32_t value = 0;
+};
+
+// It seems like this should be able to inherit from TestFlattenable (to avoid duplicating code),
+// but the SafeInterface logic can't easily be extended to find an indirect Flattenable<T>
+// base class
+class TestLightRefBaseFlattenable : public Flattenable<TestLightRefBaseFlattenable>,
+ public LightRefBase<TestLightRefBaseFlattenable> {
+public:
+ TestLightRefBaseFlattenable() = default;
+ explicit TestLightRefBaseFlattenable(int32_t v) : value(v) {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const { return sizeof(value); }
+ size_t getFdCount() const { return 0; }
+ status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const {
+ FlattenableUtils::write(buffer, size, value);
+ return NO_ERROR;
+ }
+ status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+ FlattenableUtils::read(buffer, size, value);
+ return NO_ERROR;
+ }
+
+ int32_t value = 0;
+};
+
+class TestParcelable : public Parcelable {
+public:
+ TestParcelable() = default;
+ explicit TestParcelable(int32_t value) : mValue(value) {}
+ TestParcelable(const TestParcelable& other) : TestParcelable(other.mValue) {}
+ TestParcelable(TestParcelable&& other) : TestParcelable(other.mValue) {}
+
+ // Parcelable interface
+ status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); }
+ status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); }
+
+ int32_t getValue() const { return mValue; }
+ void setValue(int32_t value) { mValue = value; }
+
+private:
+ int32_t mValue = 0;
+};
+
+class ExitOnDeath : public IBinder::DeathRecipient {
+public:
+ ~ExitOnDeath() override = default;
+
+ void binderDied(const wp<IBinder>& /*who*/) override {
+ ALOG(LOG_INFO, "ExitOnDeath", "Exiting");
+ exit(0);
+ }
+};
+
+// This callback class is used to test both one-way transactions and that sp<IInterface> can be
+// passed correctly
+class ICallback : public IInterface {
+public:
+ DECLARE_META_INTERFACE(Callback)
+
+ enum class Tag : uint32_t {
+ OnCallback = IBinder::FIRST_CALL_TRANSACTION,
+ Last,
+ };
+
+ virtual void onCallback(int32_t aPlusOne) = 0;
+};
+
+class BpCallback : public SafeBpInterface<ICallback> {
+public:
+ explicit BpCallback(const sp<IBinder>& impl) : SafeBpInterface<ICallback>(impl, getLogTag()) {}
+
+ void onCallback(int32_t aPlusOne) override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemoteAsync<decltype(&ICallback::onCallback)>(Tag::OnCallback, aPlusOne);
+ }
+
+private:
+ static constexpr const char* getLogTag() { return "BpCallback"; }
+};
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback");
+#pragma clang diagnostic pop
+
+class BnCallback : public SafeBnInterface<ICallback> {
+public:
+ BnCallback() : SafeBnInterface("BnCallback") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t /*flags*/) override {
+ EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+ EXPECT_LT(code, static_cast<uint32_t>(ICallback::Tag::Last));
+ ICallback::Tag tag = static_cast<ICallback::Tag>(code);
+ switch (tag) {
+ case ICallback::Tag::OnCallback: {
+ return callLocalAsync(data, reply, &ICallback::onCallback);
+ }
+ case ICallback::Tag::Last:
+ // Should not be possible because of the asserts at the beginning of the method
+ [&]() { FAIL(); }();
+ return UNKNOWN_ERROR;
+ }
+ }
+};
+
+class ISafeInterfaceTest : public IInterface {
+public:
+ DECLARE_META_INTERFACE(SafeInterfaceTest)
+
+ enum class Tag : uint32_t {
+ SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
+ ReturnsNoMemory,
+ LogicalNot,
+ ModifyEnum,
+ IncrementFlattenable,
+ IncrementLightFlattenable,
+ IncrementLightRefBaseFlattenable,
+ IncrementNativeHandle,
+ IncrementNoCopyNoMove,
+ IncrementParcelableVector,
+ ToUpper,
+ CallMeBack,
+ IncrementInt32,
+ IncrementUint32,
+ IncrementInt64,
+ IncrementUint64,
+ IncrementTwo,
+ Last,
+ };
+
+ // This is primarily so that the remote service dies when the test does, but it also serves to
+ // test the handling of sp<IBinder> and non-const methods
+ virtual status_t setDeathToken(const sp<IBinder>& token) = 0;
+
+ // This is the most basic test since it doesn't require parceling any arguments
+ virtual status_t returnsNoMemory() const = 0;
+
+ // These are ordered according to their corresponding methods in SafeInterface::ParcelHandler
+ virtual status_t logicalNot(bool a, bool* notA) const = 0;
+ virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0;
+ virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0;
+ virtual status_t increment(const TestLightFlattenable& a,
+ TestLightFlattenable* aPlusOne) const = 0;
+ virtual status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+ sp<TestLightRefBaseFlattenable>* aPlusOne) const = 0;
+ virtual status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const = 0;
+ virtual status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const = 0;
+ virtual status_t increment(const std::vector<TestParcelable>& a,
+ std::vector<TestParcelable>* aPlusOne) const = 0;
+ virtual status_t toUpper(const String8& str, String8* upperStr) const = 0;
+ // As mentioned above, sp<IBinder> is already tested by setDeathToken
+ virtual void callMeBack(const sp<ICallback>& callback, int32_t a) const = 0;
+ virtual status_t increment(int32_t a, int32_t* aPlusOne) const = 0;
+ virtual status_t increment(uint32_t a, uint32_t* aPlusOne) const = 0;
+ virtual status_t increment(int64_t a, int64_t* aPlusOne) const = 0;
+ virtual status_t increment(uint64_t a, uint64_t* aPlusOne) const = 0;
+
+ // This tests that input/output parameter interleaving works correctly
+ virtual status_t increment(int32_t a, int32_t* aPlusOne, int32_t b,
+ int32_t* bPlusOne) const = 0;
+};
+
+class BpSafeInterfaceTest : public SafeBpInterface<ISafeInterfaceTest> {
+public:
+ explicit BpSafeInterfaceTest(const sp<IBinder>& impl)
+ : SafeBpInterface<ISafeInterfaceTest>(impl, getLogTag()) {}
+
+ status_t setDeathToken(const sp<IBinder>& token) override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::setDeathToken)>(Tag::SetDeathToken, token);
+ }
+ status_t returnsNoMemory() const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::returnsNoMemory)>(Tag::ReturnsNoMemory);
+ }
+ status_t logicalNot(bool a, bool* notA) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::logicalNot)>(Tag::LogicalNot, a, notA);
+ }
+ status_t modifyEnum(TestEnum a, TestEnum* b) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::modifyEnum)>(Tag::ModifyEnum, a, b);
+ }
+ status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override {
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const TestFlattenable&, TestFlattenable*) const;
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<Signature>(Tag::IncrementFlattenable, a, aPlusOne);
+ }
+ status_t increment(const TestLightFlattenable& a,
+ TestLightFlattenable* aPlusOne) const override {
+ using Signature = status_t (ISafeInterfaceTest::*)(const TestLightFlattenable&,
+ TestLightFlattenable*) const;
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<Signature>(Tag::IncrementLightFlattenable, a, aPlusOne);
+ }
+ status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+ sp<TestLightRefBaseFlattenable>* aPlusOne) const override {
+ using Signature = status_t (ISafeInterfaceTest::*)(const sp<TestLightRefBaseFlattenable>&,
+ sp<TestLightRefBaseFlattenable>*) const;
+ return callRemote<Signature>(Tag::IncrementLightRefBaseFlattenable, a, aPlusOne);
+ }
+ status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const sp<NativeHandle>&, sp<NativeHandle>*) const;
+ return callRemote<Signature>(Tag::IncrementNativeHandle, a, aPlusOne);
+ }
+ status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a,
+ NoCopyNoMove* aPlusOne) const;
+ return callRemote<Signature>(Tag::IncrementNoCopyNoMove, a, aPlusOne);
+ }
+ status_t increment(const std::vector<TestParcelable>& a,
+ std::vector<TestParcelable>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<TestParcelable>&,
+ std::vector<TestParcelable>*);
+ return callRemote<Signature>(Tag::IncrementParcelableVector, a, aPlusOne);
+ }
+ status_t toUpper(const String8& str, String8* upperStr) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemote<decltype(&ISafeInterfaceTest::toUpper)>(Tag::ToUpper, str, upperStr);
+ }
+ void callMeBack(const sp<ICallback>& callback, int32_t a) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return callRemoteAsync<decltype(&ISafeInterfaceTest::callMeBack)>(Tag::CallMeBack, callback,
+ a);
+ }
+ status_t increment(int32_t a, int32_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const;
+ return callRemote<Signature>(Tag::IncrementInt32, a, aPlusOne);
+ }
+ status_t increment(uint32_t a, uint32_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const;
+ return callRemote<Signature>(Tag::IncrementUint32, a, aPlusOne);
+ }
+ status_t increment(int64_t a, int64_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const;
+ return callRemote<Signature>(Tag::IncrementInt64, a, aPlusOne);
+ }
+ status_t increment(uint64_t a, uint64_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
+ return callRemote<Signature>(Tag::IncrementUint64, a, aPlusOne);
+ }
+ status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t, int32_t*) const;
+ return callRemote<Signature>(Tag::IncrementTwo, a, aPlusOne, b, bPlusOne);
+ }
+
+private:
+ static constexpr const char* getLogTag() { return "BpSafeInterfaceTest"; }
+};
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest");
+
+static sp<IBinder::DeathRecipient> getDeathRecipient() {
+ static sp<IBinder::DeathRecipient> recipient = new ExitOnDeath;
+ return recipient;
+}
+#pragma clang diagnostic pop
+
+class BnSafeInterfaceTest : public SafeBnInterface<ISafeInterfaceTest> {
+public:
+ BnSafeInterfaceTest() : SafeBnInterface(getLogTag()) {}
+
+ status_t setDeathToken(const sp<IBinder>& token) override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ token->linkToDeath(getDeathRecipient());
+ return NO_ERROR;
+ }
+ status_t returnsNoMemory() const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ return NO_MEMORY;
+ }
+ status_t logicalNot(bool a, bool* notA) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *notA = !a;
+ return NO_ERROR;
+ }
+ status_t modifyEnum(TestEnum a, TestEnum* b) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID;
+ return NO_ERROR;
+ }
+ status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ aPlusOne->value = a.value + 1;
+ return NO_ERROR;
+ }
+ status_t increment(const TestLightFlattenable& a,
+ TestLightFlattenable* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ aPlusOne->value = a.value + 1;
+ return NO_ERROR;
+ }
+ status_t increment(const sp<TestLightRefBaseFlattenable>& a,
+ sp<TestLightRefBaseFlattenable>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = new TestLightRefBaseFlattenable(a->value + 1);
+ return NO_ERROR;
+ }
+ status_t increment(const sp<NativeHandle>& a, sp<NativeHandle>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ native_handle* rawHandle = native_handle_create(1 /*numFds*/, 1 /*numInts*/);
+ if (rawHandle == nullptr) return NO_MEMORY;
+
+ // Copy the fd over directly
+ rawHandle->data[0] = dup(a->handle()->data[0]);
+
+ // Increment the int
+ rawHandle->data[1] = a->handle()->data[1] + 1;
+
+ // This cannot fail, as it is just the sp<NativeHandle> taking responsibility for closing
+ // the native_handle when it goes out of scope
+ *aPlusOne = NativeHandle::create(rawHandle, true);
+ return NO_ERROR;
+ }
+ status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ aPlusOne->setValue(a.getValue() + 1);
+ return NO_ERROR;
+ }
+ status_t increment(const std::vector<TestParcelable>& a,
+ std::vector<TestParcelable>* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ aPlusOne->resize(a.size());
+ for (size_t i = 0; i < a.size(); ++i) {
+ (*aPlusOne)[i].setValue(a[i].getValue() + 1);
+ }
+ return NO_ERROR;
+ }
+ status_t toUpper(const String8& str, String8* upperStr) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *upperStr = str;
+ upperStr->toUpper();
+ return NO_ERROR;
+ }
+ void callMeBack(const sp<ICallback>& callback, int32_t a) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ callback->onCallback(a + 1);
+ }
+ status_t increment(int32_t a, int32_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ return NO_ERROR;
+ }
+ status_t increment(uint32_t a, uint32_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ return NO_ERROR;
+ }
+ status_t increment(int64_t a, int64_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ return NO_ERROR;
+ }
+ status_t increment(uint64_t a, uint64_t* aPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ return NO_ERROR;
+ }
+ status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ *aPlusOne = a + 1;
+ *bPlusOne = b + 1;
+ return NO_ERROR;
+ }
+
+ // BnInterface
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t /*flags*/) override {
+ EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+ EXPECT_LT(code, static_cast<uint32_t>(Tag::Last));
+ ISafeInterfaceTest::Tag tag = static_cast<ISafeInterfaceTest::Tag>(code);
+ switch (tag) {
+ case ISafeInterfaceTest::Tag::SetDeathToken: {
+ return callLocal(data, reply, &ISafeInterfaceTest::setDeathToken);
+ }
+ case ISafeInterfaceTest::Tag::ReturnsNoMemory: {
+ return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory);
+ }
+ case ISafeInterfaceTest::Tag::LogicalNot: {
+ return callLocal(data, reply, &ISafeInterfaceTest::logicalNot);
+ }
+ case ISafeInterfaceTest::Tag::ModifyEnum: {
+ return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum);
+ }
+ case ISafeInterfaceTest::Tag::IncrementFlattenable: {
+ using Signature = status_t (ISafeInterfaceTest::*)(const TestFlattenable& a,
+ TestFlattenable* aPlusOne) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementLightFlattenable: {
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const TestLightFlattenable& a,
+ TestLightFlattenable* aPlusOne) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementLightRefBaseFlattenable: {
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const sp<TestLightRefBaseFlattenable>&,
+ sp<TestLightRefBaseFlattenable>*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementNativeHandle: {
+ using Signature = status_t (ISafeInterfaceTest::*)(const sp<NativeHandle>&,
+ sp<NativeHandle>*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementNoCopyNoMove: {
+ using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a,
+ NoCopyNoMove* aPlusOne) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementParcelableVector: {
+ using Signature =
+ status_t (ISafeInterfaceTest::*)(const std::vector<TestParcelable>&,
+ std::vector<TestParcelable>*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::ToUpper: {
+ return callLocal(data, reply, &ISafeInterfaceTest::toUpper);
+ }
+ case ISafeInterfaceTest::Tag::CallMeBack: {
+ return callLocalAsync(data, reply, &ISafeInterfaceTest::callMeBack);
+ }
+ case ISafeInterfaceTest::Tag::IncrementInt32: {
+ using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementUint32: {
+ using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementInt64: {
+ using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementUint64: {
+ using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::IncrementTwo: {
+ using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t,
+ int32_t*) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
+ }
+ case ISafeInterfaceTest::Tag::Last:
+ // Should not be possible because of the asserts at the beginning of the method
+ [&]() { FAIL(); }();
+ return UNKNOWN_ERROR;
+ }
+ }
+
+private:
+ static constexpr const char* getLogTag() { return "BnSafeInterfaceTest"; }
+};
+
+class SafeInterfaceTest : public ::testing::Test {
+public:
+ SafeInterfaceTest() : mSafeInterfaceTest(getRemoteService()) {
+ ProcessState::self()->startThreadPool();
+ }
+ ~SafeInterfaceTest() override = default;
+
+protected:
+ sp<ISafeInterfaceTest> mSafeInterfaceTest;
+
+private:
+ static constexpr const char* getLogTag() { return "SafeInterfaceTest"; }
+
+ sp<ISafeInterfaceTest> getRemoteService() {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+ static std::mutex sMutex;
+ static sp<ISafeInterfaceTest> sService;
+ static sp<IBinder> sDeathToken = new BBinder;
+#pragma clang diagnostic pop
+
+ std::unique_lock<decltype(sMutex)> lock;
+ if (sService == nullptr) {
+ ALOG(LOG_INFO, getLogTag(), "Forking remote process");
+ pid_t forkPid = fork();
+ EXPECT_NE(forkPid, -1);
+
+ const String16 serviceName("SafeInterfaceTest");
+
+ if (forkPid == 0) {
+ ALOG(LOG_INFO, getLogTag(), "Remote process checking in");
+ sp<ISafeInterfaceTest> nativeService = new BnSafeInterfaceTest;
+ defaultServiceManager()->addService(serviceName,
+ IInterface::asBinder(nativeService));
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ // We shouldn't get to this point
+ [&]() { FAIL(); }();
+ }
+
+ sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+ sService = interface_cast<ISafeInterfaceTest>(binder);
+ EXPECT_TRUE(sService != nullptr);
+
+ sService->setDeathToken(sDeathToken);
+ }
+
+ return sService;
+ }
+};
+
+TEST_F(SafeInterfaceTest, TestReturnsNoMemory) {
+ status_t result = mSafeInterfaceTest->returnsNoMemory();
+ ASSERT_EQ(NO_MEMORY, result);
+}
+
+TEST_F(SafeInterfaceTest, TestLogicalNot) {
+ const bool a = true;
+ bool notA = true;
+ status_t result = mSafeInterfaceTest->logicalNot(a, &notA);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(!a, notA);
+ // Test both since we don't want to accidentally catch a default false somewhere
+ const bool b = false;
+ bool notB = false;
+ result = mSafeInterfaceTest->logicalNot(b, &notB);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(!b, notB);
+}
+
+TEST_F(SafeInterfaceTest, TestModifyEnum) {
+ const TestEnum a = TestEnum::INITIAL;
+ TestEnum b = TestEnum::INVALID;
+ status_t result = mSafeInterfaceTest->modifyEnum(a, &b);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(TestEnum::FINAL, b);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementFlattenable) {
+ const TestFlattenable a{1};
+ TestFlattenable aPlusOne{0};
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a.value + 1, aPlusOne.value);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementLightFlattenable) {
+ const TestLightFlattenable a{1};
+ TestLightFlattenable aPlusOne{0};
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a.value + 1, aPlusOne.value);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementLightRefBaseFlattenable) {
+ sp<TestLightRefBaseFlattenable> a = new TestLightRefBaseFlattenable{1};
+ sp<TestLightRefBaseFlattenable> aPlusOne;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_NE(nullptr, aPlusOne.get());
+ ASSERT_EQ(a->value + 1, aPlusOne->value);
+}
+
+namespace { // Anonymous namespace
+
+bool fdsAreEquivalent(int a, int b) {
+ struct stat statA {};
+ struct stat statB {};
+ if (fstat(a, &statA) != 0) return false;
+ if (fstat(b, &statB) != 0) return false;
+ return (statA.st_dev == statB.st_dev) && (statA.st_ino == statB.st_ino);
+}
+
+} // Anonymous namespace
+
+TEST_F(SafeInterfaceTest, TestIncrementNativeHandle) {
+ // Create an fd we can use to send and receive from the remote process
+ base::unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)};
+ ASSERT_NE(-1, eventFd);
+
+ // Determine the maximum number of fds this process can have open
+ struct rlimit limit {};
+ ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit));
+ uint32_t maxFds = static_cast<uint32_t>(limit.rlim_cur);
+
+ // Perform this test enough times to rule out fd leaks
+ for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) {
+ native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/);
+ ASSERT_NE(nullptr, handle);
+ handle->data[0] = dup(eventFd.get());
+ handle->data[1] = 1;
+
+ // This cannot fail, as it is just the sp<NativeHandle> taking responsibility for closing
+ // the native_handle when it goes out of scope
+ sp<NativeHandle> a = NativeHandle::create(handle, true);
+
+ sp<NativeHandle> aPlusOne;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_TRUE(fdsAreEquivalent(a->handle()->data[0], aPlusOne->handle()->data[0]));
+ ASSERT_EQ(a->handle()->data[1] + 1, aPlusOne->handle()->data[1]);
+ }
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementNoCopyNoMove) {
+ const NoCopyNoMove a{1};
+ NoCopyNoMove aPlusOne{0};
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a.getValue() + 1, aPlusOne.getValue());
+}
+
+TEST_F(SafeInterfaceTest, TestIncremementParcelableVector) {
+ const std::vector<TestParcelable> a{TestParcelable{1}, TestParcelable{2}};
+ std::vector<TestParcelable> aPlusOne;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(a.size(), aPlusOne.size());
+ for (size_t i = 0; i < a.size(); ++i) {
+ ASSERT_EQ(a[i].getValue() + 1, aPlusOne[i].getValue());
+ }
+}
+
+TEST_F(SafeInterfaceTest, TestToUpper) {
+ const String8 str{"Hello, world!"};
+ String8 upperStr;
+ status_t result = mSafeInterfaceTest->toUpper(str, &upperStr);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_TRUE(upperStr == String8{"HELLO, WORLD!"});
+}
+
+TEST_F(SafeInterfaceTest, TestCallMeBack) {
+ class CallbackReceiver : public BnCallback {
+ public:
+ void onCallback(int32_t aPlusOne) override {
+ ALOG(LOG_INFO, "CallbackReceiver", "%s", __PRETTY_FUNCTION__);
+ std::unique_lock<decltype(mMutex)> lock(mMutex);
+ mValue = aPlusOne;
+ mCondition.notify_one();
+ }
+
+ std::optional<int32_t> waitForCallback() {
+ std::unique_lock<decltype(mMutex)> lock(mMutex);
+ bool success =
+ mCondition.wait_for(lock, 100ms, [&]() { return static_cast<bool>(mValue); });
+ return success ? mValue : std::nullopt;
+ }
+
+ private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ std::optional<int32_t> mValue;
+ };
+
+ sp<CallbackReceiver> receiver = new CallbackReceiver;
+ const int32_t a = 1;
+ mSafeInterfaceTest->callMeBack(receiver, a);
+ auto result = receiver->waitForCallback();
+ ASSERT_TRUE(result);
+ ASSERT_EQ(a + 1, *result);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementInt32) {
+ const int32_t a = 1;
+ int32_t aPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementUint32) {
+ const uint32_t a = 1;
+ uint32_t aPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementInt64) {
+ const int64_t a = 1;
+ int64_t aPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementUint64) {
+ const uint64_t a = 1;
+ uint64_t aPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(a, &aPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+}
+
+TEST_F(SafeInterfaceTest, TestIncrementTwo) {
+ const int32_t a = 1;
+ int32_t aPlusOne = 0;
+ const int32_t b = 2;
+ int32_t bPlusOne = 0;
+ status_t result = mSafeInterfaceTest->increment(1, &aPlusOne, 2, &bPlusOne);
+ ASSERT_EQ(NO_ERROR, result);
+ ASSERT_EQ(a + 1, aPlusOne);
+ ASSERT_EQ(b + 1, bPlusOne);
+}
+
+} // namespace tests
+} // namespace android
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 71b96d4947..6e8f7df84d 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -170,6 +170,8 @@ void worker_fx(
int num,
int worker_count,
int iterations,
+ int payload_size,
+ bool cs_pair,
Pipe p)
{
// Create BinderWorkerService and for go.
@@ -182,22 +184,32 @@ void worker_fx(
p.signal();
p.wait();
+ // If client/server pairs, then half the workers are
+ // servers and half are clients
+ int server_count = cs_pair ? worker_count / 2 : worker_count;
+
// Get references to other binder services.
cout << "Created BinderWorker" << num << endl;
(void)worker_count;
vector<sp<IBinder> > workers;
- for (int i = 0; i < worker_count; i++) {
+ for (int i = 0; i < server_count; i++) {
if (num == i)
continue;
workers.push_back(serviceMgr->getService(generateServiceName(i)));
}
- // Run the benchmark.
+ // Run the benchmark if client
ProcResults results;
chrono::time_point<chrono::high_resolution_clock> start, end;
- for (int i = 0; i < iterations; i++) {
- int target = rand() % workers.size();
+ for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) {
Parcel data, reply;
+ int target = cs_pair ? num % server_count : rand() % workers.size();
+ int sz = payload_size;
+
+ while (sz > sizeof(uint32_t)) {
+ data.writeInt32(0);
+ sz -= sizeof(uint32_t);
+ }
start = chrono::high_resolution_clock::now();
status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
end = chrono::high_resolution_clock::now();
@@ -210,6 +222,7 @@ void worker_fx(
exit(EXIT_FAILURE);
}
}
+
// Signal completion to master and wait.
p.signal();
p.wait();
@@ -221,7 +234,7 @@ void worker_fx(
exit(EXIT_SUCCESS);
}
-Pipe make_worker(int num, int iterations, int worker_count)
+Pipe make_worker(int num, int iterations, int worker_count, int payload_size, bool cs_pair)
{
auto pipe_pair = Pipe::createPipePair();
pid_t pid = fork();
@@ -230,7 +243,7 @@ Pipe make_worker(int num, int iterations, int worker_count)
return move(get<0>(pipe_pair));
} else {
/* child */
- worker_fx(num, worker_count, iterations, move(get<1>(pipe_pair)));
+ worker_fx(num, worker_count, iterations, payload_size, cs_pair, move(get<1>(pipe_pair)));
/* never get here */
return move(get<0>(pipe_pair));
}
@@ -255,6 +268,8 @@ int main(int argc, char *argv[])
{
int workers = 2;
int iterations = 10000;
+ int payload_size = 0;
+ bool cs_pair = false;
(void)argc;
(void)argv;
vector<Pipe> pipes;
@@ -271,11 +286,21 @@ int main(int argc, char *argv[])
i++;
continue;
}
+ if (string(argv[i]) == "-s") {
+ payload_size = atoi(argv[i+1]);
+ i++;
+ }
+ if (string(argv[i]) == "-p") {
+ // client/server pairs instead of spreading
+ // requests to all workers. If true, half
+ // the workers become clients and half servers
+ cs_pair = true;
+ }
}
// Create all the workers and wait for them to spawn.
for (int i = 0; i < workers; i++) {
- pipes.push_back(make_worker(i, iterations, workers));
+ pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
}
wait_all(pipes);
diff --git a/libs/binder/tests/binderValueTypeTest.cpp b/libs/binder/tests/binderValueTypeTest.cpp
new file mode 100644
index 0000000000..c8f46977cd
--- /dev/null
+++ b/libs/binder/tests/binderValueTypeTest.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits>
+#include <cstddef>
+#include <vector>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+#include <binder/Value.h>
+#include <binder/Debug.h>
+
+using ::android::binder::Value;
+using ::android::os::PersistableBundle;
+using ::android::String16;
+using ::std::vector;
+
+#define VALUE_TYPE_TEST(T, TYPENAME, VAL) \
+ TEST(ValueType, Handles ## TYPENAME) { \
+ T x = VAL; \
+ T y = T(); \
+ Value value = VAL; \
+ ASSERT_FALSE(value.empty()); \
+ ASSERT_TRUE(value.is ## TYPENAME ()); \
+ ASSERT_TRUE(value.get ## TYPENAME (&y)); \
+ ASSERT_EQ(x, y); \
+ ASSERT_EQ(value, Value(y)); \
+ value.put ## TYPENAME (x); \
+ ASSERT_EQ(value, Value(y)); \
+ value = Value(); \
+ ASSERT_TRUE(value.empty()); \
+ ASSERT_NE(value, Value(y)); \
+ value = y; \
+ ASSERT_EQ(value, Value(x)); \
+ }
+
+#define VALUE_TYPE_VECTOR_TEST(T, TYPENAME, VAL) \
+ TEST(ValueType, Handles ## TYPENAME ## Vector) { \
+ vector<T> x; \
+ vector<T> y; \
+ x.push_back(VAL); \
+ x.push_back(T()); \
+ Value value(x); \
+ ASSERT_FALSE(value.empty()); \
+ ASSERT_TRUE(value.is ## TYPENAME ## Vector()); \
+ ASSERT_TRUE(value.get ## TYPENAME ## Vector(&y)); \
+ ASSERT_EQ(x, y); \
+ ASSERT_EQ(value, Value(y)); \
+ value.put ## TYPENAME ## Vector(x); \
+ ASSERT_EQ(value, Value(y)); \
+ value = Value(); \
+ ASSERT_TRUE(value.empty()); \
+ ASSERT_NE(value, Value(y)); \
+ value = y; \
+ ASSERT_EQ(value, Value(x)); \
+ }
+
+VALUE_TYPE_TEST(bool, Boolean, true)
+VALUE_TYPE_TEST(int32_t, Int, 31337)
+VALUE_TYPE_TEST(int64_t, Long, 13370133701337l)
+VALUE_TYPE_TEST(double, Double, 3.14159265358979323846)
+VALUE_TYPE_TEST(String16, String, String16("Lovely"))
+
+VALUE_TYPE_VECTOR_TEST(bool, Boolean, true)
+VALUE_TYPE_VECTOR_TEST(int32_t, Int, 31337)
+VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337l)
+VALUE_TYPE_VECTOR_TEST(double, Double, 3.14159265358979323846)
+VALUE_TYPE_VECTOR_TEST(String16, String, String16("Lovely"))
+
+VALUE_TYPE_TEST(PersistableBundle, PersistableBundle, PersistableBundle())
+
+TEST(ValueType, HandlesClear) {
+ Value value;
+ ASSERT_TRUE(value.empty());
+ value.putInt(31337);
+ ASSERT_FALSE(value.empty());
+ value.clear();
+ ASSERT_TRUE(value.empty());
+}
+
+TEST(ValueType, HandlesSwap) {
+ Value value_a, value_b;
+ int32_t int_x;
+ value_a.putInt(31337);
+ ASSERT_FALSE(value_a.empty());
+ ASSERT_TRUE(value_b.empty());
+ value_a.swap(value_b);
+ ASSERT_FALSE(value_b.empty());
+ ASSERT_TRUE(value_a.empty());
+ ASSERT_TRUE(value_b.getInt(&int_x));
+ ASSERT_EQ(31337, int_x);
+}
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp
index fe9e05a4f1..13f03b1ae0 100644
--- a/libs/binder/tests/schd-dbg.cpp
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -40,7 +40,7 @@ vector<sp<IBinder> > workers;
// GOOD_SYNC_MIN is considered as good
#define GOOD_SYNC_MIN (0.6)
-#define DUMP_PRICISION 3
+#define DUMP_PRESICION 2
string trace_path = "/sys/kernel/debug/tracing";
@@ -246,10 +246,11 @@ struct Results {
double worst = (double)m_worst / 1.0E6;
double average = (double)m_total_time / m_transactions / 1.0E6;
// FIXME: libjson?
- cout << std::setprecision(DUMP_PRICISION) << "{ \"avg\":" << setw(5) << left
- << average << ", \"wst\":" << setw(5) << left << worst
- << ", \"bst\":" << setw(5) << left << best << ", \"miss\":" << setw(5)
- << left << m_miss << ", \"meetR\":" << setw(3) << left
+ int W = DUMP_PRESICION + 2;
+ cout << setprecision(DUMP_PRESICION) << "{ \"avg\":" << setw(W) << left
+ << average << ",\"wst\":" << setw(W) << left << worst
+ << ",\"bst\":" << setw(W) << left << best << ",\"miss\":" << left
+ << m_miss << ",\"meetR\":" << left << setprecision(DUMP_PRESICION + 3)
<< (1.0 - (double)m_miss / m_transactions) << "}";
}
};
@@ -272,8 +273,15 @@ static void parcel_fill(Parcel& data, int sz, int priority, int cpu) {
}
}
+typedef struct {
+ void* result;
+ int target;
+} thread_priv_t;
+
static void* thread_start(void* p) {
- Results* results_fifo = (Results*)p;
+ thread_priv_t* priv = (thread_priv_t*)p;
+ int target = priv->target;
+ Results* results_fifo = (Results*)priv->result;
Parcel data, reply;
Tick sta, end;
@@ -281,7 +289,7 @@ static void* thread_start(void* p) {
thread_dump("fifo-caller");
sta = tickNow();
- status_t ret = workers[0]->transact(BINDER_NOP, data, &reply);
+ status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
end = tickNow();
results_fifo->add_time(tickNano(sta, end));
@@ -291,16 +299,19 @@ static void* thread_start(void* p) {
}
// create a fifo thread to transact and wait it to finished
-static void thread_transaction(Results* results_fifo) {
+static void thread_transaction(int target, Results* results_fifo) {
+ thread_priv_t thread_priv;
void* dummy;
pthread_t thread;
pthread_attr_t attr;
struct sched_param param;
+ thread_priv.target = target;
+ thread_priv.result = results_fifo;
ASSERT(!pthread_attr_init(&attr));
ASSERT(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO));
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
ASSERT(!pthread_attr_setschedparam(&attr, &param));
- ASSERT(!pthread_create(&thread, &attr, &thread_start, results_fifo));
+ ASSERT(!pthread_create(&thread, &attr, &thread_start, &thread_priv));
ASSERT(!pthread_join(thread, &dummy));
}
@@ -316,7 +327,9 @@ void worker_fx(int num, int no_process, int iterations, int payload_size,
sp<IServiceManager> serviceMgr = defaultServiceManager();
sp<BinderWorkerService> service = new BinderWorkerService;
serviceMgr->addService(generateServiceName(num), service);
+ // init done
p.signal();
+ // wait for kick-off
p.wait();
// If client/server pairs, then half the workers are
@@ -338,7 +351,7 @@ void worker_fx(int num, int no_process, int iterations, int payload_size,
int target = num % server_count;
// 1. transaction by fifo thread
- thread_transaction(&results_fifo);
+ thread_transaction(target, &results_fifo);
parcel_fill(data, payload_size, thread_pri(), sched_getcpu());
thread_dump("other-caller");
@@ -356,6 +369,7 @@ void worker_fx(int num, int no_process, int iterations, int payload_size,
p.wait();
p.send(&dummy);
+ // wait for kill
p.wait();
// Client for each pair dump here
if (is_client(num)) {
@@ -367,10 +381,10 @@ void worker_fx(int num, int no_process, int iterations, int payload_size,
<< "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
<< "\"R\":" << sync_ratio << "," << endl;
- cout << " \"other_ms\":";
+ cout << " \"other_ms\":";
results_other.dump();
cout << "," << endl;
- cout << " \"fifo_ms\": ";
+ cout << " \"fifo_ms\": ";
results_fifo.dump();
cout << endl;
cout << "}," << endl;
@@ -463,12 +477,17 @@ int main(int argc, char** argv) {
for (int i = 0; i < no_process; i++) {
pipes.push_back(make_process(i, iterations, no_process, payload_size));
}
+ // wait for init done
wait_all(pipes);
+ // kick-off iterations
signal_all(pipes);
+ // wait for completion
wait_all(pipes);
+ // start to send result
signal_all(pipes);
for (int i = 0; i < no_process; i++) {
int status;
+ // kill
pipes[i].signal();
wait(&status);
// the exit status is number of transactions without priority inheritance
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 7ac03f19d5..f1d1346366 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -38,9 +38,17 @@ cc_library_shared {
// Don't warn about struct padding
"-Wno-padded",
- // android/sensors.h uses nested anonymous unions and anonymous structs
- "-Wno-nested-anon-types",
- "-Wno-gnu-anonymous-struct",
+ // We are aware of the risks inherent in comparing floats for equality
+ "-Wno-float-equal",
+
+ // Pure abstract classes trigger this warning
+ "-Wno-weak-vtables",
+
+ // Allow four-character integer literals
+ "-Wno-four-char-constants",
+
+ // Allow documentation warnings
+ "-Wno-documentation",
"-DDEBUG_ONLY_CODE=0",
],
@@ -49,7 +57,7 @@ cc_library_shared {
brillo: {
cflags: ["-DHAVE_NO_SURFACE_FLINGER"],
},
- debuggable: {
+ eng: {
cppflags: [
"-UDEBUG_ONLY_CODE",
"-DDEBUG_ONLY_CODE=1",
@@ -58,8 +66,6 @@ cc_library_shared {
},
srcs: [
- "IGraphicBufferConsumer.cpp",
- "IConsumerListener.cpp",
"BitTube.cpp",
"BufferItem.cpp",
"BufferItemConsumer.cpp",
@@ -71,43 +77,56 @@ cc_library_shared {
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DisplayEventReceiver.cpp",
+ "FrameTimestamps.cpp",
"GLConsumer.cpp",
- "GraphicBufferAlloc.cpp",
- "GraphicsEnv.cpp",
"GuiConfig.cpp",
"IDisplayEventConnection.cpp",
- "IGraphicBufferAlloc.cpp",
+ "IConsumerListener.cpp",
+ "IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
- "ISensorEventConnection.cpp",
- "ISensorServer.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"LayerState.cpp",
"OccupancyTracker.cpp",
- "Sensor.cpp",
- "SensorEventQueue.cpp",
- "SensorManager.cpp",
"StreamSplitter.cpp",
"Surface.cpp",
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
+ "view/Surface.cpp",
+ "bufferqueue/1.0/B2HProducerListener.cpp",
+ "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
],
shared_libs: [
- "libnativeloader",
+ "libsync",
"libbinder",
"libcutils",
"libEGL",
"libGLESv2",
- "libsync",
"libui",
"libutils",
+ "libnativewindow",
"liblog",
+ "libhidlbase",
+ "libhidltransport",
+ "android.hidl.token@1.0-utils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore-utils",
],
- export_shared_lib_headers: ["libbinder"],
+ export_shared_lib_headers: [
+ "libbinder",
+ "libui",
+ "android.hidl.token@1.0-utils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
}
subdirs = ["tests"]
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
index b653c5b69c..ef7a6f54d9 100644
--- a/libs/gui/BitTube.cpp
+++ b/libs/gui/BitTube.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#include <private/gui/BitTube.h>
+
#include <stdint.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
@@ -25,46 +27,21 @@
#include <binder/Parcel.h>
-#include <gui/BitTube.h>
-
namespace android {
-// ----------------------------------------------------------------------------
+namespace gui {
-// Socket buffer size. The default is typically about 128KB, which is much larger than
-// we really need. So we make it smaller.
+// Socket buffer size. The default is typically about 128KB, which is much larger than we really
+// need. So we make it smaller.
static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
-
-BitTube::BitTube()
- : mSendFd(-1), mReceiveFd(-1)
-{
- init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
-}
-
-BitTube::BitTube(size_t bufsize)
- : mSendFd(-1), mReceiveFd(-1)
-{
+BitTube::BitTube(size_t bufsize) {
init(bufsize, bufsize);
}
-BitTube::BitTube(const Parcel& data)
- : mSendFd(-1), mReceiveFd(-1)
-{
- mReceiveFd = dup(data.readFileDescriptor());
- if (mReceiveFd < 0) {
- mReceiveFd = -errno;
- ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)",
- strerror(-mReceiveFd));
- }
-}
+BitTube::BitTube(DefaultSizeType) : BitTube(DEFAULT_SOCKET_BUFFER_SIZE) {}
-BitTube::~BitTube()
-{
- if (mSendFd >= 0)
- close(mSendFd);
-
- if (mReceiveFd >= 0)
- close(mReceiveFd);
+BitTube::BitTube(const Parcel& data) {
+ readFromParcel(&data);
}
void BitTube::init(size_t rcvbuf, size_t sndbuf) {
@@ -73,39 +50,43 @@ void BitTube::init(size_t rcvbuf, size_t sndbuf) {
size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
- // sine we don't use the "return channel", we keep it small...
+ // since we don't use the "return channel", we keep it small...
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
fcntl(sockets[0], F_SETFL, O_NONBLOCK);
fcntl(sockets[1], F_SETFL, O_NONBLOCK);
- mReceiveFd = sockets[0];
- mSendFd = sockets[1];
+ mReceiveFd.reset(sockets[0]);
+ mSendFd.reset(sockets[1]);
} else {
- mReceiveFd = -errno;
- ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
+ mReceiveFd.reset();
+ ALOGE("BitTube: pipe creation failed (%s)", strerror(errno));
}
}
-status_t BitTube::initCheck() const
-{
+status_t BitTube::initCheck() const {
if (mReceiveFd < 0) {
return status_t(mReceiveFd);
}
return NO_ERROR;
}
-int BitTube::getFd() const
-{
+int BitTube::getFd() const {
return mReceiveFd;
}
-int BitTube::getSendFd() const
-{
+int BitTube::getSendFd() const {
return mSendFd;
}
-ssize_t BitTube::write(void const* vaddr, size_t size)
-{
+base::unique_fd BitTube::moveReceiveFd() {
+ return std::move(mReceiveFd);
+}
+
+void BitTube::setReceiveFd(base::unique_fd&& receiveFd) {
+ mReceiveFd = std::move(receiveFd);
+}
+
+ssize_t BitTube::write(void const* vaddr, size_t size) {
ssize_t err, len;
do {
len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
@@ -115,62 +96,66 @@ ssize_t BitTube::write(void const* vaddr, size_t size)
return err == 0 ? len : -err;
}
-ssize_t BitTube::read(void* vaddr, size_t size)
-{
+ssize_t BitTube::read(void* vaddr, size_t size) {
ssize_t err, len;
do {
len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
err = len < 0 ? errno : 0;
} while (err == EINTR);
if (err == EAGAIN || err == EWOULDBLOCK) {
- // EAGAIN means that we have non-blocking I/O but there was
- // no data to be read. Nothing the client should care about.
+ // EAGAIN means that we have non-blocking I/O but there was no data to be read. Nothing the
+ // client should care about.
return 0;
}
return err == 0 ? len : -err;
}
-status_t BitTube::writeToParcel(Parcel* reply) const
-{
- if (mReceiveFd < 0)
- return -EINVAL;
+status_t BitTube::writeToParcel(Parcel* reply) const {
+ if (mReceiveFd < 0) return -EINVAL;
status_t result = reply->writeDupFileDescriptor(mReceiveFd);
- close(mReceiveFd);
- mReceiveFd = -1;
+ mReceiveFd.reset();
return result;
}
+status_t BitTube::readFromParcel(const Parcel* parcel) {
+ mReceiveFd.reset(dup(parcel->readFileDescriptor()));
+ if (mReceiveFd < 0) {
+ mReceiveFd.reset();
+ int error = errno;
+ ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error));
+ return -error;
+ }
+ return NO_ERROR;
+}
-ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
- void const* events, size_t count, size_t objSize)
-{
+ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
const char* vaddr = reinterpret_cast<const char*>(events);
- ssize_t size = tube->write(vaddr, count*objSize);
+ ssize_t size = tube->write(vaddr, count * objSize);
// should never happen because of SOCK_SEQPACKET
LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
- "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)",
- count, objSize, size);
+ "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were "
+ "sent!)",
+ count, objSize, size);
- //ALOGE_IF(size<0, "error %d sending %d events", size, count);
+ // ALOGE_IF(size<0, "error %d sending %d events", size, count);
return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
-ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
- void* events, size_t count, size_t objSize)
-{
+ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) {
char* vaddr = reinterpret_cast<char*>(events);
- ssize_t size = tube->read(vaddr, count*objSize);
+ ssize_t size = tube->read(vaddr, count * objSize);
// should never happen because of SOCK_SEQPACKET
LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
- "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)",
- count, objSize, size);
+ "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were "
+ "received!)",
+ count, objSize, size);
- //ALOGE_IF(size<0, "error %d receiving %d events", size, count);
+ // ALOGE_IF(size<0, "error %d receiving %d events", size, count);
return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
-// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace gui
+} // namespace android
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 1357a4aa1a..69b5962441 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -81,6 +81,9 @@ size_t BufferItem::getPodSize() const {
addAligned(size, mIsDroppable);
addAligned(size, mAcquireCalled);
addAligned(size, mTransformToDisplayInverse);
+ addAligned(size, mAutoRefresh);
+ addAligned(size, mQueuedBuffer);
+ addAligned(size, mIsStale);
return size;
}
@@ -88,11 +91,11 @@ size_t BufferItem::getFlattenedSize() const {
size_t size = sizeof(uint32_t); // Flags
if (mGraphicBuffer != 0) {
size += mGraphicBuffer->getFlattenedSize();
- FlattenableUtils::align<4>(size);
+ size = FlattenableUtils::align<4>(size);
}
if (mFence != 0) {
size += mFence->getFlattenedSize();
- FlattenableUtils::align<4>(size);
+ size = FlattenableUtils::align<4>(size);
}
size += mSurfaceDamage.getFlattenedSize();
size = FlattenableUtils::align<8>(size);
@@ -166,6 +169,9 @@ status_t BufferItem::flatten(
writeAligned(buffer, size, mIsDroppable);
writeAligned(buffer, size, mAcquireCalled);
writeAligned(buffer, size, mTransformToDisplayInverse);
+ writeAligned(buffer, size, mAutoRefresh);
+ writeAligned(buffer, size, mQueuedBuffer);
+ writeAligned(buffer, size, mIsStale);
return NO_ERROR;
}
@@ -198,6 +204,8 @@ status_t BufferItem::unflatten(
status_t err = mFence->unflatten(buffer, size, fds, count);
if (err) return err;
size -= FlattenableUtils::align<4>(buffer);
+
+ mFenceTime = std::make_shared<FenceTime>(mFence);
}
status_t err = mSurfaceDamage.unflatten(buffer, size);
@@ -227,6 +235,9 @@ status_t BufferItem::unflatten(
readAligned(buffer, size, mIsDroppable);
readAligned(buffer, size, mAcquireCalled);
readAligned(buffer, size, mTransformToDisplayInverse);
+ readAligned(buffer, size, mAutoRefresh);
+ readAligned(buffer, size, mQueuedBuffer);
+ readAligned(buffer, size, mIsStale);
return NO_ERROR;
}
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 3491043811..d9d50dbeb4 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -22,7 +22,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
-//#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#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__)
@@ -57,6 +57,12 @@ void BufferItemConsumer::setName(const String8& name) {
mConsumer->setConsumerName(name);
}
+void BufferItemConsumer::setBufferFreedListener(
+ const wp<BufferFreedListener>& listener) {
+ Mutex::Autolock _l(mMutex);
+ mBufferFreedListener = listener;
+}
+
status_t BufferItemConsumer::acquireBuffer(BufferItem *item,
nsecs_t presentWhen, bool waitForFence) {
status_t err;
@@ -104,4 +110,14 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item,
return err;
}
+void BufferItemConsumer::freeBufferLocked(int slotIndex) {
+ sp<BufferFreedListener> listener = mBufferFreedListener.promote();
+ if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) {
+ // Fire callback if we have a listener registered and the buffer being freed is valid.
+ BI_LOGV("actually calling onBufferFreed");
+ listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer);
+ }
+ ConsumerBase::freeBufferLocked(slotIndex);
+}
+
} // namespace android
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 6de98f5a25..41512127f2 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -31,6 +31,13 @@ BufferQueue::ProxyConsumerListener::ProxyConsumerListener(
BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
+void BufferQueue::ProxyConsumerListener::onDisconnect() {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ listener->onDisconnect();
+ }
+}
+
void BufferQueue::ProxyConsumerListener::onFrameAvailable(
const BufferItem& item) {
sp<ConsumerListener> listener(mConsumerListener.promote());
@@ -61,28 +68,28 @@ void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() {
}
}
-bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
- uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
sp<ConsumerListener> listener(mConsumerListener.promote());
- if (listener != NULL) {
- return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ if (listener != nullptr) {
+ listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
- return false;
}
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
- const sp<IGraphicBufferAlloc>& allocator) {
+ bool consumerIsSurfaceFlinger) {
LOG_ALWAYS_FATAL_IF(outProducer == NULL,
"BufferQueue: outProducer must not be NULL");
LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
"BufferQueue: outConsumer must not be NULL");
- sp<BufferQueueCore> core(new BufferQueueCore(allocator));
+ sp<BufferQueueCore> core(new BufferQueueCore());
LOG_ALWAYS_FATAL_IF(core == NULL,
"BufferQueue: failed to create BufferQueueCore");
- sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
+ sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
LOG_ALWAYS_FATAL_IF(producer == NULL,
"BufferQueue: failed to create BufferQueueProducer");
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index ee4c58cfce..5e5de443f4 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -205,6 +205,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
// was cached when it was last queued.
outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
outBuffer->mFence = Fence::NO_FENCE;
+ outBuffer->mFenceTime = FenceTime::NO_FENCE;
outBuffer->mCrop = mCore->mSharedBufferCache.crop;
outBuffer->mTransform = mCore->mSharedBufferCache.transform &
~static_cast<uint32_t>(
@@ -674,12 +675,13 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
return NO_ERROR;
}
-void BufferQueueConsumer::setConsumerName(const String8& name) {
+status_t BufferQueueConsumer::setConsumerName(const String8& name) {
ATRACE_CALL();
BQ_LOGV("setConsumerName: '%s'", name.string());
Mutex::Autolock lock(mCore->mMutex);
mCore->mConsumerName = name;
mConsumerName = name;
+ return NO_ERROR;
}
status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) {
@@ -707,6 +709,14 @@ status_t BufferQueueConsumer::setConsumerUsageBits(uint32_t usage) {
return NO_ERROR;
}
+status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) {
+ ATRACE_CALL();
+ BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false");
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->mConsumerIsProtected = isProtected;
+ return NO_ERROR;
+}
+
status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
ATRACE_CALL();
BQ_LOGV("setTransformHint: %#x", hint);
@@ -715,9 +725,10 @@ status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
return NO_ERROR;
}
-sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const {
+status_t BufferQueueConsumer::getSidebandStream(sp<NativeHandle>* outStream) const {
Mutex::Autolock lock(mCore->mMutex);
- return mCore->mSidebandStream;
+ *outStream = mCore->mSidebandStream;
+ return NO_ERROR;
}
status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
@@ -733,20 +744,22 @@ status_t BufferQueueConsumer::discardFreeBuffers() {
return NO_ERROR;
}
-void BufferQueueConsumer::dumpState(String8& result, const char* prefix) const {
+status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const {
const IPCThreadState* ipc = IPCThreadState::self();
const pid_t pid = ipc->getCallingPid();
const uid_t uid = ipc->getCallingUid();
if ((uid != AID_SHELL)
&& !PermissionCache::checkPermission(String16(
"android.permission.DUMP"), pid, uid)) {
- result.appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+ outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
"from pid=%d, uid=%d\n", pid, uid);
android_errorWriteWithInfoLog(0x534e4554, "27046057",
static_cast<int32_t>(uid), NULL, 0);
- } else {
- mCore->dumpState(result, prefix);
+ return PERMISSION_DENIED;
}
+
+ mCore->dumpState(prefix, outResult);
+ return NO_ERROR;
}
} // namespace android
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 6e6cce28eb..cfb25e0503 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -29,12 +29,11 @@
#include <inttypes.h>
#include <cutils/properties.h>
+#include <cutils/atomic.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueueCore.h>
-#include <gui/GraphicBufferAlloc.h>
#include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferAlloc.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
@@ -53,14 +52,14 @@ static uint64_t getUniqueId() {
return id | counter++;
}
-BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
- mAllocator(allocator),
+BufferQueueCore::BufferQueueCore() :
mMutex(),
mIsAbandoned(false),
mConsumerControlledByApp(false),
mConsumerName(getUniqueName()),
mConsumerListener(),
mConsumerUsageBits(0),
+ mConsumerIsProtected(false),
mConnectedApi(NO_CONNECTED_API),
mLinkedToDeath(),
mConnectedProducerListener(),
@@ -96,30 +95,6 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
mLastQueuedSlot(INVALID_BUFFER_SLOT),
mUniqueId(getUniqueId())
{
- if (allocator == NULL) {
-
-#ifdef HAVE_NO_SURFACE_FLINGER
- // Without a SurfaceFlinger, allocate in-process. This only makes
- // sense in systems with static SELinux configurations and no
- // applications (since applications need dynamic SELinux policy).
- mAllocator = new GraphicBufferAlloc();
-#else
- // Run time check for headless, where we also allocate in-process.
- char value[PROPERTY_VALUE_MAX];
- property_get("config.headless", value, "0");
- if (atoi(value) == 1) {
- mAllocator = new GraphicBufferAlloc();
- } else {
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- mAllocator = composer->createGraphicBufferAlloc();
- }
-#endif // HAVE_NO_SURFACE_FLINGER
-
- if (mAllocator == NULL) {
- BQ_LOGE("createGraphicBufferAlloc failed");
- }
- }
-
int numStartingBuffers = getMaxBufferCountLocked();
for (int s = 0; s < numStartingBuffers; s++) {
mFreeSlots.insert(s);
@@ -132,7 +107,7 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
BufferQueueCore::~BufferQueueCore() {}
-void BufferQueueCore::dumpState(String8& result, const char* prefix) const {
+void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
Mutex::Autolock lock(mMutex);
String8 fifo;
@@ -147,10 +122,10 @@ void BufferQueueCore::dumpState(String8& result, const char* prefix) const {
++current;
}
- result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
+ outResult->appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
"mMaxDequeuedBufferCount=%d, mDequeueBufferCannotBlock=%d "
"mAsyncMode=%d, default-size=[%dx%d], default-format=%d, "
- "transform-hint=%02x, FIFO(%zu)={%s}\n", prefix,
+ "transform-hint=%02x, FIFO(%zu)={%s}\n", prefix.string(),
mMaxAcquiredBufferCount, mMaxDequeuedBufferCount,
mDequeueBufferCannotBlock, mAsyncMode, mDefaultWidth,
mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(),
@@ -160,28 +135,28 @@ void BufferQueueCore::dumpState(String8& result, const char* prefix) const {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
// A dequeued buffer might be null if it's still being allocated
if (buffer.get()) {
- result.appendFormat("%s%s[%02d:%p] state=%-8s, %p "
- "[%4ux%4u:%4u,%3X]\n", prefix,
+ outResult->appendFormat("%s%s[%02d:%p] state=%-8s, %p "
+ "[%4ux%4u:%4u,%3X]\n", prefix.string(),
(mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
buffer.get(), mSlots[s].mBufferState.string(),
buffer->handle, buffer->width, buffer->height,
buffer->stride, buffer->format);
} else {
- result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+ outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s,
buffer.get(), mSlots[s].mBufferState.string());
}
}
for (int s : mFreeBuffers) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
- result.appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
- prefix, s, buffer.get(), mSlots[s].mBufferState.string(),
+ outResult->appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+ prefix.string(), s, buffer.get(), mSlots[s].mBufferState.string(),
buffer->handle, buffer->width, buffer->height, buffer->stride,
buffer->format);
}
for (int s : mFreeSlots) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
- result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+ outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s,
buffer.get(), mSlots[s].mBufferState.string());
}
}
@@ -260,6 +235,13 @@ void BufferQueueCore::freeAllBuffersLocked() {
for (auto& b : mQueue) {
b.mIsStale = true;
+
+ // We set this to false to force the BufferQueue to resend the buffer
+ // handle upon acquire, since if we're here due to a producer
+ // disconnect, the consumer will have been told to purge its cache of
+ // slot-to-buffer-handle mappings and will not be able to otherwise
+ // obtain a valid buffer handle.
+ b.mAcquireCalled = false;
}
VALIDATE_CONSISTENCY();
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index f0e701e9f3..838586411d 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -34,7 +34,6 @@
#include <gui/BufferQueueProducer.h>
#include <gui/GLConsumer.h>
#include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferAlloc.h>
#include <gui/IProducerListener.h>
#include <utils/Log.h>
@@ -42,12 +41,17 @@
namespace android {
-BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
+static constexpr uint32_t BQ_LAYER_COUNT = 1;
+
+BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
+ bool consumerIsSurfaceFlinger) :
mCore(core),
mSlots(core->mSlots),
mConsumerName(),
mStickyTransform(0),
+ mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),
mLastQueueBufferFence(Fence::NO_FENCE),
+ mLastQueuedTransform(0),
mCallbackMutex(),
mNextCallbackTicket(0),
mCurrentCallbackTicket(0),
@@ -343,7 +347,8 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
sp<android::Fence> *outFence, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t usage) {
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
ATRACE_CALL();
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -411,7 +416,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
// buffer. If this buffer would require reallocation to meet the
// requested attributes, we free it and attempt to get another one.
if (!mCore->mAllowAllocation) {
- if (buffer->needsReallocation(width, height, format, usage)) {
+ if (buffer->needsReallocation(width, height, format,
+ BQ_LAYER_COUNT, usage)) {
if (mCore->mSharedBufferSlot == found) {
BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
"buffer");
@@ -427,7 +433,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if (mCore->mSharedBufferSlot == found &&
- buffer->needsReallocation(width, height, format, usage)) {
+ buffer->needsReallocation(width, height, format,
+ BQ_LAYER_COUNT, usage)) {
BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
"buffer");
@@ -446,7 +453,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
mSlots[found].mBufferState.dequeue();
if ((buffer == NULL) ||
- buffer->needsReallocation(width, height, format, usage))
+ buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
{
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = NULL;
@@ -494,15 +501,17 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
- status_t error;
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
- sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
- width, height, format, usage,
- {mConsumerName.string(), mConsumerName.size()}, &error));
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+ width, height, format, BQ_LAYER_COUNT, usage,
+ {mConsumerName.string(), mConsumerName.size()});
+
+ status_t error = graphicBuffer->initCheck();
+
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
- if (graphicBuffer != NULL && !mCore->mIsAbandoned) {
+ if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
}
@@ -510,7 +519,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.broadcast();
- if (graphicBuffer == NULL) {
+ if (error != NO_ERROR) {
mCore->mFreeSlots.insert(*outSlot);
mCore->clearBufferSlotLocked(*outSlot);
BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
@@ -552,6 +561,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+ addAndGetFrameTimestamps(nullptr, outTimestamps);
+
return returnFlags;
}
@@ -621,40 +632,48 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
return BAD_VALUE;
}
- Mutex::Autolock lock(mCore->mMutex);
+ sp<IConsumerListener> listener;
+ {
+ Mutex::Autolock lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
- return NO_INIT;
- }
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
- if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
- if (mCore->mSharedBufferMode) {
- BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer "
- "mode");
- return BAD_VALUE;
- }
+ if (mCore->mSharedBufferMode) {
+ BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer "
+ "mode");
+ return BAD_VALUE;
+ }
- mCore->waitWhileAllocatingLocked();
+ mCore->waitWhileAllocatingLocked();
- if (mCore->mFreeBuffers.empty()) {
- return NO_MEMORY;
- }
+ if (mCore->mFreeBuffers.empty()) {
+ return NO_MEMORY;
+ }
- int found = mCore->mFreeBuffers.front();
- mCore->mFreeBuffers.remove(found);
- mCore->mFreeSlots.insert(found);
+ int found = mCore->mFreeBuffers.front();
+ mCore->mFreeBuffers.remove(found);
+ mCore->mFreeSlots.insert(found);
- BQ_LOGV("detachNextBuffer detached slot %d", found);
+ BQ_LOGV("detachNextBuffer detached slot %d", found);
- *outBuffer = mSlots[found].mGraphicBuffer;
- *outFence = mSlots[found].mFence;
- mCore->clearBufferSlotLocked(found);
- VALIDATE_CONSISTENCY();
+ *outBuffer = mSlots[found].mGraphicBuffer;
+ *outFence = mSlots[found].mFence;
+ mCore->clearBufferSlotLocked(found);
+ VALIDATE_CONSISTENCY();
+ listener = mCore->mConsumerListener;
+ }
+
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
return NO_ERROR;
}
@@ -733,23 +752,27 @@ status_t BufferQueueProducer::queueBuffer(int slot,
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- int64_t timestamp;
+ int64_t requestedPresentTimestamp;
bool isAutoTimestamp;
android_dataspace dataSpace;
Rect crop(Rect::EMPTY_RECT);
int scalingMode;
uint32_t transform;
uint32_t stickyTransform;
- sp<Fence> fence;
- input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode,
- &transform, &fence, &stickyTransform);
+ sp<Fence> acquireFence;
+ bool getFrameTimestamps = false;
+ input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
+ &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
+ &getFrameTimestamps);
Region surfaceDamage = input.getSurfaceDamage();
- if (fence == NULL) {
+ if (acquireFence == NULL) {
BQ_LOGE("queueBuffer: fence is NULL");
return BAD_VALUE;
}
+ auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);
+
switch (scalingMode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
@@ -764,6 +787,7 @@ status_t BufferQueueProducer::queueBuffer(int slot,
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
+ uint64_t currentFrameNumber = 0;
BufferItem item;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
@@ -802,8 +826,9 @@ status_t BufferQueueProducer::queueBuffer(int slot,
BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
" crop=[%d,%d,%d,%d] transform=%#x scale=%s",
- slot, mCore->mFrameCounter + 1, timestamp, dataSpace,
- crop.left, crop.top, crop.right, crop.bottom, transform,
+ slot, mCore->mFrameCounter + 1, requestedPresentTimestamp,
+ dataSpace, crop.left, crop.top, crop.right, crop.bottom,
+ transform,
BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
@@ -821,11 +846,14 @@ status_t BufferQueueProducer::queueBuffer(int slot,
dataSpace = mCore->mDefaultBufferDataSpace;
}
- mSlots[slot].mFence = fence;
+ mSlots[slot].mFence = acquireFence;
mSlots[slot].mBufferState.queue();
+ // Increment the frame counter and store a local version of it
+ // for use outside the lock on mCore->mMutex.
++mCore->mFrameCounter;
- mSlots[slot].mFrameNumber = mCore->mFrameCounter;
+ currentFrameNumber = mCore->mFrameCounter;
+ mSlots[slot].mFrameNumber = currentFrameNumber;
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
@@ -835,12 +863,13 @@ status_t BufferQueueProducer::queueBuffer(int slot,
item.mTransformToDisplayInverse =
(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
item.mScalingMode = static_cast<uint32_t>(scalingMode);
- item.mTimestamp = timestamp;
+ item.mTimestamp = requestedPresentTimestamp;
item.mIsAutoTimestamp = isAutoTimestamp;
item.mDataSpace = dataSpace;
- item.mFrameNumber = mCore->mFrameCounter;
+ item.mFrameNumber = currentFrameNumber;
item.mSlot = slot;
- item.mFence = fence;
+ item.mFence = acquireFence;
+ item.mFenceTime = acquireFenceTime;
item.mIsDroppable = mCore->mAsyncMode ||
mCore->mDequeueBufferCannotBlock ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
@@ -859,6 +888,7 @@ status_t BufferQueueProducer::queueBuffer(int slot,
mCore->mSharedBufferCache.dataspace = dataSpace;
}
+ output->bufferReplaced = false;
if (mCore->mQueue.empty()) {
// When the queue is empty, we can ignore mDequeueBufferCannotBlock
// and simply queue this buffer
@@ -885,6 +915,7 @@ status_t BufferQueueProducer::queueBuffer(int slot,
if (!mSlots[last.mSlot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(last.mSlot);
mCore->mFreeBuffers.push_back(last.mSlot);
+ output->bufferReplaced = true;
}
}
@@ -901,10 +932,11 @@ status_t BufferQueueProducer::queueBuffer(int slot,
mCore->mDequeueCondition.broadcast();
mCore->mLastQueuedSlot = slot;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()),
- mCore->mFrameCounter + 1);
+ output->width = mCore->mDefaultWidth;
+ output->height = mCore->mDefaultHeight;
+ output->transformHint = mCore->mTransformHint;
+ 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()));
@@ -916,14 +948,23 @@ status_t BufferQueueProducer::queueBuffer(int slot,
VALIDATE_CONSISTENCY();
} // Autolock scope
- // Don't send the GraphicBuffer through the callback, and don't send
- // the slot number, since the consumer shouldn't need it
- item.mGraphicBuffer.clear();
+ // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because
+ // it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and
+ // there will be no Binder call
+ if (!mConsumerIsSurfaceFlinger) {
+ item.mGraphicBuffer.clear();
+ }
+
+ // Don't send the slot number through the callback since the consumer shouldn't need it
item.mSlot = BufferItem::INVALID_BUFFER_SLOT;
// Call back without the main BufferQueue lock held, but with the callback
// lock held so we can ensure that callbacks occur in order
- {
+
+ int connectedApi;
+ sp<Fence> lastQueuedFence;
+
+ { // scope for the lock
Mutex::Autolock lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
mCallbackCondition.wait(mCallbackMutex);
@@ -935,20 +976,35 @@ status_t BufferQueueProducer::queueBuffer(int slot,
frameReplacedListener->onFrameReplaced(item);
}
+ connectedApi = mCore->mConnectedApi;
+ lastQueuedFence = std::move(mLastQueueBufferFence);
+
+ mLastQueueBufferFence = std::move(acquireFence);
+ mLastQueuedCrop = item.mCrop;
+ mLastQueuedTransform = item.mTransform;
+
++mCurrentCallbackTicket;
mCallbackCondition.broadcast();
}
// Wait without lock held
- if (mCore->mConnectedApi == NATIVE_WINDOW_API_EGL) {
+ if (connectedApi == NATIVE_WINDOW_API_EGL) {
// Waiting here allows for two full buffers to be queued but not a
// third. In the event that frames take varying time, this makes a
// small trade-off in favor of latency rather than throughput.
- mLastQueueBufferFence->waitForever("Throttling EGL Production");
+ lastQueuedFence->waitForever("Throttling EGL Production");
}
- mLastQueueBufferFence = fence;
- mLastQueuedCrop = item.mCrop;
- mLastQueuedTransform = item.mTransform;
+
+ // Update and get FrameEventHistory.
+ nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ NewFrameEventsEntry newFrameEventsEntry = {
+ currentFrameNumber,
+ postedTime,
+ requestedPresentTimestamp,
+ std::move(acquireFenceTime)
+ };
+ addAndGetFrameTimestamps(&newFrameEventsEntry,
+ getFrameTimestamps ? &output->frameTimestamps : nullptr);
return NO_ERROR;
}
@@ -1032,6 +1088,10 @@ int BufferQueueProducer::query(int what, int *outValue) {
case NATIVE_WINDOW_FORMAT:
value = static_cast<int32_t>(mCore->mDefaultBufferFormat);
break;
+ case NATIVE_WINDOW_LAYER_COUNT:
+ // All BufferQueue buffers have a single layer.
+ value = BQ_LAYER_COUNT;
+ break;
case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
value = mCore->getMinUndequeuedBufferCountLocked();
break;
@@ -1054,6 +1114,9 @@ int BufferQueueProducer::query(int what, int *outValue) {
value = static_cast<int32_t>(mCore->mBufferAge);
}
break;
+ case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
+ value = static_cast<int32_t>(mCore->mConsumerIsProtected);
+ break;
default:
return BAD_VALUE;
}
@@ -1110,10 +1173,14 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
case NATIVE_WINDOW_API_MEDIA:
case NATIVE_WINDOW_API_CAMERA:
mCore->mConnectedApi = api;
- output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
- mCore->mTransformHint,
- static_cast<uint32_t>(mCore->mQueue.size()),
- mCore->mFrameCounter + 1);
+
+ output->width = mCore->mDefaultWidth;
+ output->height = mCore->mDefaultHeight;
+ output->transformHint = mCore->mTransformHint;
+ output->numPendingBuffers =
+ static_cast<uint32_t>(mCore->mQueue.size());
+ output->nextFrameNumber = mCore->mFrameCounter + 1;
+ output->bufferReplaced = false;
if (listener != NULL) {
// Set up a death notification so that we can disconnect
@@ -1175,6 +1242,9 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) {
}
if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) {
+ if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) {
+ ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode);
+ }
api = mCore->mConnectedApi;
// If we're asked to disconnect the currently connected api but
// nobody is connected, it's not really an error.
@@ -1209,7 +1279,10 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) {
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener;
- } else if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+ } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("disconnect: not connected (req=%d)", api);
+ status = NO_INIT;
+ } else {
BQ_LOGE("disconnect: still connected to another API "
"(cur=%d req=%d)", mCore->mConnectedApi, api);
status = BAD_VALUE;
@@ -1225,6 +1298,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) {
// Call back without lock held
if (listener != NULL) {
listener->onBuffersReleased();
+ listener->onDisconnect();
}
return status;
@@ -1278,10 +1352,12 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
Vector<sp<GraphicBuffer>> buffers;
for (size_t i = 0; i < newBufferCount; ++i) {
- status_t result = NO_ERROR;
- sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
- allocWidth, allocHeight, allocFormat, allocUsage,
- {mConsumerName.string(), mConsumerName.size()}, &result));
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+ allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+ allocUsage, {mConsumerName.string(), mConsumerName.size()});
+
+ status_t result = graphicBuffer->initCheck();
+
if (result != NO_ERROR) {
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
" %u, usage %u)", width, height, format, usage);
@@ -1432,20 +1508,27 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
return NO_ERROR;
}
-bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
+void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ addAndGetFrameTimestamps(nullptr, outDelta);
+}
+
+void BufferQueueProducer::addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) {
+ if (newTimestamps == nullptr && outDelta == nullptr) {
+ return;
+ }
+
ATRACE_CALL();
- BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
+ BQ_LOGV("addAndGetFrameTimestamps");
sp<IConsumerListener> listener;
-
{
Mutex::Autolock lock(mCore->mMutex);
listener = mCore->mConsumerListener;
}
if (listener != NULL) {
- return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ listener->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
- return false;
}
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 3cf3078345..c2b10a91dd 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -27,8 +27,9 @@
#include <hardware/hardware.h>
+#include <cutils/atomic.h>
+
#include <gui/BufferItem.h>
-#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/ConsumerBase.h>
@@ -56,7 +57,8 @@ static int32_t createProcessUniqueId() {
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
mAbandoned(false),
- mConsumer(bufferQueue) {
+ mConsumer(bufferQueue),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
@@ -251,7 +253,18 @@ status_t ConsumerBase::discardFreeBuffers() {
CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!");
return NO_INIT;
}
- return mConsumer->discardFreeBuffers();
+ status_t err = mConsumer->discardFreeBuffers();
+ if (err != OK) {
+ return err;
+ }
+ uint64_t mask;
+ mConsumer->getReleasedBuffers(&mask);
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1ULL << i)) {
+ freeBufferLocked(i);
+ }
+ }
+ return OK;
}
void ConsumerBase::dumpState(String8& result) const {
@@ -267,7 +280,9 @@ void ConsumerBase::dumpLocked(String8& result, const char* prefix) const {
result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
if (!mAbandoned) {
- mConsumer->dumpState(result, prefix);
+ String8 consumerState;
+ mConsumer->dumpState(String8(prefix), &consumerState);
+ result.append(consumerState);
}
}
@@ -284,6 +299,9 @@ status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
}
if (item->mGraphicBuffer != NULL) {
+ if (mSlots[item->mSlot].mGraphicBuffer != NULL) {
+ freeBufferLocked(item->mSlot);
+ }
mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;
}
@@ -317,16 +335,16 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot,
return OK;
}
- auto signaled = mSlots[slot].mFence->hasSignaled();
+ auto status = mSlots[slot].mFence->getStatus();
- if (!signaled) {
+ if (status == Fence::Status::Invalid) {
CB_LOGE("fence has invalid state");
return BAD_VALUE;
}
- if (*signaled) {
+ if (status == Fence::Status::Signaled) {
mSlots[slot].mFence = fence;
- } else {
+ } else { // status == Fence::Status::Unsignaled
char fenceName[32] = {};
snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
sp<Fence> mergedFence = Fence::merge(
@@ -366,6 +384,7 @@ status_t ConsumerBase::releaseBufferLocked(
freeBufferLocked(slot);
}
+ mPrevFinalReleaseFence = mSlots[slot].mFence;
mSlots[slot].mFence = Fence::NO_FENCE;
return err;
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 839316021e..ae7c65c441 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -64,6 +64,8 @@ static bool isPossiblyYUV(PixelFormat format) {
switch (static_cast<int>(format)) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
+ case HAL_PIXEL_FORMAT_RGBA_1010102:
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_BGRA_8888:
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 9973e8daef..1757ec1cd3 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -18,25 +18,27 @@
#include <utils/Errors.h>
-#include <gui/BitTube.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
+#include <private/gui/BitTube.h>
+
// ---------------------------------------------------------------------------
namespace android {
// ---------------------------------------------------------------------------
-DisplayEventReceiver::DisplayEventReceiver() {
+DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != NULL) {
- mEventConnection = sf->createDisplayEventConnection();
+ mEventConnection = sf->createDisplayEventConnection(vsyncSource);
if (mEventConnection != NULL) {
- mDataChannel = mEventConnection->getDataChannel();
+ mDataChannel = std::make_unique<gui::BitTube>();
+ mEventConnection->stealReceiveChannel(mDataChannel.get());
}
}
}
@@ -79,19 +81,19 @@ status_t DisplayEventReceiver::requestNextVsync() {
ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
size_t count) {
- return DisplayEventReceiver::getEvents(mDataChannel, events, count);
+ return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
}
-ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel,
+ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel,
Event* events, size_t count)
{
- return BitTube::recvObjects(dataChannel, events, count);
+ return gui::BitTube::recvObjects(dataChannel, events, count);
}
-ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
+ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
Event const* events, size_t count)
{
- return BitTube::sendObjects(dataChannel, events, count);
+ return gui::BitTube::sendObjects(dataChannel, events, count);
}
// ---------------------------------------------------------------------------
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000000..fccca97f54
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,701 @@
+/*
+* Copyright 2016 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 <gui/FrameTimestamps.h>
+
+#define LOG_TAG "FrameEvents"
+
+#include <cutils/compiler.h> // For CC_[UN]LIKELY
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <algorithm>
+#include <limits>
+#include <numeric>
+
+namespace android {
+
+
+// ============================================================================
+// FrameEvents
+// ============================================================================
+
+bool FrameEvents::hasPostedInfo() const {
+ return FrameEvents::isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+ return FrameEvents::isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+ return FrameEvents::isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+ return FrameEvents::isValidTimestamp(firstRefreshStartTime);
+}
+
+bool FrameEvents::hasLastRefreshStartInfo() const {
+ // The last refresh start time may continue to update until a new frame
+ // is latched. We know we have the final value once the release info is set.
+ return addReleaseCalled;
+}
+
+bool FrameEvents::hasDequeueReadyInfo() const {
+ return FrameEvents::isValidTimestamp(dequeueReadyTime);
+}
+
+bool FrameEvents::hasAcquireInfo() const {
+ return acquireFence->isValid();
+}
+
+bool FrameEvents::hasGpuCompositionDoneInfo() const {
+ // We may not get a gpuCompositionDone in addPostComposite if
+ // client/gles compositing isn't needed.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayPresentInfo() const {
+ // We may not get a displayPresent in addPostComposite for HWC1.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasReleaseInfo() const {
+ return addReleaseCalled;
+}
+
+void FrameEvents::checkFencesForCompletion() {
+ acquireFence->getSignalTime();
+ gpuCompositionDoneFence->getSignalTime();
+ displayPresentFence->getSignalTime();
+ releaseFence->getSignalTime();
+}
+
+static void dumpFenceTime(String8& outString, const char* name,
+ bool pending, const FenceTime& fenceTime) {
+ outString.appendFormat("--- %s", name);
+ nsecs_t signalTime = fenceTime.getCachedSignalTime();
+ if (Fence::isValidTimestamp(signalTime)) {
+ outString.appendFormat("%" PRId64 "\n", signalTime);
+ } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) {
+ outString.appendFormat("Pending\n");
+ } else if (&fenceTime == FenceTime::NO_FENCE.get()){
+ outString.appendFormat("N/A\n");
+ } else {
+ outString.appendFormat("Error\n");
+ }
+}
+
+void FrameEvents::dump(String8& outString) const
+{
+ if (!valid) {
+ return;
+ }
+
+ outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
+ outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime);
+ outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
+
+ outString.appendFormat("--- Latched \t");
+ if (FrameEvents::isValidTimestamp(latchTime)) {
+ outString.appendFormat("%" PRId64 "\n", latchTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (First)\t");
+ if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (Last)\t");
+ if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ dumpFenceTime(outString, "Acquire \t",
+ true, *acquireFence);
+ dumpFenceTime(outString, "GPU Composite Done\t",
+ !addPostCompositeCalled, *gpuCompositionDoneFence);
+ dumpFenceTime(outString, "Display Present \t",
+ !addPostCompositeCalled, *displayPresentFence);
+
+ outString.appendFormat("--- DequeueReady \t");
+ if (FrameEvents::isValidTimestamp(dequeueReadyTime)) {
+ outString.appendFormat("%" PRId64 "\n", dequeueReadyTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ dumpFenceTime(outString, "Release \t",
+ true, *releaseFence);
+}
+
+
+// ============================================================================
+// FrameEventHistory
+// ============================================================================
+
+namespace {
+
+struct FrameNumberEqual {
+ FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {}
+ bool operator()(const FrameEvents& frame) {
+ return frame.valid && mFrameNumber == frame.frameNumber;
+ }
+ const uint64_t mFrameNumber;
+};
+
+} // namespace
+
+FrameEventHistory::~FrameEventHistory() = default;
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
+ auto frame = std::find_if(
+ mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber));
+ return frame == mFrames.end() ? nullptr : &(*frame);
+}
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) {
+ *iHint = std::min(*iHint, mFrames.size());
+ auto hint = mFrames.begin() + *iHint;
+ auto frame = std::find_if(
+ hint, mFrames.end(), FrameNumberEqual(frameNumber));
+ if (frame == mFrames.end()) {
+ frame = std::find_if(
+ mFrames.begin(), hint, FrameNumberEqual(frameNumber));
+ if (frame == hint) {
+ return nullptr;
+ }
+ }
+ *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+ return &(*frame);
+}
+
+void FrameEventHistory::checkFencesForCompletion() {
+ for (auto& frame : mFrames) {
+ frame.checkFencesForCompletion();
+ }
+}
+
+// Uses !|valid| as the MSB.
+static bool FrameNumberLessThan(
+ const FrameEvents& lhs, const FrameEvents& rhs) {
+ if (lhs.valid == rhs.valid) {
+ return lhs.frameNumber < rhs.frameNumber;
+ }
+ return lhs.valid;
+}
+
+void FrameEventHistory::dump(String8& outString) const {
+ auto earliestFrame = std::min_element(
+ mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+ if (!earliestFrame->valid) {
+ outString.appendFormat("-- N/A\n");
+ return;
+ }
+ for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+ frame->dump(outString);
+ }
+ for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+ frame->dump(outString);
+ }
+}
+
+
+// ============================================================================
+// ProducerFrameEventHistory
+// ============================================================================
+
+ProducerFrameEventHistory::~ProducerFrameEventHistory() = default;
+
+nsecs_t ProducerFrameEventHistory::snapToNextTick(
+ nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) {
+ nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval;
+ // Integer modulo rounds towards 0 and not -inf before taking the remainder,
+ // so adjust the offset if it is negative.
+ if (tickOffset < 0) {
+ tickOffset += tickInterval;
+ }
+ return timestamp + tickOffset;
+}
+
+nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline(
+ const nsecs_t now) const{
+ return snapToNextTick(
+ now, mCompositorTiming.deadline, mCompositorTiming.interval);
+}
+
+void ProducerFrameEventHistory::updateAcquireFence(
+ uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) {
+ FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
+ if (frame == nullptr) {
+ ALOGE("updateAcquireFence: Did not find frame.");
+ return;
+ }
+
+ if (acquire->isValid()) {
+ mAcquireTimeline.push(acquire);
+ frame->acquireFence = std::move(acquire);
+ } else {
+ // If there isn't an acquire fence, assume that buffer was
+ // ready for the consumer when posted.
+ frame->acquireFence = std::make_shared<FenceTime>(frame->postedTime);
+ }
+}
+
+void ProducerFrameEventHistory::applyDelta(
+ const FrameEventHistoryDelta& delta) {
+ mCompositorTiming = delta.mCompositorTiming;
+
+ for (auto& d : delta.mDeltas) {
+ // Avoid out-of-bounds access.
+ if (CC_UNLIKELY(d.mIndex >= mFrames.size())) {
+ ALOGE("applyDelta: Bad index.");
+ return;
+ }
+
+ FrameEvents& frame = mFrames[d.mIndex];
+
+ frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
+ frame.addReleaseCalled = d.mAddReleaseCalled != 0;
+
+ frame.postedTime = d.mPostedTime;
+ frame.requestedPresentTime = d.mRequestedPresentTime;
+ frame.latchTime = d.mLatchTime;
+ frame.firstRefreshStartTime = d.mFirstRefreshStartTime;
+ frame.lastRefreshStartTime = d.mLastRefreshStartTime;
+ frame.dequeueReadyTime = d.mDequeueReadyTime;
+
+ if (frame.frameNumber != d.mFrameNumber) {
+ // We got a new frame. Initialize some of the fields.
+ frame.frameNumber = d.mFrameNumber;
+ frame.acquireFence = FenceTime::NO_FENCE;
+ frame.gpuCompositionDoneFence = FenceTime::NO_FENCE;
+ frame.displayPresentFence = FenceTime::NO_FENCE;
+ frame.releaseFence = FenceTime::NO_FENCE;
+ // The consumer only sends valid frames.
+ frame.valid = true;
+ }
+
+ applyFenceDelta(&mGpuCompositionDoneTimeline,
+ &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence);
+ applyFenceDelta(&mPresentTimeline,
+ &frame.displayPresentFence, d.mDisplayPresentFence);
+ applyFenceDelta(&mReleaseTimeline,
+ &frame.releaseFence, d.mReleaseFence);
+ }
+}
+
+void ProducerFrameEventHistory::updateSignalTimes() {
+ mAcquireTimeline.updateSignalTimes();
+ mGpuCompositionDoneTimeline.updateSignalTimes();
+ mPresentTimeline.updateSignalTimes();
+ mReleaseTimeline.updateSignalTimes();
+}
+
+void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline,
+ std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) const {
+ if (CC_UNLIKELY(dst == nullptr || dst->get() == nullptr)) {
+ ALOGE("applyFenceDelta: dst is null.");
+ return;
+ }
+
+ switch (src.state) {
+ case FenceTime::Snapshot::State::EMPTY:
+ return;
+ case FenceTime::Snapshot::State::FENCE:
+ ALOGE_IF((*dst)->isValid(), "applyFenceDelta: Unexpected fence.");
+ *dst = createFenceTime(src.fence);
+ timeline->push(*dst);
+ return;
+ case FenceTime::Snapshot::State::SIGNAL_TIME:
+ if ((*dst)->isValid()) {
+ (*dst)->applyTrustedSnapshot(src);
+ } else {
+ *dst = std::make_shared<FenceTime>(src.signalTime);
+ }
+ return;
+ }
+}
+
+std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime(
+ const sp<Fence>& fence) const {
+ return std::make_shared<FenceTime>(fence);
+}
+
+
+// ============================================================================
+// ConsumerFrameEventHistory
+// ============================================================================
+
+ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
+
+void ConsumerFrameEventHistory::onDisconnect() {
+ mCurrentConnectId++;
+ mProducerWantsEvents = false;
+}
+
+void ConsumerFrameEventHistory::initializeCompositorTiming(
+ const CompositorTiming& compositorTiming) {
+ mCompositorTiming = compositorTiming;
+}
+
+void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) {
+ // Overwrite all fields of the frame with default values unless set here.
+ FrameEvents newTimestamps;
+ newTimestamps.connectId = mCurrentConnectId;
+ newTimestamps.frameNumber = newEntry.frameNumber;
+ newTimestamps.postedTime = newEntry.postedTime;
+ newTimestamps.requestedPresentTime = newEntry.requestedPresentTime;
+ newTimestamps.acquireFence = newEntry.acquireFence;
+ newTimestamps.valid = true;
+ mFrames[mQueueOffset] = newTimestamps;
+
+ // Note: We avoid sending the acquire fence back to the caller since
+ // they have the original one already, so there is no need to set the
+ // acquire dirty bit.
+ mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>();
+
+ mQueueOffset = (mQueueOffset + 1) % mFrames.size();
+}
+
+void ConsumerFrameEventHistory::addLatch(
+ uint64_t frameNumber, nsecs_t latchTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents, "addLatch: Did not find frame.");
+ return;
+ }
+ frame->latchTime = latchTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>();
+}
+
+void ConsumerFrameEventHistory::addPreComposition(
+ uint64_t frameNumber, nsecs_t refreshStartTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents,
+ "addPreComposition: Did not find frame.");
+ return;
+ }
+ frame->lastRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
+ if (!FrameEvents::isValidTimestamp(frame->firstRefreshStartTime)) {
+ frame->firstRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
+ }
+}
+
+void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
+ const std::shared_ptr<FenceTime>& gpuCompositionDone,
+ const std::shared_ptr<FenceTime>& displayPresent,
+ const CompositorTiming& compositorTiming) {
+ mCompositorTiming = compositorTiming;
+
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents,
+ "addPostComposition: Did not find frame.");
+ return;
+ }
+ // Only get GPU and present info for the first composite.
+ if (!frame->addPostCompositeCalled) {
+ frame->addPostCompositeCalled = true;
+ frame->gpuCompositionDoneFence = gpuCompositionDone;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GPU_COMPOSITION_DONE>();
+ if (!frame->displayPresentFence->isValid()) {
+ frame->displayPresentFence = displayPresent;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
+ }
+ }
+}
+
+void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber,
+ nsecs_t dequeueReadyTime, std::shared_ptr<FenceTime>&& release) {
+ FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
+ if (frame == nullptr) {
+ ALOGE_IF(mProducerWantsEvents, "addRelease: Did not find frame.");
+ return;
+ }
+ frame->addReleaseCalled = true;
+ frame->dequeueReadyTime = dequeueReadyTime;
+ frame->releaseFence = std::move(release);
+ mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
+}
+
+void ConsumerFrameEventHistory::getFrameDelta(
+ FrameEventHistoryDelta* delta,
+ const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) {
+ mProducerWantsEvents = true;
+ size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+ if (mFramesDirty[i].anyDirty()) {
+ // Make sure only to send back deltas for the current connection
+ // since the producer won't have the correct state to apply a delta
+ // from a previous connection.
+ if (mFrames[i].connectId == mCurrentConnectId) {
+ delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]);
+ }
+ mFramesDirty[i].reset();
+ }
+}
+
+void ConsumerFrameEventHistory::getAndResetDelta(
+ FrameEventHistoryDelta* delta) {
+ mProducerWantsEvents = true;
+ delta->mCompositorTiming = mCompositorTiming;
+
+ // Write these in order of frame number so that it is easy to
+ // add them to a FenceTimeline in the proper order producer side.
+ delta->mDeltas.reserve(mFramesDirty.size());
+ auto earliestFrame = std::min_element(
+ mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+ for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+ getFrameDelta(delta, frame);
+ }
+ for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+ getFrameDelta(delta, frame);
+ }
+}
+
+
+// ============================================================================
+// FrameEventsDelta
+// ============================================================================
+
+FrameEventsDelta::FrameEventsDelta(
+ size_t index,
+ const FrameEvents& frameTimestamps,
+ const FrameEventDirtyFields& dirtyFields)
+ : mIndex(index),
+ mFrameNumber(frameTimestamps.frameNumber),
+ mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled),
+ mAddReleaseCalled(frameTimestamps.addReleaseCalled),
+ mPostedTime(frameTimestamps.postedTime),
+ mRequestedPresentTime(frameTimestamps.requestedPresentTime),
+ mLatchTime(frameTimestamps.latchTime),
+ mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime),
+ mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime),
+ mDequeueReadyTime(frameTimestamps.dequeueReadyTime) {
+ if (dirtyFields.isDirty<FrameEvent::GPU_COMPOSITION_DONE>()) {
+ mGpuCompositionDoneFence =
+ frameTimestamps.gpuCompositionDoneFence->getSnapshot();
+ }
+ if (dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>()) {
+ mDisplayPresentFence =
+ frameTimestamps.displayPresentFence->getSnapshot();
+ }
+ if (dirtyFields.isDirty<FrameEvent::RELEASE>()) {
+ mReleaseFence = frameTimestamps.releaseFence->getSnapshot();
+ }
+}
+
+constexpr size_t FrameEventsDelta::minFlattenedSize() {
+ return sizeof(FrameEventsDelta::mFrameNumber) +
+ sizeof(uint16_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(FrameEventsDelta::mPostedTime) +
+ sizeof(FrameEventsDelta::mRequestedPresentTime) +
+ sizeof(FrameEventsDelta::mLatchTime) +
+ sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
+ sizeof(FrameEventsDelta::mLastRefreshStartTime) +
+ sizeof(FrameEventsDelta::mDequeueReadyTime);
+}
+
+// Flattenable implementation
+size_t FrameEventsDelta::getFlattenedSize() const {
+ auto fences = allFences(this);
+ return minFlattenedSize() +
+ std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const FenceTime::Snapshot* fence) {
+ return a + fence->getFlattenedSize();
+ });
+}
+
+size_t FrameEventsDelta::getFdCount() const {
+ auto fences = allFences(this);
+ return std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const FenceTime::Snapshot* fence) {
+ return a + fence->getFdCount();
+ });
+}
+
+status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const {
+ if (size < getFlattenedSize() || count < getFdCount()) {
+ return NO_MEMORY;
+ }
+
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
+ mIndex > std::numeric_limits<uint16_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, mFrameNumber);
+
+ // These are static_cast to uint16_t/uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint16_t>(mIndex));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, mPostedTime);
+ FlattenableUtils::write(buffer, size, mRequestedPresentTime);
+ FlattenableUtils::write(buffer, size, mLatchTime);
+ FlattenableUtils::write(buffer, size, mFirstRefreshStartTime);
+ FlattenableUtils::write(buffer, size, mLastRefreshStartTime);
+ FlattenableUtils::write(buffer, size, mDequeueReadyTime);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ status_t status = fence->flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size,
+ int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mFrameNumber);
+
+ // These were written as uint16_t/uint8_t for alignment.
+ uint16_t temp16 = 0;
+ FlattenableUtils::read(buffer, size, temp16);
+ mIndex = temp16;
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ uint8_t temp8 = 0;
+ FlattenableUtils::read(buffer, size, temp8);
+ mAddPostCompositeCalled = static_cast<bool>(temp8);
+ FlattenableUtils::read(buffer, size, temp8);
+ mAddReleaseCalled = static_cast<bool>(temp8);
+
+ FlattenableUtils::read(buffer, size, mPostedTime);
+ FlattenableUtils::read(buffer, size, mRequestedPresentTime);
+ FlattenableUtils::read(buffer, size, mLatchTime);
+ FlattenableUtils::read(buffer, size, mFirstRefreshStartTime);
+ FlattenableUtils::read(buffer, size, mLastRefreshStartTime);
+ FlattenableUtils::read(buffer, size, mDequeueReadyTime);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ status_t status = fence->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+// ============================================================================
+// FrameEventHistoryDelta
+// ============================================================================
+
+FrameEventHistoryDelta& FrameEventHistoryDelta::operator=(
+ FrameEventHistoryDelta&& src) {
+ mCompositorTiming = src.mCompositorTiming;
+
+ if (CC_UNLIKELY(!mDeltas.empty())) {
+ ALOGE("FrameEventHistoryDelta assign clobbering history.");
+ }
+ mDeltas = std::move(src.mDeltas);
+ ALOGE_IF(src.mDeltas.empty(), "Source mDeltas not empty.");
+ return *this;
+}
+
+constexpr size_t FrameEventHistoryDelta::minFlattenedSize() {
+ return sizeof(uint32_t) + // mDeltas.size()
+ sizeof(mCompositorTiming);
+}
+
+size_t FrameEventHistoryDelta::getFlattenedSize() const {
+ return minFlattenedSize() +
+ std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFlattenedSize();
+ });
+}
+
+size_t FrameEventHistoryDelta::getFdCount() const {
+ return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFdCount();
+ });
+}
+
+status_t FrameEventHistoryDelta::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, mCompositorTiming);
+
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint32_t>(mDeltas.size()));
+ for (auto& d : mDeltas) {
+ status_t status = d.flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventHistoryDelta::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mCompositorTiming);
+
+ uint32_t deltaCount = 0;
+ FlattenableUtils::read(buffer, size, deltaCount);
+ if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ mDeltas.resize(deltaCount);
+ for (auto& d : mDeltas) {
+ status_t status = d.unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+} // namespace android
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 10e999c228..c654f083b3 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -31,7 +31,6 @@
#include <gui/BufferItem.h>
#include <gui/GLConsumer.h>
-#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
@@ -157,6 +156,7 @@ GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mCurrentFence(Fence::NO_FENCE),
mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
mCurrentFrameNumber(0),
mDefaultWidth(1),
mDefaultHeight(1),
@@ -185,6 +185,7 @@ GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mCurrentFence(Fence::NO_FENCE),
mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
mCurrentFrameNumber(0),
mDefaultWidth(1),
mDefaultHeight(1),
@@ -321,7 +322,9 @@ status_t GLConsumer::releaseTexImage() {
mCurrentCrop.makeInvalid();
mCurrentTransform = 0;
mCurrentTimestamp = 0;
+ mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
mCurrentFence = Fence::NO_FENCE;
+ mCurrentFenceTime = FenceTime::NO_FENCE;
if (mAttached) {
// This binds a dummy buffer (mReleasedTexImage).
@@ -487,7 +490,9 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item,
mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode;
mCurrentTimestamp = item.mTimestamp;
+ mCurrentDataSpace = item.mDataSpace;
mCurrentFence = item.mFence;
+ mCurrentFenceTime = item.mFenceTime;
mCurrentFrameNumber = item.mFrameNumber;
computeCurrentTransformMatrixLocked();
@@ -856,6 +861,8 @@ void GLConsumer::computeTransformMatrix(float outTransform[16],
switch (buf->getPixelFormat()) {
case PIXEL_FORMAT_RGBA_8888:
case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
case PIXEL_FORMAT_RGB_888:
case PIXEL_FORMAT_RGB_565:
case PIXEL_FORMAT_BGRA_8888:
@@ -911,15 +918,26 @@ nsecs_t GLConsumer::getTimestamp() {
return mCurrentTimestamp;
}
+android_dataspace GLConsumer::getCurrentDataSpace() {
+ GLC_LOGV("getCurrentDataSpace");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentDataSpace;
+}
+
uint64_t GLConsumer::getFrameNumber() {
GLC_LOGV("getFrameNumber");
Mutex::Autolock lock(mMutex);
return mCurrentFrameNumber;
}
-sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
+sp<GraphicBuffer> GLConsumer::getCurrentBuffer(int* outSlot) const {
Mutex::Autolock lock(mMutex);
- return (mCurrentTextureImage == NULL) ?
+
+ if (outSlot != nullptr) {
+ *outSlot = mCurrentTexture;
+ }
+
+ return (mCurrentTextureImage == nullptr) ?
NULL : mCurrentTextureImage->graphicBuffer();
}
@@ -981,6 +999,11 @@ sp<Fence> GLConsumer::getCurrentFence() const {
return mCurrentFence;
}
+std::shared_ptr<FenceTime> GLConsumer::getCurrentFenceTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFenceTime;
+}
+
status_t GLConsumer::doGLFenceWait() const {
Mutex::Autolock lock(mMutex);
return doGLFenceWaitLocked();
diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp
deleted file mode 100644
index a8f40e0e8b..0000000000
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- **
- ** Copyright 2012 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 <log/log.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <gui/GraphicBufferAlloc.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-GraphicBufferAlloc::GraphicBufferAlloc() {
-}
-
-GraphicBufferAlloc::~GraphicBufferAlloc() {
-}
-
-sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage,
- std::string requestorName, status_t* error) {
- sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
- width, height, format, usage, std::move(requestorName)));
- status_t err = graphicBuffer->initCheck();
- *error = err;
- if (err != 0 || graphicBuffer->handle == 0) {
- if (err == NO_MEMORY) {
- GraphicBuffer::dumpAllocationsToSystemLog();
- }
- ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
- "failed (%s), handle=%p",
- width, height, strerror(-err), graphicBuffer->handle);
- return 0;
- }
- return graphicBuffer;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index ff7b83a7d6..85ac304ab8 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -14,147 +14,86 @@
* limitations under the License.
*/
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
#include <gui/IConsumerListener.h>
+
#include <gui/BufferItem.h>
-// ---------------------------------------------------------------------------
namespace android {
-// ---------------------------------------------------------------------------
-enum {
- ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION,
- ON_BUFFER_RELEASED,
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
+ ON_FRAME_AVAILABLE,
+ ON_FRAME_REPLACED,
+ ON_BUFFERS_RELEASED,
ON_SIDEBAND_STREAM_CHANGED,
- GET_FRAME_TIMESTAMPS
+ LAST = ON_SIDEBAND_STREAM_CHANGED,
};
-class BpConsumerListener : public BpInterface<IConsumerListener>
-{
+} // Anonymous namespace
+
+class BpConsumerListener : public SafeBpInterface<IConsumerListener> {
public:
explicit BpConsumerListener(const sp<IBinder>& impl)
- : BpInterface<IConsumerListener>(impl) {
+ : SafeBpInterface<IConsumerListener>(impl, "BpConsumerListener") {}
+
+ ~BpConsumerListener() override;
+
+ void onDisconnect() override {
+ callRemoteAsync<decltype(&IConsumerListener::onDisconnect)>(Tag::ON_DISCONNECT);
}
- virtual ~BpConsumerListener();
+ void onFrameAvailable(const BufferItem& item) override {
+ callRemoteAsync<decltype(&IConsumerListener::onFrameAvailable)>(Tag::ON_FRAME_AVAILABLE,
+ item);
+ }
- virtual void onFrameAvailable(const BufferItem& item) {
- Parcel data, reply;
- data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
- data.write(item);
- remote()->transact(ON_FRAME_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY);
+ void onFrameReplaced(const BufferItem& item) override {
+ callRemoteAsync<decltype(&IConsumerListener::onFrameReplaced)>(Tag::ON_FRAME_REPLACED,
+ item);
}
- virtual void onBuffersReleased() {
- Parcel data, reply;
- data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
- remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
+ void onBuffersReleased() override {
+ callRemoteAsync<decltype(&IConsumerListener::onBuffersReleased)>(Tag::ON_BUFFERS_RELEASED);
}
- virtual void onSidebandStreamChanged() {
- Parcel data, reply;
- data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
- remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
+ void onSidebandStreamChanged() override {
+ callRemoteAsync<decltype(&IConsumerListener::onSidebandStreamChanged)>(
+ Tag::ON_SIDEBAND_STREAM_CHANGED);
}
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IConsumerListener::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write token: %d", result);
- return false;
- }
- result = data.writeUint64(frameNumber);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write: %d", result);
- return false;
- }
- result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to transact: %d", result);
- return false;
- }
- bool found = false;
- result = reply.readBool(&found);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read: %d", result);
- return false;
- }
- if (found) {
- result = reply.read(*outTimestamps);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read timestamps: %d",
- result);
- return false;
- }
- }
- return found;
+ void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/,
+ FrameEventHistoryDelta* /*outDelta*/) override {
+ LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied");
}
};
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpConsumerListener::~BpConsumerListener() {}
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpConsumerListener::~BpConsumerListener() = default;
+ConsumerListener::~ConsumerListener() = default;
IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");
-// ----------------------------------------------------------------------
-
-status_t BnConsumerListener::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case ON_FRAME_AVAILABLE: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- BufferItem item;
- data.read(item);
- onFrameAvailable(item);
- return NO_ERROR; }
- case ON_BUFFER_RELEASED: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- onBuffersReleased();
- return NO_ERROR; }
- case ON_SIDEBAND_STREAM_CHANGED: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- onSidebandStreamChanged();
- return NO_ERROR; }
- case GET_FRAME_TIMESTAMPS: {
- CHECK_INTERFACE(IConsumerListener, data, reply);
- uint64_t frameNumber = 0;
- status_t result = data.readUint64(&frameNumber);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to read: %d", result);
- return result;
- }
- FrameTimestamps timestamps;
- bool found = getFrameTimestamps(frameNumber, &timestamps);
- result = reply->writeBool(found);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write: %d", result);
- return result;
- }
- if (found) {
- result = reply->write(timestamps);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write timestamps: %d", result);
- return result;
- }
- }
- return NO_ERROR;
- }
+status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ON_DISCONNECT:
+ return callLocalAsync(data, reply, &IConsumerListener::onDisconnect);
+ case Tag::ON_FRAME_AVAILABLE:
+ return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable);
+ case Tag::ON_FRAME_REPLACED:
+ return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced);
+ case Tag::ON_BUFFERS_RELEASED:
+ return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased);
+ case Tag::ON_SIDEBAND_STREAM_CHANGED:
+ return callLocalAsync(data, reply, &IConsumerListener::onSidebandStreamChanged);
}
- return BBinder::onTransact(code, data, reply, flags);
}
-ConsumerListener::~ConsumerListener() = default;
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index b1d3b00dbb..c0e246fa15 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -14,91 +14,67 @@
* limitations under the License.
*/
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
#include <gui/IDisplayEventConnection.h>
-#include <gui/BitTube.h>
+
+#include <private/gui/BitTube.h>
namespace android {
-// ----------------------------------------------------------------------------
-enum {
- GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
SET_VSYNC_RATE,
- REQUEST_NEXT_VSYNC
+ REQUEST_NEXT_VSYNC,
+ LAST = REQUEST_NEXT_VSYNC,
};
-class BpDisplayEventConnection : public BpInterface<IDisplayEventConnection>
-{
+} // Anonymous namespace
+
+class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> {
public:
explicit BpDisplayEventConnection(const sp<IBinder>& impl)
- : BpInterface<IDisplayEventConnection>(impl)
- {
- }
+ : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {}
- virtual ~BpDisplayEventConnection();
+ ~BpDisplayEventConnection() override;
- virtual sp<BitTube> getDataChannel() const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
- remote()->transact(GET_DATA_CHANNEL, data, &reply);
- return new BitTube(reply);
+ status_t stealReceiveChannel(gui::BitTube* outChannel) override {
+ return callRemote<decltype(
+ &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL,
+ outChannel);
}
- virtual void setVsyncRate(uint32_t count) {
- Parcel data, reply;
- data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
- data.writeUint32(count);
- remote()->transact(SET_VSYNC_RATE, data, &reply);
+ status_t setVsyncRate(uint32_t count) override {
+ return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE,
+ count);
}
- virtual void requestNextVsync() {
- Parcel data, reply;
- data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
- remote()->transact(REQUEST_NEXT_VSYNC, data, &reply, IBinder::FLAG_ONEWAY);
+ void requestNextVsync() override {
+ callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
+ Tag::REQUEST_NEXT_VSYNC);
}
};
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpDisplayEventConnection::~BpDisplayEventConnection() {}
+// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpDisplayEventConnection::~BpDisplayEventConnection() = default;
IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
-// ----------------------------------------------------------------------------
-
-status_t BnDisplayEventConnection::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case GET_DATA_CHANNEL: {
- CHECK_INTERFACE(IDisplayEventConnection, data, reply);
- sp<BitTube> channel(getDataChannel());
- channel->writeToParcel(reply);
- return NO_ERROR;
- }
- case SET_VSYNC_RATE: {
- CHECK_INTERFACE(IDisplayEventConnection, data, reply);
- setVsyncRate(data.readUint32());
- return NO_ERROR;
- }
- case REQUEST_NEXT_VSYNC: {
- CHECK_INTERFACE(IDisplayEventConnection, data, reply);
- requestNextVsync();
- return NO_ERROR;
- }
+status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::STEAL_RECEIVE_CHANNEL:
+ return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel);
+ case Tag::SET_VSYNC_RATE:
+ return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
+ case Tag::REQUEST_NEXT_VSYNC:
+ return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
}
- return BBinder::onTransact(code, data, reply, flags);
}
-// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
deleted file mode 100644
index 2fb380ccd1..0000000000
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-// tag as surfaceflinger
-#define LOG_TAG "SurfaceFlinger"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <gui/IGraphicBufferAlloc.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-enum {
- CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
-};
-
-class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc>
-{
-public:
- explicit BpGraphicBufferAlloc(const sp<IBinder>& impl)
- : BpInterface<IGraphicBufferAlloc>(impl)
- {
- }
-
- virtual ~BpGraphicBufferAlloc();
-
- virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage,
- std::string requestorName, status_t* error) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor());
- data.writeUint32(width);
- data.writeUint32(height);
- data.writeInt32(static_cast<int32_t>(format));
- data.writeUint32(usage);
- if (requestorName.empty()) {
- requestorName += "[PID ";
- requestorName += std::to_string(getpid());
- requestorName += ']';
- }
- data.writeUtf8AsUtf16(requestorName);
- remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply);
- sp<GraphicBuffer> graphicBuffer;
- status_t result = reply.readInt32();
- if (result == NO_ERROR) {
- graphicBuffer = new GraphicBuffer();
- result = reply.read(*graphicBuffer);
- if (result != NO_ERROR) {
- graphicBuffer.clear();
- }
- // reply.readStrongBinder();
- // here we don't even have to read the BufferReference from
- // the parcel, it'll die with the parcel.
- }
- *error = result;
- return graphicBuffer;
- }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpGraphicBufferAlloc::~BpGraphicBufferAlloc() {}
-
-IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc");
-
-// ----------------------------------------------------------------------
-
-status_t BnGraphicBufferAlloc::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- // codes that don't require permission check
-
- // BufferReference just keeps a strong reference to a GraphicBuffer until it
- // is destroyed (that is, until no local or remote process have a reference
- // to it).
- class BufferReference : public BBinder {
- sp<GraphicBuffer> mBuffer;
- public:
- explicit BufferReference(const sp<GraphicBuffer>& buffer) : mBuffer(buffer) {}
- };
-
-
- switch (code) {
- case CREATE_GRAPHIC_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
- uint32_t width = data.readUint32();
- uint32_t height = data.readUint32();
- PixelFormat format = static_cast<PixelFormat>(data.readInt32());
- uint32_t usage = data.readUint32();
- status_t error = NO_ERROR;
- std::string requestorName;
- data.readUtf8FromUtf16(&requestorName);
- sp<GraphicBuffer> result = createGraphicBuffer(width, height,
- format, usage, requestorName, &error);
- reply->writeInt32(error);
- if (result != 0) {
- reply->write(*result);
- // We add a BufferReference to this parcel to make sure the
- // buffer stays alive until the GraphicBuffer object on
- // the other side has been created.
- // This is needed so that the buffer handle can be
- // registered before the buffer is destroyed on implementations
- // that do not use file-descriptors to track their buffers.
- reply->writeStrongBinder( new BufferReference(result) );
- }
- return NO_ERROR;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-}; // namespace android
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 240146455e..a573bee651 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -14,27 +14,24 @@
* limitations under the License.
*/
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/NativeHandle.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
+#include <gui/IGraphicBufferConsumer.h>
#include <gui/BufferItem.h>
#include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferConsumer.h>
-#include <ui/GraphicBuffer.h>
+#include <binder/Parcel.h>
+
#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
-#include <system/window.h>
+#include <utils/NativeHandle.h>
+#include <utils/String8.h>
namespace android {
-enum {
+namespace { // Anonymous namespace
+
+enum class Tag : uint32_t {
ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
DETACH_BUFFER,
ATTACH_BUFFER,
@@ -49,443 +46,185 @@ enum {
SET_DEFAULT_BUFFER_FORMAT,
SET_DEFAULT_BUFFER_DATA_SPACE,
SET_CONSUMER_USAGE_BITS,
+ SET_CONSUMER_IS_PROTECTED,
SET_TRANSFORM_HINT,
GET_SIDEBAND_STREAM,
GET_OCCUPANCY_HISTORY,
DISCARD_FREE_BUFFERS,
- DUMP,
+ DUMP_STATE,
+ LAST = DUMP_STATE,
};
+} // Anonymous namespace
-class BpGraphicBufferConsumer : public BpInterface<IGraphicBufferConsumer>
-{
+class BpGraphicBufferConsumer : public SafeBpInterface<IGraphicBufferConsumer> {
public:
explicit BpGraphicBufferConsumer(const sp<IBinder>& impl)
- : BpInterface<IGraphicBufferConsumer>(impl)
- {
+ : SafeBpInterface<IGraphicBufferConsumer>(impl, "BpGraphicBufferConsumer") {}
+
+ ~BpGraphicBufferConsumer() override;
+
+ status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
+ uint64_t maxFrameNumber) override {
+ using Signature = decltype(&IGraphicBufferConsumer::acquireBuffer);
+ return callRemote<Signature>(Tag::ACQUIRE_BUFFER, buffer, presentWhen, maxFrameNumber);
}
- virtual ~BpGraphicBufferConsumer();
-
- virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen,
- uint64_t maxFrameNumber) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt64(presentWhen);
- data.writeUint64(maxFrameNumber);
- status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- result = reply.read(*buffer);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t detachBuffer(int slot) override {
+ using Signature = decltype(&IGraphicBufferConsumer::detachBuffer);
+ return callRemote<Signature>(Tag::DETACH_BUFFER, slot);
}
- virtual status_t detachBuffer(int slot) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(slot);
- status_t result = remote()->transact(DETACH_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- result = reply.readInt32();
- return result;
+ status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) override {
+ using Signature = decltype(&IGraphicBufferConsumer::attachBuffer);
+ return callRemote<Signature>(Tag::ATTACH_BUFFER, slot, buffer);
}
- virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.write(*buffer.get());
- status_t result = remote()->transact(ATTACH_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- *slot = reply.readInt32();
- result = reply.readInt32();
- return result;
+ status_t releaseBuffer(int buf, uint64_t frameNumber,
+ EGLDisplay display __attribute__((unused)),
+ EGLSyncKHR fence __attribute__((unused)),
+ const sp<Fence>& releaseFence) override {
+ return callRemote<ReleaseBuffer>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence);
}
- virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
- EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)),
- const sp<Fence>& releaseFence) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(buf);
- data.writeInt64(static_cast<int64_t>(frameNumber));
- data.write(*releaseFence);
- status_t result = remote()->transact(RELEASE_BUFFER, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override {
+ using Signature = decltype(&IGraphicBufferConsumer::consumerConnect);
+ return callRemote<Signature>(Tag::CONSUMER_CONNECT, consumer, controlledByApp);
}
- virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(consumer));
- data.writeInt32(controlledByApp);
- status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t consumerDisconnect() override {
+ return callRemote<decltype(&IGraphicBufferConsumer::consumerDisconnect)>(
+ Tag::CONSUMER_DISCONNECT);
}
- virtual status_t consumerDisconnect() {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t getReleasedBuffers(uint64_t* slotMask) override {
+ using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffers);
+ return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask);
}
- virtual status_t getReleasedBuffers(uint64_t* slotMask) {
- Parcel data, reply;
- if (slotMask == NULL) {
- ALOGE("getReleasedBuffers: slotMask must not be NULL");
- return BAD_VALUE;
- }
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- *slotMask = static_cast<uint64_t>(reply.readInt64());
- return reply.readInt32();
+ status_t setDefaultBufferSize(uint32_t width, uint32_t height) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize);
+ return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height);
}
- virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeUint32(width);
- data.writeUint32(height);
- status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setMaxBufferCount(int bufferCount) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount);
+ return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount);
}
- virtual status_t setMaxBufferCount(int bufferCount) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(bufferCount);
- status_t result = remote()->transact(SET_MAX_BUFFER_COUNT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setMaxAcquiredBufferCount);
+ return callRemote<Signature>(Tag::SET_MAX_ACQUIRED_BUFFER_COUNT, maxAcquiredBuffers);
}
- virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(maxAcquiredBuffers);
- status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setConsumerName(const String8& name) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setConsumerName);
+ return callRemote<Signature>(Tag::SET_CONSUMER_NAME, name);
}
- virtual void setConsumerName(const String8& name) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeString8(name);
- remote()->transact(SET_CONSUMER_NAME, data, &reply);
+ status_t setDefaultBufferFormat(PixelFormat defaultFormat) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferFormat);
+ return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_FORMAT, defaultFormat);
}
- virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(static_cast<int32_t>(defaultFormat));
- status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferDataSpace);
+ return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_DATA_SPACE, defaultDataSpace);
}
- virtual status_t setDefaultBufferDataSpace(
- android_dataspace defaultDataSpace) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeInt32(static_cast<int32_t>(defaultDataSpace));
- status_t result = remote()->transact(SET_DEFAULT_BUFFER_DATA_SPACE,
- data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setConsumerUsageBits(uint32_t usage) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setConsumerUsageBits);
+ return callRemote<Signature>(Tag::SET_CONSUMER_USAGE_BITS, usage);
}
- virtual status_t setConsumerUsageBits(uint32_t usage) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeUint32(usage);
- status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setConsumerIsProtected(bool isProtected) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setConsumerIsProtected);
+ return callRemote<Signature>(Tag::SET_CONSUMER_IS_PROTECTED, isProtected);
}
- virtual status_t setTransformHint(uint32_t hint) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeUint32(hint);
- status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- return reply.readInt32();
+ status_t setTransformHint(uint32_t hint) override {
+ using Signature = decltype(&IGraphicBufferConsumer::setTransformHint);
+ return callRemote<Signature>(Tag::SET_TRANSFORM_HINT, hint);
}
- virtual sp<NativeHandle> getSidebandStream() const {
- Parcel data, reply;
- status_t err;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- if ((err = remote()->transact(GET_SIDEBAND_STREAM, data, &reply)) != NO_ERROR) {
- return NULL;
- }
- sp<NativeHandle> stream;
- if (reply.readInt32()) {
- stream = NativeHandle::create(reply.readNativeHandle(), true);
- }
- return stream;
+ status_t getSidebandStream(sp<NativeHandle>* outStream) const override {
+ using Signature = decltype(&IGraphicBufferConsumer::getSidebandStream);
+ return callRemote<Signature>(Tag::GET_SIDEBAND_STREAM, outStream);
}
- virtual status_t getOccupancyHistory(bool forceFlush,
- std::vector<OccupancyTracker::Segment>* outHistory) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- status_t error = data.writeBool(forceFlush);
- if (error != NO_ERROR) {
- return error;
- }
- error = remote()->transact(GET_OCCUPANCY_HISTORY, data,
- &reply);
- if (error != NO_ERROR) {
- return error;
- }
- error = reply.readParcelableVector(outHistory);
- if (error != NO_ERROR) {
- return error;
- }
- status_t result = NO_ERROR;
- error = reply.readInt32(&result);
- if (error != NO_ERROR) {
- return error;
- }
- return result;
+ status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) override {
+ using Signature = decltype(&IGraphicBufferConsumer::getOccupancyHistory);
+ return callRemote<Signature>(Tag::GET_OCCUPANCY_HISTORY, forceFlush, outHistory);
}
- virtual status_t discardFreeBuffers() {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply);
- if (error != NO_ERROR) {
- return error;
- }
- int32_t result = NO_ERROR;
- error = reply.readInt32(&result);
- if (error != NO_ERROR) {
- return error;
- }
- return result;
+ status_t discardFreeBuffers() override {
+ return callRemote<decltype(&IGraphicBufferConsumer::discardFreeBuffers)>(
+ Tag::DISCARD_FREE_BUFFERS);
}
- virtual void dumpState(String8& result, const char* prefix) const {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
- data.writeString8(result);
- data.writeString8(String8(prefix ? prefix : ""));
- remote()->transact(DUMP, data, &reply);
- reply.readString8();
+ status_t dumpState(const String8& prefix, String8* outResult) const override {
+ using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
+ return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult);
}
};
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpGraphicBufferConsumer::~BpGraphicBufferConsumer() {}
+// Out-of-line virtual method definition to trigger vtable emission in this translation unit
+// (see clang warning -Wweak-vtables)
+BpGraphicBufferConsumer::~BpGraphicBufferConsumer() = default;
IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer");
-// ----------------------------------------------------------------------
-
-status_t BnGraphicBufferConsumer::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case ACQUIRE_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- BufferItem item;
- int64_t presentWhen = data.readInt64();
- uint64_t maxFrameNumber = data.readUint64();
- status_t result = acquireBuffer(&item, presentWhen, maxFrameNumber);
- status_t err = reply->write(item);
- if (err) return err;
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case DETACH_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- int slot = data.readInt32();
- int result = detachBuffer(slot);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case ATTACH_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- sp<GraphicBuffer> buffer = new GraphicBuffer();
- data.read(*buffer.get());
- int slot = -1;
- int result = attachBuffer(&slot, buffer);
- reply->writeInt32(slot);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case RELEASE_BUFFER: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- int buf = data.readInt32();
- uint64_t frameNumber = static_cast<uint64_t>(data.readInt64());
- sp<Fence> releaseFence = new Fence();
- status_t err = data.read(*releaseFence);
- if (err) return err;
- status_t result = releaseBuffer(buf, frameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case CONSUMER_CONNECT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() );
- bool controlledByApp = data.readInt32();
- status_t result = consumerConnect(consumer, controlledByApp);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case CONSUMER_DISCONNECT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- status_t result = consumerDisconnect();
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case GET_RELEASED_BUFFERS: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint64_t slotMask = 0;
- status_t result = getReleasedBuffers(&slotMask);
- reply->writeInt64(static_cast<int64_t>(slotMask));
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_DEFAULT_BUFFER_SIZE: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint32_t width = data.readUint32();
- uint32_t height = data.readUint32();
- status_t result = setDefaultBufferSize(width, height);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_MAX_BUFFER_COUNT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- int bufferCount = data.readInt32();
- status_t result = setMaxBufferCount(bufferCount);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_MAX_ACQUIRED_BUFFER_COUNT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- int maxAcquiredBuffers = data.readInt32();
- status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_CONSUMER_NAME: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- setConsumerName( data.readString8() );
- return NO_ERROR;
- }
- case SET_DEFAULT_BUFFER_FORMAT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- PixelFormat defaultFormat = static_cast<PixelFormat>(data.readInt32());
- status_t result = setDefaultBufferFormat(defaultFormat);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_DEFAULT_BUFFER_DATA_SPACE: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- android_dataspace defaultDataSpace =
- static_cast<android_dataspace>(data.readInt32());
- status_t result = setDefaultBufferDataSpace(defaultDataSpace);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_CONSUMER_USAGE_BITS: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint32_t usage = data.readUint32();
- status_t result = setConsumerUsageBits(usage);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case SET_TRANSFORM_HINT: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- uint32_t hint = data.readUint32();
- status_t result = setTransformHint(hint);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case GET_SIDEBAND_STREAM: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- sp<NativeHandle> stream = getSidebandStream();
- reply->writeInt32(static_cast<int32_t>(stream != NULL));
- if (stream != NULL) {
- reply->writeNativeHandle(stream->handle());
- }
- return NO_ERROR;
- }
- case GET_OCCUPANCY_HISTORY: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- bool forceFlush = false;
- status_t error = data.readBool(&forceFlush);
- if (error != NO_ERROR) {
- return error;
- }
- std::vector<OccupancyTracker::Segment> history;
- status_t result = getOccupancyHistory(forceFlush, &history);
- error = reply->writeParcelableVector(history);
- if (error != NO_ERROR) {
- return error;
- }
- error = reply->writeInt32(result);
- if (error != NO_ERROR) {
- return error;
- }
- return NO_ERROR;
- }
- case DISCARD_FREE_BUFFERS: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- status_t result = discardFreeBuffers();
- status_t error = reply->writeInt32(result);
- return error;
- }
- case DUMP: {
- CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
- String8 result = data.readString8();
- String8 prefix = data.readString8();
- static_cast<IGraphicBufferConsumer*>(this)->dumpState(result, prefix);
- reply->writeString8(result);
- return NO_ERROR;
+status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ACQUIRE_BUFFER:
+ return callLocal(data, reply, &IGraphicBufferConsumer::acquireBuffer);
+ case Tag::DETACH_BUFFER:
+ return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer);
+ case Tag::ATTACH_BUFFER:
+ return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer);
+ case Tag::RELEASE_BUFFER:
+ return callLocal(data, reply, &IGraphicBufferConsumer::releaseHelper);
+ case Tag::CONSUMER_CONNECT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect);
+ case Tag::CONSUMER_DISCONNECT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::consumerDisconnect);
+ case Tag::GET_RELEASED_BUFFERS:
+ return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffers);
+ case Tag::SET_DEFAULT_BUFFER_SIZE:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferSize);
+ case Tag::SET_MAX_BUFFER_COUNT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setMaxBufferCount);
+ case Tag::SET_MAX_ACQUIRED_BUFFER_COUNT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setMaxAcquiredBufferCount);
+ case Tag::SET_CONSUMER_NAME:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerName);
+ case Tag::SET_DEFAULT_BUFFER_FORMAT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferFormat);
+ case Tag::SET_DEFAULT_BUFFER_DATA_SPACE:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferDataSpace);
+ case Tag::SET_CONSUMER_USAGE_BITS:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerUsageBits);
+ case Tag::SET_CONSUMER_IS_PROTECTED:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerIsProtected);
+ case Tag::SET_TRANSFORM_HINT:
+ return callLocal(data, reply, &IGraphicBufferConsumer::setTransformHint);
+ case Tag::GET_SIDEBAND_STREAM:
+ return callLocal(data, reply, &IGraphicBufferConsumer::getSidebandStream);
+ case Tag::GET_OCCUPANCY_HISTORY:
+ return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory);
+ case Tag::DISCARD_FREE_BUFFERS:
+ return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers);
+ case Tag::DUMP_STATE: {
+ using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
+ return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
}
}
- return BBinder::onTransact(code, data, reply, flags);
}
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 0149a3f403..8481b502a0 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -20,18 +20,25 @@
#include <utils/Errors.h>
#include <utils/NativeHandle.h>
#include <utils/RefBase.h>
+#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>
+#include <gui/BufferQueueDefs.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+
namespace android {
// ----------------------------------------------------------------------------
+using ::android::hardware::graphics::bufferqueue::V1_0::utils::
+ H2BGraphicBufferProducer;
+
enum {
REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
DEQUEUE_BUFFER,
@@ -118,24 +125,35 @@ public:
}
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width,
- uint32_t height, PixelFormat format, uint32_t usage) {
+ uint32_t height, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) {
Parcel data, reply;
+ bool getFrameTimestamps = (outTimestamps != nullptr);
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeUint32(width);
data.writeUint32(height);
data.writeInt32(static_cast<int32_t>(format));
data.writeUint32(usage);
+ data.writeBool(getFrameTimestamps);
+
status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
+
*buf = reply.readInt32();
- bool nonNull = reply.readInt32();
- if (nonNull) {
- *fence = new Fence();
- result = reply.read(**fence);
+ *fence = new Fence();
+ result = reply.read(**fence);
+ if (result != NO_ERROR) {
+ fence->clear();
+ return result;
+ }
+ if (getFrameTimestamps) {
+ result = reply.read(*outTimestamps);
if (result != NO_ERROR) {
- fence->clear();
+ ALOGE("IGBP::dequeueBuffer failed to read timestamps: %d",
+ result);
return result;
}
}
@@ -203,22 +221,37 @@ public:
if (result != NO_ERROR) {
return result;
}
+
*slot = reply.readInt32();
result = reply.readInt32();
+ if (result == NO_ERROR &&
+ (*slot < 0 || *slot >= BufferQueueDefs::NUM_BUFFER_SLOTS)) {
+ ALOGE("attachBuffer returned invalid slot %d", *slot);
+ android_errorWriteLog(0x534e4554, "37478824");
+ return UNKNOWN_ERROR;
+ }
+
return result;
}
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
+
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(buf);
data.write(input);
+
status_t result = remote()->transact(QUEUE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+
+ result = reply.read(*output);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
result = reply.readInt32();
return result;
}
@@ -265,7 +298,7 @@ public:
if (result != NO_ERROR) {
return result;
}
- memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+ reply.read(*output);
result = reply.readInt32();
return result;
}
@@ -422,40 +455,24 @@ public:
return result;
}
- virtual bool getFrameTimestamps(uint64_t frameNumber,
- FrameTimestamps* outTimestamps) const {
+ virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(
IGraphicBufferProducer::getInterfaceDescriptor());
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write token: %d", result);
- return false;
- }
- result = data.writeUint64(frameNumber);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to write: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to write token: %d", result);
+ return;
}
result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to transact: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to transact: %d", result);
+ return;
}
- bool found = false;
- result = reply.readBool(&found);
+ result = reply.read(*outDelta);
if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read: %d", result);
- return false;
+ ALOGE("IGBP::getFrameTimestamps failed to read timestamps: %d",
+ result);
}
- if (found) {
- result = reply.read(*outTimestamps);
- if (result != NO_ERROR) {
- ALOGE("getFrameTimestamps failed to read timestamps: %d",
- result);
- return false;
- }
- }
- return found;
}
virtual status_t getUniqueId(uint64_t* outId) const {
@@ -482,7 +499,123 @@ public:
// translation unit (see clang warning -Wweak-vtables)
BpGraphicBufferProducer::~BpGraphicBufferProducer() {}
-IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer");
+class HpGraphicBufferProducer : public HpInterface<
+ BpGraphicBufferProducer, H2BGraphicBufferProducer> {
+public:
+ HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {}
+
+ status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override {
+ return mBase->requestBuffer(slot, buf);
+ }
+
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
+ return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+ }
+
+ status_t setAsyncMode(bool async) override {
+ return mBase->setAsyncMode(async);
+ }
+
+ status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) override {
+ return mBase->dequeueBuffer(
+ slot, fence, w, h, format, usage, outTimestamps);
+ }
+
+ status_t detachBuffer(int slot) override {
+ return mBase->detachBuffer(slot);
+ }
+
+ status_t detachNextBuffer(
+ sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
+ return mBase->detachNextBuffer(outBuffer, outFence);
+ }
+
+ status_t attachBuffer(
+ int* outSlot, const sp<GraphicBuffer>& buffer) override {
+ return mBase->attachBuffer(outSlot, buffer);
+ }
+
+ status_t queueBuffer(
+ int slot,
+ const QueueBufferInput& input,
+ QueueBufferOutput* output) override {
+ return mBase->queueBuffer(slot, input, output);
+ }
+
+ status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
+ return mBase->cancelBuffer(slot, fence);
+ }
+
+ int query(int what, int* value) override {
+ return mBase->query(what, value);
+ }
+
+ status_t connect(
+ const sp<IProducerListener>& listener,
+ int api, bool producerControlledByApp,
+ QueueBufferOutput* output) override {
+ return mBase->connect(listener, api, producerControlledByApp, output);
+ }
+
+ status_t disconnect(
+ int api, DisconnectMode mode = DisconnectMode::Api) override {
+ return mBase->disconnect(api, mode);
+ }
+
+ status_t setSidebandStream(const sp<NativeHandle>& stream) override {
+ return mBase->setSidebandStream(stream);
+ }
+
+ void allocateBuffers(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t usage) override {
+ return mBase->allocateBuffers(width, height, format, usage);
+ }
+
+ status_t allowAllocation(bool allow) override {
+ return mBase->allowAllocation(allow);
+ }
+
+ status_t setGenerationNumber(uint32_t generationNumber) override {
+ return mBase->setGenerationNumber(generationNumber);
+ }
+
+ String8 getConsumerName() const override {
+ return mBase->getConsumerName();
+ }
+
+ status_t setSharedBufferMode(bool sharedBufferMode) override {
+ return mBase->setSharedBufferMode(sharedBufferMode);
+ }
+
+ status_t setAutoRefresh(bool autoRefresh) override {
+ return mBase->setAutoRefresh(autoRefresh);
+ }
+
+ status_t setDequeueTimeout(nsecs_t timeout) override {
+ return mBase->setDequeueTimeout(timeout);
+ }
+
+ status_t getLastQueuedBuffer(
+ sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence,
+ float outTransformMatrix[16]) override {
+ return mBase->getLastQueuedBuffer(
+ outBuffer, outFence, outTransformMatrix);
+ }
+
+ void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override {
+ return mBase->getFrameTimestamps(outDelta);
+ }
+
+ status_t getUniqueId(uint64_t* outId) const override {
+ return mBase->getUniqueId(outId);
+ }
+};
+
+IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer,
+ "android.gui.IGraphicBufferProducer");
// ----------------------------------------------------------------------
@@ -522,14 +655,18 @@ status_t BnGraphicBufferProducer::onTransact(
uint32_t height = data.readUint32();
PixelFormat format = static_cast<PixelFormat>(data.readInt32());
uint32_t usage = data.readUint32();
+ bool getTimestamps = data.readBool();
+
int buf = 0;
- sp<Fence> fence;
+ sp<Fence> fence = Fence::NO_FENCE;
+ FrameEventHistoryDelta frameTimestamps;
int result = dequeueBuffer(&buf, &fence, width, height, format,
- usage);
+ usage, getTimestamps ? &frameTimestamps : nullptr);
+
reply->writeInt32(buf);
- reply->writeInt32(fence != NULL);
- if (fence != NULL) {
- reply->write(*fence);
+ reply->write(*fence);
+ if (getTimestamps) {
+ reply->write(frameTimestamps);
}
reply->writeInt32(result);
return NO_ERROR;
@@ -573,14 +710,14 @@ status_t BnGraphicBufferProducer::onTransact(
}
case QUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+
int buf = data.readInt32();
QueueBufferInput input(data);
- QueueBufferOutput* const output =
- reinterpret_cast<QueueBufferOutput *>(
- reply->writeInplace(sizeof(QueueBufferOutput)));
- memset(output, 0, sizeof(QueueBufferOutput));
- status_t result = queueBuffer(buf, input, output);
+ QueueBufferOutput output;
+ status_t result = queueBuffer(buf, input, &output);
+ reply->write(output);
reply->writeInt32(result);
+
return NO_ERROR;
}
case CANCEL_BUFFER: {
@@ -611,11 +748,9 @@ status_t BnGraphicBufferProducer::onTransact(
}
int api = data.readInt32();
bool producerControlledByApp = data.readInt32();
- QueueBufferOutput* const output =
- reinterpret_cast<QueueBufferOutput *>(
- reply->writeInplace(sizeof(QueueBufferOutput)));
- memset(output, 0, sizeof(QueueBufferOutput));
- status_t res = connect(listener, api, producerControlledByApp, output);
+ QueueBufferOutput output;
+ status_t res = connect(listener, api, producerControlledByApp, &output);
+ reply->write(output);
reply->writeInt32(res);
return NO_ERROR;
}
@@ -718,26 +853,14 @@ status_t BnGraphicBufferProducer::onTransact(
}
case GET_FRAME_TIMESTAMPS: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
- uint64_t frameNumber = 0;
- status_t result = data.readUint64(&frameNumber);
+ FrameEventHistoryDelta frameTimestamps;
+ getFrameTimestamps(&frameTimestamps);
+ status_t result = reply->write(frameTimestamps);
if (result != NO_ERROR) {
- ALOGE("onTransact failed to read: %d", result);
- return result;
- }
- FrameTimestamps timestamps;
- bool found = getFrameTimestamps(frameNumber, &timestamps);
- result = reply->writeBool(found);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write: %d", result);
+ ALOGE("BnGBP::GET_FRAME_TIMESTAMPS failed to write buffer: %d",
+ result);
return result;
}
- if (found) {
- result = reply->write(timestamps);
- if (result != NO_ERROR) {
- ALOGE("onTransact failed to write timestamps: %d", result);
- return result;
- }
- }
return NO_ERROR;
}
case GET_UNIQUE_ID: {
@@ -764,16 +887,21 @@ IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel)
parcel.read(*this);
}
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+ return sizeof(timestamp) +
+ sizeof(isAutoTimestamp) +
+ sizeof(dataSpace) +
+ sizeof(crop) +
+ sizeof(scalingMode) +
+ sizeof(transform) +
+ sizeof(stickyTransform) +
+ sizeof(getFrameTimestamps);
+}
+
size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
- return sizeof(timestamp)
- + sizeof(isAutoTimestamp)
- + sizeof(dataSpace)
- + sizeof(crop)
- + sizeof(scalingMode)
- + sizeof(transform)
- + sizeof(stickyTransform)
- + fence->getFlattenedSize()
- + surfaceDamage.getFlattenedSize();
+ return minFlattenedSize() +
+ fence->getFlattenedSize() +
+ surfaceDamage.getFlattenedSize();
}
size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
@@ -786,6 +914,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten(
if (size < getFlattenedSize()) {
return NO_MEMORY;
}
+
FlattenableUtils::write(buffer, size, timestamp);
FlattenableUtils::write(buffer, size, isAutoTimestamp);
FlattenableUtils::write(buffer, size, dataSpace);
@@ -793,6 +922,8 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten(
FlattenableUtils::write(buffer, size, scalingMode);
FlattenableUtils::write(buffer, size, transform);
FlattenableUtils::write(buffer, size, stickyTransform);
+ FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
status_t result = fence->flatten(buffer, size, fds, count);
if (result != NO_ERROR) {
return result;
@@ -803,16 +934,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten(
status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count)
{
- size_t minNeeded =
- sizeof(timestamp)
- + sizeof(isAutoTimestamp)
- + sizeof(dataSpace)
- + sizeof(crop)
- + sizeof(scalingMode)
- + sizeof(transform)
- + sizeof(stickyTransform);
-
- if (size < minNeeded) {
+ if (size < minFlattenedSize()) {
return NO_MEMORY;
}
@@ -823,6 +945,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
FlattenableUtils::read(buffer, size, scalingMode);
FlattenableUtils::read(buffer, size, transform);
FlattenableUtils::read(buffer, size, stickyTransform);
+ FlattenableUtils::read(buffer, size, getFrameTimestamps);
fence = new Fence();
status_t result = fence->unflatten(buffer, size, fds, count);
@@ -832,4 +955,56 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
return surfaceDamage.unflatten(buffer, size);
}
+// ----------------------------------------------------------------------------
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+ return sizeof(width) +
+ sizeof(height) +
+ sizeof(transformHint) +
+ sizeof(numPendingBuffers) +
+ sizeof(nextFrameNumber) +
+ sizeof(bufferReplaced);
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+ return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+ return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, width);
+ FlattenableUtils::write(buffer, size, height);
+ FlattenableUtils::write(buffer, size, transformHint);
+ FlattenableUtils::write(buffer, size, numPendingBuffers);
+ FlattenableUtils::write(buffer, size, nextFrameNumber);
+ FlattenableUtils::write(buffer, size, bufferReplaced);
+
+ return frameTimestamps.flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, width);
+ FlattenableUtils::read(buffer, size, height);
+ FlattenableUtils::read(buffer, size, transformHint);
+ FlattenableUtils::read(buffer, size, numPendingBuffers);
+ FlattenableUtils::read(buffer, size, nextFrameNumber);
+ FlattenableUtils::read(buffer, size, bufferReplaced);
+
+ return frameTimestamps.unflatten(buffer, size, fds, count);
+}
+
}; // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0a8e6a555e..0a0d112af6 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -21,14 +21,13 @@
#include <sys/types.h>
#include <binder/Parcel.h>
-#include <binder/IMemory.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-#include <gui/BitTube.h>
#include <gui/IDisplayEventConnection.h>
-#include <gui/ISurfaceComposer.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/ISurfaceComposerClient.h>
#include <private/gui/LayerState.h>
@@ -44,8 +43,6 @@
namespace android {
-class IDisplayEventConnection;
-
class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
{
public:
@@ -64,12 +61,14 @@ public:
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
- virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc()
+ virtual sp<ISurfaceComposerClient> createScopedConnection(
+ const sp<IGraphicBufferProducer>& parent)
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, data, &reply);
- return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder());
+ data.writeStrongBinder(IInterface::asBinder(parent));
+ remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply);
+ return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
virtual void setTransactionState(
@@ -104,7 +103,7 @@ public:
virtual status_t captureScreen(const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform,
ISurfaceComposer::Rotation rotation)
{
@@ -115,8 +114,8 @@ public:
data.write(sourceCrop);
data.writeUint32(reqWidth);
data.writeUint32(reqHeight);
- data.writeUint32(minLayerZ);
- data.writeUint32(maxLayerZ);
+ data.writeInt32(minLayerZ);
+ data.writeInt32(maxLayerZ);
data.writeInt32(static_cast<int32_t>(useIdentityTransform));
data.writeInt32(static_cast<int32_t>(rotation));
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
@@ -158,7 +157,51 @@ public:
return result != 0;
}
- virtual sp<IDisplayEventConnection> createDisplayEventConnection()
+ virtual status_t getSupportedFrameTimestamps(
+ std::vector<FrameEvent>* outSupported) const {
+ if (!outSupported) {
+ return UNEXPECTED_NULL;
+ }
+ outSupported->clear();
+
+ Parcel data, reply;
+
+ status_t err = data.writeInterfaceToken(
+ ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = remote()->transact(
+ BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS,
+ data, &reply);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ int32_t result = 0;
+ err = reply.readInt32(&result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ std::vector<int32_t> supported;
+ err = reply.readInt32Vector(&supported);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ outSupported->reserve(supported.size());
+ for (int32_t s : supported) {
+ outSupported->push_back(static_cast<FrameEvent>(s));
+ }
+ return NO_ERROR;
+ }
+
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource)
{
Parcel data, reply;
sp<IDisplayEventConnection> result;
@@ -167,6 +210,7 @@ public:
if (err != NO_ERROR) {
return result;
}
+ data.writeInt32(static_cast<int32_t>(vsyncSource));
err = remote()->transact(
BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
data, &reply);
@@ -379,10 +423,52 @@ public:
}
result = reply.readInt32();
if (result == NO_ERROR) {
- result = reply.readParcelable(outCapabilities);
+ result = reply.read(*outCapabilities);
}
return result;
}
+
+ virtual status_t enableVSyncInjections(bool enable) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeBool(enable);
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to writeBool: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS,
+ data, &reply, TF_ONE_WAY);
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to transact: %d", result);
+ return result;
+ }
+ return result;
+ }
+
+ virtual status_t injectVSync(nsecs_t when) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("injectVSync failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeInt64(when);
+ if (result != NO_ERROR) {
+ ALOGE("injectVSync failed to writeInt64: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, TF_ONE_WAY);
+ if (result != NO_ERROR) {
+ ALOGE("injectVSync failed to transact: %d", result);
+ return result;
+ }
+ return result;
+ }
+
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -403,9 +489,11 @@ status_t BnSurfaceComposer::onTransact(
reply->writeStrongBinder(b);
return NO_ERROR;
}
- case CREATE_GRAPHIC_BUFFER_ALLOC: {
+ case CREATE_SCOPED_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> b = IInterface::asBinder(createGraphicBufferAlloc());
+ sp<IGraphicBufferProducer> bufferProducer =
+ interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+ sp<IBinder> b = IInterface::asBinder(createScopedConnection(bufferProducer));
reply->writeStrongBinder(b);
return NO_ERROR;
}
@@ -458,8 +546,8 @@ status_t BnSurfaceComposer::onTransact(
data.read(sourceCrop);
uint32_t reqWidth = data.readUint32();
uint32_t reqHeight = data.readUint32();
- uint32_t minLayerZ = data.readUint32();
- uint32_t maxLayerZ = data.readUint32();
+ int32_t minLayerZ = data.readInt32();
+ int32_t maxLayerZ = data.readInt32();
bool useIdentityTransform = static_cast<bool>(data.readInt32());
int32_t rotation = data.readInt32();
@@ -478,9 +566,29 @@ status_t BnSurfaceComposer::onTransact(
reply->writeInt32(result);
return NO_ERROR;
}
+ case GET_SUPPORTED_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ std::vector<FrameEvent> supportedTimestamps;
+ status_t result = getSupportedFrameTimestamps(&supportedTimestamps);
+ status_t err = reply->writeInt32(result);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ std::vector<int32_t> supported;
+ supported.reserve(supportedTimestamps.size());
+ for (FrameEvent s : supportedTimestamps) {
+ supported.push_back(static_cast<int32_t>(s));
+ }
+ return reply->writeInt32Vector(supported);
+ }
case CREATE_DISPLAY_EVENT_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IDisplayEventConnection> connection(createDisplayEventConnection());
+ sp<IDisplayEventConnection> connection(createDisplayEventConnection(
+ static_cast<ISurfaceComposer::VsyncSource>(data.readInt32())));
reply->writeStrongBinder(IInterface::asBinder(connection));
return NO_ERROR;
}
@@ -631,10 +739,30 @@ status_t BnSurfaceComposer::onTransact(
result = getHdrCapabilities(display, &capabilities);
reply->writeInt32(result);
if (result == NO_ERROR) {
- reply->writeParcelable(capabilities);
+ reply->write(capabilities);
}
return NO_ERROR;
}
+ case ENABLE_VSYNC_INJECTIONS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ bool enable = false;
+ status_t result = data.readBool(&enable);
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to readBool: %d", result);
+ return result;
+ }
+ return enableVSyncInjections(enable);
+ }
+ case INJECT_VSYNC: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int64_t when = 0;
+ status_t result = data.readInt64(&when);
+ if (result != NO_ERROR) {
+ ALOGE("enableVSyncInjections failed to readInt64: %d", result);
+ return result;
+ }
+ return injectVSync(when);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 47cb0473e8..679f44b57b 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -17,112 +17,61 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
-#include <stdio.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <ui/Point.h>
-#include <ui/Rect.h>
+#include <gui/ISurfaceComposerClient.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/ISurfaceComposerClient.h>
-#include <private/gui/LayerState.h>
-// ---------------------------------------------------------------------------
+#include <binder/SafeInterface.h>
+
+#include <ui/FrameStats.h>
namespace android {
-enum {
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
DESTROY_SURFACE,
CLEAR_LAYER_FRAME_STATS,
GET_LAYER_FRAME_STATS,
- GET_TRANSFORM_TO_DISPLAY_INVERSE
+ LAST = GET_LAYER_FRAME_STATS,
};
-class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
-{
+} // Anonymous namespace
+
+class BpSurfaceComposerClient : public SafeBpInterface<ISurfaceComposerClient> {
public:
explicit BpSurfaceComposerClient(const sp<IBinder>& impl)
- : BpInterface<ISurfaceComposerClient>(impl) {
- }
-
- virtual ~BpSurfaceComposerClient();
-
- virtual status_t createSurface(const String8& name, uint32_t width,
- uint32_t height, PixelFormat format, uint32_t flags,
- sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp) {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeString8(name);
- data.writeUint32(width);
- data.writeUint32(height);
- data.writeInt32(static_cast<int32_t>(format));
- data.writeUint32(flags);
- remote()->transact(CREATE_SURFACE, data, &reply);
- *handle = reply.readStrongBinder();
- *gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
- return reply.readInt32();
+ : SafeBpInterface<ISurfaceComposerClient>(impl, "BpSurfaceComposerClient") {}
+
+ ~BpSurfaceComposerClient() override;
+
+ status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t flags, const sp<IBinder>& parent, uint32_t windowType,
+ uint32_t ownerUid, sp<IBinder>* handle,
+ sp<IGraphicBufferProducer>* gbp) override {
+ return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
+ name, width, height,
+ format, flags, parent,
+ windowType, ownerUid,
+ handle, gbp);
}
- virtual status_t destroySurface(const sp<IBinder>& handle) {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeStrongBinder(handle);
- remote()->transact(DESTROY_SURFACE, data, &reply);
- return reply.readInt32();
+ status_t destroySurface(const sp<IBinder>& handle) override {
+ return callRemote<decltype(&ISurfaceComposerClient::destroySurface)>(Tag::DESTROY_SURFACE,
+ handle);
}
- virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeStrongBinder(handle);
- remote()->transact(CLEAR_LAYER_FRAME_STATS, data, &reply);
- return reply.readInt32();
+ status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
+ return callRemote<decltype(
+ &ISurfaceComposerClient::clearLayerFrameStats)>(Tag::CLEAR_LAYER_FRAME_STATS,
+ handle);
}
- virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- data.writeStrongBinder(handle);
- remote()->transact(GET_LAYER_FRAME_STATS, data, &reply);
- reply.read(*outStats);
- return reply.readInt32();
- }
-
- virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
- bool* outTransformToDisplayInverse) const {
- Parcel data, reply;
- status_t result =
- data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- return result;
- }
- result = data.writeStrongBinder(handle);
- if (result != NO_ERROR) {
- return result;
- }
- result = remote()->transact(GET_TRANSFORM_TO_DISPLAY_INVERSE, data, &reply);
- if (result != NO_ERROR) {
- return result;
- }
- int transformInverse;
- result = reply.readInt32(&transformInverse);
- if (result != NO_ERROR) {
- return result;
- }
- *outTransformToDisplayInverse = transformInverse != 0 ? true : false;
- status_t result2 = reply.readInt32(&result);
- if (result2 != NO_ERROR) {
- return result2;
- }
- return result;
+ status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const override {
+ return callRemote<decltype(
+ &ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle,
+ outStats);
}
};
@@ -134,69 +83,22 @@ IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClie
// ----------------------------------------------------------------------
-status_t BnSurfaceComposerClient::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case CREATE_SURFACE: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- String8 name = data.readString8();
- uint32_t width = data.readUint32();
- uint32_t height = data.readUint32();
- PixelFormat format = static_cast<PixelFormat>(data.readInt32());
- uint32_t createFlags = data.readUint32();
- sp<IBinder> handle;
- sp<IGraphicBufferProducer> gbp;
- status_t result = createSurface(name, width, height, format,
- createFlags, &handle, &gbp);
- reply->writeStrongBinder(handle);
- reply->writeStrongBinder(IInterface::asBinder(gbp));
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case DESTROY_SURFACE: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- reply->writeInt32(destroySurface( data.readStrongBinder() ) );
- return NO_ERROR;
- }
- case CLEAR_LAYER_FRAME_STATS: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- sp<IBinder> handle = data.readStrongBinder();
- status_t result = clearLayerFrameStats(handle);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case GET_LAYER_FRAME_STATS: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- sp<IBinder> handle = data.readStrongBinder();
- FrameStats stats;
- status_t result = getLayerFrameStats(handle, &stats);
- reply->write(stats);
- reply->writeInt32(result);
- return NO_ERROR;
- }
- case GET_TRANSFORM_TO_DISPLAY_INVERSE: {
- CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
- sp<IBinder> handle;
- status_t result = data.readStrongBinder(&handle);
- if (result != NO_ERROR) {
- return result;
- }
- bool transformInverse = false;
- result = getTransformToDisplayInverse(handle, &transformInverse);
- if (result != NO_ERROR) {
- return result;
- }
- result = reply->writeInt32(transformInverse ? 1 : 0);
- if (result != NO_ERROR) {
- return result;
- }
- result = reply->writeInt32(NO_ERROR);
- return result;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
+status_t BnSurfaceComposerClient::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::CREATE_SURFACE:
+ return callLocal(data, reply, &ISurfaceComposerClient::createSurface);
+ case Tag::DESTROY_SURFACE:
+ return callLocal(data, reply, &ISurfaceComposerClient::destroySurface);
+ case Tag::CLEAR_LAYER_FRAME_STATS:
+ return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats);
+ case Tag::GET_LAYER_FRAME_STATS:
+ return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats);
}
}
-}; // namespace android
+} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index d1c576e4c5..9b06e63610 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -28,7 +28,7 @@ status_t layer_state_t::write(Parcel& output) const
output.writeUint32(what);
output.writeFloat(x);
output.writeFloat(y);
- output.writeUint32(z);
+ output.writeInt32(z);
output.writeUint32(w);
output.writeUint32(h);
output.writeUint32(layerStack);
@@ -39,9 +39,12 @@ status_t layer_state_t::write(Parcel& output) const
output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
output.write(crop);
output.write(finalCrop);
- output.writeStrongBinder(handle);
+ output.writeStrongBinder(barrierHandle);
+ output.writeStrongBinder(reparentHandle);
output.writeUint64(frameNumber);
output.writeInt32(overrideScalingMode);
+ output.writeStrongBinder(IInterface::asBinder(barrierGbp));
+ output.writeStrongBinder(relativeLayerHandle);
output.write(transparentRegion);
return NO_ERROR;
}
@@ -52,7 +55,7 @@ status_t layer_state_t::read(const Parcel& input)
what = input.readUint32();
x = input.readFloat();
y = input.readFloat();
- z = input.readUint32();
+ z = input.readInt32();
w = input.readUint32();
h = input.readUint32();
layerStack = input.readUint32();
@@ -67,9 +70,13 @@ status_t layer_state_t::read(const Parcel& input)
}
input.read(crop);
input.read(finalCrop);
- handle = input.readStrongBinder();
+ barrierHandle = input.readStrongBinder();
+ reparentHandle = input.readStrongBinder();
frameNumber = input.readUint64();
overrideScalingMode = input.readInt32();
+ barrierGbp =
+ interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
+ relativeLayerHandle = input.readStrongBinder();
input.read(transparentRegion);
return NO_ERROR;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d1a9cbbbda..7b2b5c37f1 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -18,25 +18,28 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
-#include <android/native_window.h>
+#include <gui/Surface.h>
-#include <binder/Parcel.h>
+#include <android/native_window.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/NativeHandle.h>
+#include <ui/DisplayStatInfo.h>
#include <ui/Fence.h>
+#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
+#include <gui/BufferItem.h>
#include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <gui/GLConsumer.h>
-#include <gui/Surface.h>
+#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
namespace android {
Surface::Surface(
@@ -49,7 +52,10 @@ Surface::Surface(
mAutoRefresh(false),
mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
mSharedBufferHasBeenQueued(false),
- mNextFrameNumber(1)
+ mQueriedSupportedTimestamps(false),
+ mFrameTimestampsSupportsPresent(false),
+ mEnableFrameTimestamps(false),
+ mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>())
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
@@ -93,6 +99,14 @@ Surface::~Surface() {
}
}
+sp<ISurfaceComposer> Surface::composerService() const {
+ return ComposerService::getComposerService();
+}
+
+nsecs_t Surface::now() const {
+ return systemTime();
+}
+
sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
return mGraphicBufferProducer;
}
@@ -135,37 +149,224 @@ status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
outTransformMatrix);
}
-bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
- nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
- nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
- nsecs_t* outReleaseTime) {
+status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) {
ATRACE_CALL();
- FrameTimestamps timestamps;
- bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
- &timestamps);
- if (found) {
- if (outPostedTime) {
- *outPostedTime = timestamps.postedTime;
- }
- if (outAcquireTime) {
- *outAcquireTime = timestamps.acquireTime;
- }
- if (outRefreshStartTime) {
- *outRefreshStartTime = timestamps.refreshStartTime;
- }
- if (outGlCompositionDoneTime) {
- *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
- }
- if (outDisplayRetireTime) {
- *outDisplayRetireTime = timestamps.displayRetireTime;
+ DisplayStatInfo stats;
+ status_t err = composerService()->getDisplayStats(NULL, &stats);
+
+ *outRefreshDuration = stats.vsyncPeriod;
+
+ return NO_ERROR;
+}
+
+void Surface::enableFrameTimestamps(bool enable) {
+ Mutex::Autolock lock(mMutex);
+ // If going from disabled to enabled, get the initial values for
+ // compositor and display timing.
+ if (!mEnableFrameTimestamps && enable) {
+ FrameEventHistoryDelta delta;
+ mGraphicBufferProducer->getFrameTimestamps(&delta);
+ mFrameEventHistory->applyDelta(delta);
+ }
+ mEnableFrameTimestamps = enable;
+}
+
+status_t Surface::getCompositorTiming(
+ nsecs_t* compositeDeadline, nsecs_t* compositeInterval,
+ nsecs_t* compositeToPresentLatency) {
+ Mutex::Autolock lock(mMutex);
+ if (!mEnableFrameTimestamps) {
+ return INVALID_OPERATION;
+ }
+
+ if (compositeDeadline != nullptr) {
+ *compositeDeadline =
+ mFrameEventHistory->getNextCompositeDeadline(now());
+ }
+ if (compositeInterval != nullptr) {
+ *compositeInterval = mFrameEventHistory->getCompositeInterval();
+ }
+ if (compositeToPresentLatency != nullptr) {
+ *compositeToPresentLatency =
+ mFrameEventHistory->getCompositeToPresentLatency();
+ }
+ return NO_ERROR;
+}
+
+static bool checkConsumerForUpdates(
+ const FrameEvents* e, const uint64_t lastFrameNumber,
+ const nsecs_t* outLatchTime,
+ const nsecs_t* outFirstRefreshStartTime,
+ const nsecs_t* outLastRefreshStartTime,
+ const nsecs_t* outGpuCompositionDoneTime,
+ const nsecs_t* outDisplayPresentTime,
+ const nsecs_t* outDequeueReadyTime,
+ const nsecs_t* outReleaseTime) {
+ bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo();
+ bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) &&
+ !e->hasFirstRefreshStartInfo();
+ bool checkForGpuCompositionDone = (outGpuCompositionDoneTime != nullptr) &&
+ !e->hasGpuCompositionDoneInfo();
+ bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) &&
+ !e->hasDisplayPresentInfo();
+
+ // LastRefreshStart, DequeueReady, and Release are never available for the
+ // last frame.
+ bool checkForLastRefreshStart = (outLastRefreshStartTime != nullptr) &&
+ !e->hasLastRefreshStartInfo() &&
+ (e->frameNumber != lastFrameNumber);
+ bool checkForDequeueReady = (outDequeueReadyTime != nullptr) &&
+ !e->hasDequeueReadyInfo() && (e->frameNumber != lastFrameNumber);
+ bool checkForRelease = (outReleaseTime != nullptr) &&
+ !e->hasReleaseInfo() && (e->frameNumber != lastFrameNumber);
+
+ // RequestedPresent and Acquire info are always available producer-side.
+ return checkForLatch || checkForFirstRefreshStart ||
+ checkForLastRefreshStart || checkForGpuCompositionDone ||
+ checkForDisplayPresent || checkForDequeueReady || checkForRelease;
+}
+
+static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) {
+ if (dst != nullptr) {
+ // We always get valid timestamps for these eventually.
+ *dst = (src == FrameEvents::TIMESTAMP_PENDING) ?
+ NATIVE_WINDOW_TIMESTAMP_PENDING : src;
+ }
+}
+
+static void getFrameTimestampFence(nsecs_t *dst,
+ const std::shared_ptr<FenceTime>& src, bool fenceShouldBeKnown) {
+ if (dst != nullptr) {
+ if (!fenceShouldBeKnown) {
+ *dst = NATIVE_WINDOW_TIMESTAMP_PENDING;
+ return;
}
- if (outReleaseTime) {
- *outReleaseTime = timestamps.releaseTime;
+
+ nsecs_t signalTime = src->getSignalTime();
+ *dst = (signalTime == Fence::SIGNAL_TIME_PENDING) ?
+ NATIVE_WINDOW_TIMESTAMP_PENDING :
+ (signalTime == Fence::SIGNAL_TIME_INVALID) ?
+ NATIVE_WINDOW_TIMESTAMP_INVALID :
+ signalTime;
+ }
+}
+
+status_t Surface::getFrameTimestamps(uint64_t frameNumber,
+ nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime,
+ nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime,
+ nsecs_t* outLastRefreshStartTime, nsecs_t* outGpuCompositionDoneTime,
+ nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
+ nsecs_t* outReleaseTime) {
+ ATRACE_CALL();
+
+ Mutex::Autolock lock(mMutex);
+
+ if (!mEnableFrameTimestamps) {
+ return INVALID_OPERATION;
+ }
+
+ // Verify the requested timestamps are supported.
+ querySupportedTimestampsLocked();
+ if (outDisplayPresentTime != nullptr && !mFrameTimestampsSupportsPresent) {
+ return BAD_VALUE;
+ }
+
+ FrameEvents* events = mFrameEventHistory->getFrame(frameNumber);
+ if (events == nullptr) {
+ // If the entry isn't available in the producer, it's definitely not
+ // available in the consumer.
+ return NAME_NOT_FOUND;
+ }
+
+ // Update our cache of events if the requested events are not available.
+ if (checkConsumerForUpdates(events, mLastFrameNumber,
+ outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGpuCompositionDoneTime, outDisplayPresentTime,
+ outDequeueReadyTime, outReleaseTime)) {
+ FrameEventHistoryDelta delta;
+ mGraphicBufferProducer->getFrameTimestamps(&delta);
+ mFrameEventHistory->applyDelta(delta);
+ events = mFrameEventHistory->getFrame(frameNumber);
+ }
+
+ if (events == nullptr) {
+ // The entry was available before the update, but was overwritten
+ // after the update. Make sure not to send the wrong frame's data.
+ return NAME_NOT_FOUND;
+ }
+
+ getFrameTimestamp(outRequestedPresentTime, events->requestedPresentTime);
+ getFrameTimestamp(outLatchTime, events->latchTime);
+ getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime);
+ getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime);
+ getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime);
+
+ getFrameTimestampFence(outAcquireTime, events->acquireFence,
+ events->hasAcquireInfo());
+ getFrameTimestampFence(outGpuCompositionDoneTime,
+ events->gpuCompositionDoneFence,
+ events->hasGpuCompositionDoneInfo());
+ getFrameTimestampFence(outDisplayPresentTime, events->displayPresentFence,
+ events->hasDisplayPresentInfo());
+ getFrameTimestampFence(outReleaseTime, events->releaseFence,
+ events->hasReleaseInfo());
+
+ return NO_ERROR;
+}
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+status_t Surface::getWideColorSupport(bool* supported) {
+ ATRACE_CALL();
+
+ sp<IBinder> display(
+ composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ Vector<android_color_mode_t> colorModes;
+ status_t err =
+ composerService()->getDisplayColorModes(display, &colorModes);
+
+ if (err)
+ return err;
+
+ bool wideColorBoardConfig =
+ getBool<ISurfaceFlingerConfigs,
+ &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
+ *supported = false;
+ for (android_color_mode_t colorMode : colorModes) {
+ switch (colorMode) {
+ case HAL_COLOR_MODE_DISPLAY_P3:
+ case HAL_COLOR_MODE_ADOBE_RGB:
+ case HAL_COLOR_MODE_DCI_P3:
+ if (wideColorBoardConfig) {
+ *supported = true;
+ }
+ break;
+ default:
+ break;
}
- return true;
}
- return false;
+
+ return NO_ERROR;
+}
+
+status_t Surface::getHdrSupport(bool* supported) {
+ ATRACE_CALL();
+
+ sp<IBinder> display(
+ composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ HdrCapabilities hdrCapabilities;
+ status_t err =
+ composerService()->getHdrCapabilities(display, &hdrCapabilities);
+
+ if (err)
+ return err;
+
+ *supported = !hdrCapabilities.getSupportedHdrTypes().empty();
+
+ return NO_ERROR;
}
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
@@ -271,9 +472,13 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
uint32_t reqHeight;
PixelFormat reqFormat;
uint32_t reqUsage;
+ bool enableFrameTimestamps;
{
Mutex::Autolock lock(mMutex);
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.clear();
+ }
reqWidth = mReqWidth ? mReqWidth : mUserWidth;
reqHeight = mReqHeight ? mReqHeight : mUserHeight;
@@ -281,6 +486,8 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
reqFormat = mReqFormat;
reqUsage = mReqUsage;
+ enableFrameTimestamps = mEnableFrameTimestamps;
+
if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
BufferItem::INVALID_BUFFER_SLOT) {
sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -294,10 +501,13 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
int buf = -1;
sp<Fence> fence;
- nsecs_t now = systemTime();
+ nsecs_t startTime = systemTime();
+
+ FrameEventHistoryDelta frameTimestamps;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
- reqWidth, reqHeight, reqFormat, reqUsage);
- mLastDequeueDuration = systemTime() - now;
+ reqWidth, reqHeight, reqFormat, reqUsage,
+ enableFrameTimestamps ? &frameTimestamps : nullptr);
+ mLastDequeueDuration = systemTime() - startTime;
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
@@ -306,8 +516,17 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
return result;
}
+ if (buf < 0 || buf >= NUM_BUFFER_SLOTS) {
+ ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf);
+ android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
+ return FAILED_TRANSACTION;
+ }
+
Mutex::Autolock lock(mMutex);
+ // Write this while holding the mutex
+ mLastDequeueStartTime = startTime;
+
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
// this should never happen
@@ -317,7 +536,14 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
freeAllBuffers();
}
- if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
+ if (enableFrameTimestamps) {
+ mFrameEventHistory->applyDelta(frameTimestamps);
+ }
+
+ if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
+ if (mReportRemovedBuffers && (gbuf != nullptr)) {
+ mRemovedBuffers.push_back(gbuf);
+ }
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
@@ -435,7 +661,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
IGraphicBufferProducer::QueueBufferOutput output;
IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
- fence, mStickyTransform);
+ fence, mStickyTransform, mEnableFrameTimestamps);
if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
input.setSurfaceDamage(Region::INVALID_REGION);
@@ -507,17 +733,31 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
- uint32_t numPendingBuffers = 0;
- uint32_t hint = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers, &mNextFrameNumber);
+ if (mEnableFrameTimestamps) {
+ mFrameEventHistory->applyDelta(output.frameTimestamps);
+ // Update timestamps with the local acquire fence.
+ // The consumer doesn't send it back to prevent us from having two
+ // file descriptors of the same fence.
+ mFrameEventHistory->updateAcquireFence(mNextFrameNumber,
+ std::make_shared<FenceTime>(std::move(fence)));
+
+ // Cache timestamps of signaled fences so we can close their file
+ // descriptors.
+ mFrameEventHistory->updateSignalTimes();
+ }
+
+ mLastFrameNumber = mNextFrameNumber;
+
+ mDefaultWidth = output.width;
+ mDefaultHeight = output.height;
+ mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
- mTransformHint = hint;
+ mTransformHint = output.transformHint;
}
- mConsumerRunningBehind = (numPendingBuffers >= 2);
+ mConsumerRunningBehind = (output.numPendingBuffers >= 2);
if (!mConnectedToCpu) {
// Clear surface damage back to full-buffer
@@ -533,6 +773,29 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
return err;
}
+void Surface::querySupportedTimestampsLocked() const {
+ // mMutex must be locked when calling this method.
+
+ if (mQueriedSupportedTimestamps) {
+ return;
+ }
+ mQueriedSupportedTimestamps = true;
+
+ std::vector<FrameEvent> supportedFrameTimestamps;
+ status_t err = composerService()->getSupportedFrameTimestamps(
+ &supportedFrameTimestamps);
+
+ if (err != NO_ERROR) {
+ return;
+ }
+
+ for (auto sft : supportedFrameTimestamps) {
+ if (sft == FrameEvent::DISPLAY_PRESENT) {
+ mFrameTimestampsSupportsPresent = true;
+ }
+ }
+}
+
int Surface::query(int what, int* value) const {
ATRACE_CALL();
ALOGV("Surface::query");
@@ -546,9 +809,8 @@ int Surface::query(int what, int* value) const {
}
break;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
- sp<ISurfaceComposer> composer(
- ComposerService::getComposerService());
- if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) {
+ if (composerService()->authenticateSurfaceTexture(
+ mGraphicBufferProducer)) {
*value = 1;
} else {
*value = 0;
@@ -595,6 +857,15 @@ int Surface::query(int what, int* value) const {
static_cast<int>(durationUs);
return NO_ERROR;
}
+ case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT: {
+ querySupportedTimestampsLocked();
+ *value = mFrameTimestampsSupportsPresent ? 1 : 0;
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_IS_VALID: {
+ *value = mGraphicBufferProducer != nullptr ? 1 : 0;
+ return NO_ERROR;
+ }
}
}
return mGraphicBufferProducer->query(what, value);
@@ -670,9 +941,27 @@ int Surface::perform(int operation, va_list args)
case NATIVE_WINDOW_SET_AUTO_REFRESH:
res = dispatchSetAutoRefresh(args);
break;
+ case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION:
+ res = dispatchGetDisplayRefreshCycleDuration(args);
+ break;
+ case NATIVE_WINDOW_GET_NEXT_FRAME_ID:
+ res = dispatchGetNextFrameId(args);
+ break;
+ case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS:
+ res = dispatchEnableFrameTimestamps(args);
+ break;
+ case NATIVE_WINDOW_GET_COMPOSITOR_TIMING:
+ res = dispatchGetCompositorTiming(args);
+ break;
case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
res = dispatchGetFrameTimestamps(args);
break;
+ case NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT:
+ res = dispatchGetWideColorSupport(args);
+ break;
+ case NATIVE_WINDOW_GET_HDR_SUPPORT:
+ res = dispatchGetHdrSupport(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -793,18 +1082,57 @@ int Surface::dispatchSetAutoRefresh(va_list args) {
return setAutoRefresh(autoRefresh);
}
+int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) {
+ nsecs_t* outRefreshDuration = va_arg(args, int64_t*);
+ return getDisplayRefreshCycleDuration(outRefreshDuration);
+}
+
+int Surface::dispatchGetNextFrameId(va_list args) {
+ uint64_t* nextFrameId = va_arg(args, uint64_t*);
+ *nextFrameId = getNextFrameNumber();
+ return NO_ERROR;
+}
+
+int Surface::dispatchEnableFrameTimestamps(va_list args) {
+ bool enable = va_arg(args, int);
+ enableFrameTimestamps(enable);
+ return NO_ERROR;
+}
+
+int Surface::dispatchGetCompositorTiming(va_list args) {
+ nsecs_t* compositeDeadline = va_arg(args, int64_t*);
+ nsecs_t* compositeInterval = va_arg(args, int64_t*);
+ nsecs_t* compositeToPresentLatency = va_arg(args, int64_t*);
+ return getCompositorTiming(compositeDeadline, compositeInterval,
+ compositeToPresentLatency);
+}
+
int Surface::dispatchGetFrameTimestamps(va_list args) {
- uint32_t framesAgo = va_arg(args, uint32_t);
- nsecs_t* outPostedTime = va_arg(args, int64_t*);
+ uint64_t frameId = va_arg(args, uint64_t);
+ nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*);
nsecs_t* outAcquireTime = va_arg(args, int64_t*);
- nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
- nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
- nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
+ nsecs_t* outLatchTime = va_arg(args, int64_t*);
+ nsecs_t* outFirstRefreshStartTime = va_arg(args, int64_t*);
+ nsecs_t* outLastRefreshStartTime = va_arg(args, int64_t*);
+ nsecs_t* outGpuCompositionDoneTime = va_arg(args, int64_t*);
+ nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
+ nsecs_t* outDequeueReadyTime = va_arg(args, int64_t*);
nsecs_t* outReleaseTime = va_arg(args, int64_t*);
- bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
- outPostedTime, outAcquireTime, outRefreshStartTime,
- outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
- return ret ? NO_ERROR : BAD_VALUE;
+ return getFrameTimestamps(frameId,
+ outRequestedPresentTime, outAcquireTime, outLatchTime,
+ outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGpuCompositionDoneTime, outDisplayPresentTime,
+ outDequeueReadyTime, outReleaseTime);
+}
+
+int Surface::dispatchGetWideColorSupport(va_list args) {
+ bool* outSupport = va_arg(args, bool*);
+ return getWideColorSupport(outSupport);
+}
+
+int Surface::dispatchGetHdrSupport(va_list args) {
+ bool* outSupport = va_arg(args, bool*);
+ return getHdrSupport(outSupport);
}
int Surface::connect(int api) {
@@ -813,23 +1141,28 @@ int Surface::connect(int api) {
}
int Surface::connect(int api, const sp<IProducerListener>& listener) {
+ return connect(api, listener, false);
+}
+
+int Surface::connect(
+ int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
ATRACE_CALL();
ALOGV("Surface::connect");
Mutex::Autolock lock(mMutex);
IGraphicBufferProducer::QueueBufferOutput output;
+ mReportRemovedBuffers = reportBufferRemoval;
int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
if (err == NO_ERROR) {
- uint32_t numPendingBuffers = 0;
- uint32_t hint = 0;
- output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
- &numPendingBuffers, &mNextFrameNumber);
+ mDefaultWidth = output.width;
+ mDefaultHeight = output.height;
+ mNextFrameNumber = output.nextFrameNumber;
// Disable transform hint if sticky transform is set.
if (mStickyTransform == 0) {
- mTransformHint = hint;
+ mTransformHint = output.transformHint;
}
- mConsumerRunningBehind = (numPendingBuffers >= 2);
+ mConsumerRunningBehind = (output.numPendingBuffers >= 2);
}
if (!err && api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = true;
@@ -848,6 +1181,7 @@ int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) {
ATRACE_CALL();
ALOGV("Surface::disconnect");
Mutex::Autolock lock(mMutex);
+ mRemovedBuffers.clear();
mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
mSharedBufferHasBeenQueued = false;
freeAllBuffers();
@@ -879,6 +1213,9 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
}
Mutex::Autolock lock(mMutex);
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.clear();
+ }
sp<GraphicBuffer> buffer(NULL);
sp<Fence> fence(NULL);
@@ -897,7 +1234,10 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
if (mSlots[i].buffer != NULL &&
- mSlots[i].buffer->handle == buffer->handle) {
+ mSlots[i].buffer->getId() == buffer->getId()) {
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.push_back(mSlots[i].buffer);
+ }
mSlots[i].buffer = NULL;
}
}
@@ -911,6 +1251,9 @@ int Surface::attachBuffer(ANativeWindowBuffer* buffer)
ALOGV("Surface::attachBuffer");
Mutex::Autolock lock(mMutex);
+ if (mReportRemovedBuffers) {
+ mRemovedBuffers.clear();
+ }
sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
uint32_t priorGeneration = graphicBuffer->mGenerationNumber;
@@ -923,6 +1266,9 @@ int Surface::attachBuffer(ANativeWindowBuffer* buffer)
graphicBuffer->mGenerationNumber = priorGeneration;
return result;
}
+ if (mReportRemovedBuffers && (mSlots[attachedSlot].buffer != nullptr)) {
+ mRemovedBuffers.push_back(mSlots[attachedSlot].buffer);
+ }
mSlots[attachedSlot].buffer = graphicBuffer;
return NO_ERROR;
@@ -1356,70 +1702,21 @@ status_t Surface::getUniqueId(uint64_t* outId) const {
return mGraphicBufferProducer->getUniqueId(outId);
}
-namespace view {
-
-status_t Surface::writeToParcel(Parcel* parcel) const {
- return writeToParcel(parcel, false);
-}
-
-status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
- if (parcel == nullptr) return BAD_VALUE;
-
- status_t res = OK;
-
- if (!nameAlreadyWritten) {
- res = parcel->writeString16(name);
- if (res != OK) return res;
-
- /* isSingleBuffered defaults to no */
- res = parcel->writeInt32(0);
- if (res != OK) return res;
- }
-
- res = parcel->writeStrongBinder(
- IGraphicBufferProducer::asBinder(graphicBufferProducer));
-
- return res;
-}
-
-status_t Surface::readFromParcel(const Parcel* parcel) {
- return readFromParcel(parcel, false);
+nsecs_t Surface::getLastDequeueStartTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mLastDequeueStartTime;
}
-status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
- if (parcel == nullptr) return BAD_VALUE;
-
- status_t res = OK;
- if (!nameAlreadyRead) {
- name = readMaybeEmptyString16(parcel);
- // Discard this for now
- int isSingleBuffered;
- res = parcel->readInt32(&isSingleBuffered);
- if (res != OK) {
- return res;
- }
+status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) {
+ if (out == nullptr) {
+ ALOGE("%s: out must not be null!", __FUNCTION__);
+ return BAD_VALUE;
}
- sp<IBinder> binder;
-
- res = parcel->readStrongBinder(&binder);
- if (res != OK) return res;
-
- graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
-
+ Mutex::Autolock lock(mMutex);
+ *out = mRemovedBuffers;
+ mRemovedBuffers.clear();
return OK;
}
-String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
- size_t len;
- const char16_t* str = parcel->readString16Inplace(&len);
- if (str != nullptr) {
- return String16(str, len);
- } else {
- return String16();
- }
-}
-
-} // namespace view
-
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 43506e9191..8c8384399c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -26,17 +26,18 @@
#include <utils/String8.h>
#include <utils/threads.h>
-#include <binder/IMemory.h>
#include <binder/IServiceManager.h>
#include <system/graphics.h>
#include <ui/DisplayInfo.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
+#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
@@ -129,6 +130,8 @@ class Composer : public Singleton<Composer>
void openGlobalTransactionImpl();
void closeGlobalTransactionImpl(bool synchronous);
void setAnimationTransactionImpl();
+ status_t enableVSyncInjectionsImpl(bool enable);
+ status_t injectVSyncImpl(nsecs_t when);
layer_state_t* getLayerStateLocked(
const sp<SurfaceComposerClient>& client, const sp<IBinder>& id);
@@ -145,7 +148,9 @@ public:
status_t setSize(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
uint32_t w, uint32_t h);
status_t setLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
- uint32_t z);
+ int32_t z);
+ status_t setRelativeLayer(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
+ const sp<IBinder>& relativeTo, int32_t z);
status_t setFlags(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
uint32_t flags, uint32_t mask);
status_t setTransparentRegionHint(
@@ -154,7 +159,7 @@ public:
status_t setAlpha(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
float alpha);
status_t setMatrix(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
- float dsdx, float dtdx, float dsdy, float dtdy);
+ float dsdx, float dtdx, float dtdy, float dsdy);
status_t setOrientation(int orientation);
status_t setCrop(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
const Rect& crop);
@@ -165,6 +170,14 @@ public:
status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, const sp<IBinder>& handle,
uint64_t frameNumber);
+ status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const sp<Surface>& barrierSurface,
+ uint64_t frameNumber);
+ status_t reparentChildren(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle);
+ status_t detachChildren(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id);
status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, int32_t overrideScalingMode);
status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client,
@@ -190,6 +203,14 @@ public:
static void closeGlobalTransaction(bool synchronous) {
Composer::getInstance().closeGlobalTransactionImpl(synchronous);
}
+
+ static status_t enableVSyncInjections(bool enable) {
+ return Composer::getInstance().enableVSyncInjectionsImpl(enable);
+ }
+
+ static status_t injectVSync(nsecs_t when) {
+ return Composer::getInstance().injectVSyncImpl(when);
+ }
};
ANDROID_SINGLETON_STATIC_INSTANCE(Composer);
@@ -253,6 +274,16 @@ void Composer::closeGlobalTransactionImpl(bool synchronous) {
sm->setTransactionState(transaction, displayTransaction, flags);
}
+status_t Composer::enableVSyncInjectionsImpl(bool enable) {
+ sp<ISurfaceComposer> sm(ComposerService::getComposerService());
+ return sm->enableVSyncInjections(enable);
+}
+
+status_t Composer::injectVSyncImpl(nsecs_t when) {
+ sp<ISurfaceComposer> sm(ComposerService::getComposerService());
+ return sm->injectVSync(when);
+}
+
void Composer::setAnimationTransactionImpl() {
Mutex::Autolock _l(mLock);
mAnimation = true;
@@ -304,7 +335,7 @@ status_t Composer::setSize(const sp<SurfaceComposerClient>& client,
}
status_t Composer::setLayer(const sp<SurfaceComposerClient>& client,
- const sp<IBinder>& id, uint32_t z) {
+ const sp<IBinder>& id, int32_t z) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -314,6 +345,20 @@ status_t Composer::setLayer(const sp<SurfaceComposerClient>& client,
return NO_ERROR;
}
+status_t Composer::setRelativeLayer(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const sp<IBinder>& relativeTo,
+ int32_t z) {
+ Mutex::Autolock _l(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eRelativeLayerChanged;
+ s->relativeLayerHandle = relativeTo;
+ s->z = z;
+ return NO_ERROR;
+}
+
status_t Composer::setFlags(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, uint32_t flags,
uint32_t mask) {
@@ -368,7 +413,7 @@ status_t Composer::setLayerStack(const sp<SurfaceComposerClient>& client,
status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, float dsdx, float dtdx,
- float dsdy, float dtdy) {
+ float dtdy, float dsdy) {
Mutex::Autolock _l(mLock);
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
@@ -415,11 +460,51 @@ status_t Composer::deferTransactionUntil(
return BAD_INDEX;
}
s->what |= layer_state_t::eDeferTransaction;
- s->handle = handle;
+ s->barrierHandle = handle;
s->frameNumber = frameNumber;
return NO_ERROR;
}
+status_t Composer::deferTransactionUntil(
+ const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
+ const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+ Mutex::Autolock lock(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eDeferTransaction;
+ s->barrierGbp = barrierSurface->getIGraphicBufferProducer();
+ s->frameNumber = frameNumber;
+ return NO_ERROR;
+}
+
+status_t Composer::reparentChildren(
+ const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle) {
+ Mutex::Autolock lock(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eReparentChildren;
+ s->reparentHandle = newParentHandle;
+ return NO_ERROR;
+}
+
+status_t Composer::detachChildren(
+ const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id) {
+ Mutex::Autolock lock(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eDetachChildren;
+ return NO_ERROR;
+}
+
status_t Composer::setOverrideScalingMode(
const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, int32_t overrideScalingMode) {
@@ -529,10 +614,18 @@ SurfaceComposerClient::SurfaceComposerClient()
{
}
+SurfaceComposerClient::SurfaceComposerClient(const sp<IGraphicBufferProducer>& root)
+ : mStatus(NO_INIT), mComposer(Composer::getInstance()), mParent(root)
+{
+}
+
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sm(ComposerService::getComposerService());
if (sm != 0) {
- sp<ISurfaceComposerClient> conn = sm->createConnection();
+ auto rootProducer = mParent.promote();
+ sp<ISurfaceComposerClient> conn;
+ conn = (rootProducer != nullptr) ? sm->createScopedConnection(rootProducer) :
+ sm->createConnection();
if (conn != 0) {
mClient = conn;
mStatus = NO_ERROR;
@@ -575,14 +668,22 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface(
uint32_t w,
uint32_t h,
PixelFormat format,
- uint32_t flags)
+ uint32_t flags,
+ SurfaceControl* parent,
+ uint32_t windowType,
+ uint32_t ownerUid)
{
sp<SurfaceControl> sur;
if (mStatus == NO_ERROR) {
sp<IBinder> handle;
+ sp<IBinder> parentHandle;
sp<IGraphicBufferProducer> gbp;
- status_t err = mClient->createSurface(name, w, h, format, flags,
- &handle, &gbp);
+
+ if (parent != nullptr) {
+ parentHandle = parent->getHandle();
+ }
+ status_t err = mClient->createSurface(name, w, h, format, flags, parentHandle,
+ windowType, ownerUid, &handle, &gbp);
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
sur = new SurfaceControl(this, handle, gbp);
@@ -626,14 +727,6 @@ status_t SurfaceComposerClient::getLayerFrameStats(const sp<IBinder>& token,
return mClient->getLayerFrameStats(token, outStats);
}
-status_t SurfaceComposerClient::getTransformToDisplayInverse(const sp<IBinder>& token,
- bool* outTransformToDisplayInverse) const {
- if (mStatus != NO_ERROR) {
- return mStatus;
- }
- return mClient->getTransformToDisplayInverse(token, outTransformToDisplayInverse);
-}
-
inline Composer& SurfaceComposerClient::getComposer() {
return mComposer;
}
@@ -652,6 +745,14 @@ void SurfaceComposerClient::setAnimationTransaction() {
Composer::setAnimationTransaction();
}
+status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
+ return Composer::enableVSyncInjections(enable);
+}
+
+status_t SurfaceComposerClient::injectVSync(nsecs_t when) {
+ return Composer::injectVSync(when);
+}
+
// ----------------------------------------------------------------------------
status_t SurfaceComposerClient::setCrop(const sp<IBinder>& id, const Rect& crop) {
@@ -671,10 +772,15 @@ status_t SurfaceComposerClient::setSize(const sp<IBinder>& id, uint32_t w, uint3
return getComposer().setSize(this, id, w, h);
}
-status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, uint32_t z) {
+status_t SurfaceComposerClient::setLayer(const sp<IBinder>& id, int32_t z) {
return getComposer().setLayer(this, id, z);
}
+status_t SurfaceComposerClient::setRelativeLayer(const sp<IBinder>& id,
+ const sp<IBinder>& relativeTo, int32_t z) {
+ return getComposer().setRelativeLayer(this, id, relativeTo, z);
+}
+
status_t SurfaceComposerClient::hide(const sp<IBinder>& id) {
return getComposer().setFlags(this, id,
layer_state_t::eLayerHidden,
@@ -706,8 +812,8 @@ status_t SurfaceComposerClient::setLayerStack(const sp<IBinder>& id, uint32_t la
}
status_t SurfaceComposerClient::setMatrix(const sp<IBinder>& id, float dsdx, float dtdx,
- float dsdy, float dtdy) {
- return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy);
+ float dtdy, float dsdy) {
+ return getComposer().setMatrix(this, id, dsdx, dtdx, dtdy, dsdy);
}
status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
@@ -715,6 +821,20 @@ status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
return getComposer().deferTransactionUntil(this, id, handle, frameNumber);
}
+status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
+ const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+ return getComposer().deferTransactionUntil(this, id, barrierSurface, frameNumber);
+}
+
+status_t SurfaceComposerClient::reparentChildren(const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle) {
+ return getComposer().reparentChildren(this, id, newParentHandle);
+}
+
+status_t SurfaceComposerClient::detachChildren(const sp<IBinder>& id) {
+ return getComposer().detachChildren(this, id);
+}
+
status_t SurfaceComposerClient::setOverrideScalingMode(
const sp<IBinder>& id, int32_t overrideScalingMode) {
return getComposer().setOverrideScalingMode(
@@ -824,13 +944,40 @@ status_t ScreenshotClient::capture(
const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ, bool useIdentityTransform) {
+ int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == NULL) return NO_INIT;
return s->captureScreen(display, producer, sourceCrop,
reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform);
}
+status_t ScreenshotClient::captureToBuffer(const sp<IBinder>& display,
+ Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+ int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform,
+ uint32_t rotation,
+ sp<GraphicBuffer>* outBuffer) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+
+ sp<IGraphicBufferConsumer> gbpConsumer;
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &gbpConsumer);
+ sp<BufferItemConsumer> consumer(new BufferItemConsumer(gbpConsumer,
+ GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER,
+ 1, true));
+
+ status_t ret = s->captureScreen(display, producer, sourceCrop, reqWidth, reqHeight,
+ minLayerZ, maxLayerZ, useIdentityTransform,
+ static_cast<ISurfaceComposer::Rotation>(rotation));
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ BufferItem b;
+ consumer->acquireBuffer(&b, 0, true);
+ *outBuffer = b.mGraphicBuffer;
+ return ret;
+}
+
ScreenshotClient::ScreenshotClient()
: mHaveBuffer(false) {
memset(&mBuffer, 0, sizeof(mBuffer));
@@ -852,7 +999,7 @@ sp<CpuConsumer> ScreenshotClient::getCpuConsumer() const {
status_t ScreenshotClient::update(const sp<IBinder>& display,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform, uint32_t rotation) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == NULL) return NO_INIT;
@@ -879,7 +1026,7 @@ status_t ScreenshotClient::update(const sp<IBinder>& display,
status_t ScreenshotClient::update(const sp<IBinder>& display,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- uint32_t minLayerZ, uint32_t maxLayerZ,
+ int32_t minLayerZ, int32_t maxLayerZ,
bool useIdentityTransform) {
return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight,
@@ -888,14 +1035,16 @@ status_t ScreenshotClient::update(const sp<IBinder>& display,
status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop,
bool useIdentityTransform) {
- return ScreenshotClient::update(display, sourceCrop, 0, 0, 0, -1U,
+ return ScreenshotClient::update(display, sourceCrop, 0, 0,
+ INT32_MIN, INT32_MAX,
useIdentityTransform, ISurfaceComposer::eRotateNone);
}
status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop,
uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform) {
return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight,
- 0, -1U, useIdentityTransform, ISurfaceComposer::eRotateNone);
+ INT32_MIN, INT32_MAX,
+ useIdentityTransform, ISurfaceComposer::eRotateNone);
}
void ScreenshotClient::release() {
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 33c1d906e6..58bd273de6 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -102,11 +102,19 @@ status_t SurfaceControl::setLayerStack(uint32_t layerStack) {
if (err < 0) return err;
return mClient->setLayerStack(mHandle, layerStack);
}
-status_t SurfaceControl::setLayer(uint32_t layer) {
+
+status_t SurfaceControl::setLayer(int32_t layer) {
status_t err = validate();
if (err < 0) return err;
return mClient->setLayer(mHandle, layer);
}
+
+status_t SurfaceControl::setRelativeLayer(const sp<IBinder>& relativeTo, int32_t layer) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->setRelativeLayer(mHandle, relativeTo, layer);
+}
+
status_t SurfaceControl::setPosition(float x, float y) {
status_t err = validate();
if (err < 0) return err;
@@ -147,10 +155,10 @@ status_t SurfaceControl::setAlpha(float alpha) {
if (err < 0) return err;
return mClient->setAlpha(mHandle, alpha);
}
-status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
status_t err = validate();
if (err < 0) return err;
- return mClient->setMatrix(mHandle, dsdx, dtdx, dsdy, dtdy);
+ return mClient->setMatrix(mHandle, dsdx, dtdx, dtdy, dsdy);
}
status_t SurfaceControl::setCrop(const Rect& crop) {
status_t err = validate();
@@ -163,13 +171,32 @@ status_t SurfaceControl::setFinalCrop(const Rect& crop) {
return mClient->setFinalCrop(mHandle, crop);
}
-status_t SurfaceControl::deferTransactionUntil(sp<IBinder> handle,
+status_t SurfaceControl::deferTransactionUntil(const sp<IBinder>& handle,
uint64_t frameNumber) {
status_t err = validate();
if (err < 0) return err;
return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
}
+status_t SurfaceControl::deferTransactionUntil(const sp<Surface>& handle,
+ uint64_t frameNumber) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
+}
+
+status_t SurfaceControl::reparentChildren(const sp<IBinder>& newParentHandle) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->reparentChildren(mHandle, newParentHandle);
+}
+
+status_t SurfaceControl::detachChildren() {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->detachChildren(mHandle);
+}
+
status_t SurfaceControl::setOverrideScalingMode(int32_t overrideScalingMode) {
status_t err = validate();
if (err < 0) return err;
@@ -190,13 +217,6 @@ status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const {
return client->getLayerFrameStats(mHandle, outStats);
}
-status_t SurfaceControl::getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const {
- status_t err = validate();
- if (err < 0) return err;
- const sp<SurfaceComposerClient>& client(mClient);
- return client->getTransformToDisplayInverse(mHandle, outTransformToDisplayInverse);
-}
-
status_t SurfaceControl::validate() const
{
if (mHandle==0 || mClient==0) {
@@ -217,17 +237,30 @@ status_t SurfaceControl::writeSurfaceToParcel(
return parcel->writeStrongBinder(IInterface::asBinder(bp));
}
+sp<Surface> SurfaceControl::generateSurfaceLocked() const
+{
+ // This surface is always consumed by SurfaceFlinger, so the
+ // producerControlledByApp value doesn't matter; using false.
+ mSurfaceData = new Surface(mGraphicBufferProducer, false);
+
+ return mSurfaceData;
+}
+
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
- // This surface is always consumed by SurfaceFlinger, so the
- // producerControlledByApp value doesn't matter; using false.
- mSurfaceData = new Surface(mGraphicBufferProducer, false);
+ return generateSurfaceLocked();
}
return mSurfaceData;
}
+sp<Surface> SurfaceControl::createSurface() const
+{
+ Mutex::Autolock _l(mLock);
+ return generateSurfaceLocked();
+}
+
sp<IBinder> SurfaceControl::getHandle() const
{
Mutex::Autolock lock(mLock);
diff --git a/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
new file mode 100644
index 0000000000..a5f28cdbd7
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/B2HProducerListener.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, 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 <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+// B2HProducerListener
+B2HProducerListener::B2HProducerListener(
+ sp<BProducerListener> const& base):
+ mBase(base) {
+}
+
+Return<void> B2HProducerListener::onBufferReleased() {
+ mBase->onBufferReleased();
+ return Void();
+}
+
+Return<bool> B2HProducerListener::needsReleaseNotify() {
+ return mBase->needsReleaseNotify();
+}
+
+} // namespace utils
+} // namespace V1_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
new file mode 100644
index 0000000000..fda5b945da
--- /dev/null
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -0,0 +1,1234 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "H2BGraphicBufferProducer"
+
+#include <android-base/logging.h>
+
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/B2HProducerListener.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace bufferqueue {
+namespace V1_0 {
+namespace utils {
+
+using Status = HGraphicBufferProducer::Status;
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+typedef ::android::hardware::media::V1_0::Rect HRect;
+typedef ::android::hardware::media::V1_0::Region HRegion;
+
+// Conversion functions
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline native_handle_t* native_handle_create_from_fd(int fd) {
+ if (fd < 0) {
+ return native_handle_create(0, 0);
+ }
+ native_handle_t* nh = native_handle_create(1, 0);
+ if (nh == nullptr) {
+ return nullptr;
+ }
+ nh->data[0] = fd;
+ return nh;
+}
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) {
+ return ((nh == nullptr) || (nh->numFds == 0) ||
+ (nh->numFds <= index) || (index < 0)) ?
+ -1 : nh->data[index];
+}
+
+/**
+ * \brief Convert `Return<Status>` to `status_t`. This is for legacy binder
+ * calls.
+ *
+ * \param[in] t The source `Return<Status>`.
+ * \return The corresponding `status_t`.
+ *
+ * This function first check if \p t has a transport error. If it does, then the
+ * return value is the transport error code. Otherwise, the return value is
+ * converted from `Status` contained inside \p t.
+ *
+ * Note:
+ * - This `Status` is omx-specific. It is defined in `types.hal`.
+ * - The name of this function is not `convert`.
+ */
+// convert: Return<Status> -> status_t
+inline status_t toStatusT(Return<Status> const& t) {
+ return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+inline status_t toStatusT(Return<void> const& t) {
+ return t.isOk() ? OK : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
+ t->attr.width = l.getWidth();
+ t->attr.height = l.getHeight();
+ t->attr.stride = l.getStride();
+ t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
+ t->attr.layerCount = l.getLayerCount();
+ t->attr.usage = l.getUsage();
+ t->attr.id = l.getId();
+ t->attr.generationNumber = l.getGenerationNumber();
+ t->nativeHandle = hidl_handle(l.handle);
+}
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
+ native_handle_t* handle = t.nativeHandle == nullptr ?
+ nullptr : native_handle_clone(t.nativeHandle);
+
+ size_t const numInts = 12 +
+ static_cast<size_t>(handle ? handle->numInts : 0);
+ int32_t* ints = new int32_t[numInts];
+
+ size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
+ int* fds = new int[numFds];
+
+ ints[0] = 'GBFR';
+ ints[1] = static_cast<int32_t>(t.attr.width);
+ ints[2] = static_cast<int32_t>(t.attr.height);
+ ints[3] = static_cast<int32_t>(t.attr.stride);
+ ints[4] = static_cast<int32_t>(t.attr.format);
+ ints[5] = static_cast<int32_t>(t.attr.layerCount);
+ ints[6] = static_cast<int32_t>(t.attr.usage);
+ ints[7] = static_cast<int32_t>(t.attr.id >> 32);
+ ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
+ ints[9] = static_cast<int32_t>(t.attr.generationNumber);
+ ints[10] = 0;
+ ints[11] = 0;
+ if (handle) {
+ ints[10] = static_cast<int32_t>(handle->numFds);
+ ints[11] = static_cast<int32_t>(handle->numInts);
+ int* intsStart = handle->data + handle->numFds;
+ std::copy(handle->data, intsStart, fds);
+ std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
+ }
+
+ void const* constBuffer = static_cast<void const*>(ints);
+ size_t size = numInts * sizeof(int32_t);
+ int const* constFds = static_cast<int const*>(fds);
+ status_t status = l->unflatten(constBuffer, size, constFds, numFds);
+
+ delete [] fds;
+ delete [] ints;
+ native_handle_delete(handle);
+ return status == NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
+ return 4;
+};
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+inline size_t getFenceFdCount(hidl_handle const& fence) {
+ return native_handle_read_fd(fence) == -1 ? 0 : 1;
+}
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < 4) {
+ return NO_MEMORY;
+ }
+
+ uint32_t numFdsInHandle;
+ FlattenableUtils::read(buffer, size, numFdsInHandle);
+
+ if (numFdsInHandle > 1) {
+ return BAD_VALUE;
+ }
+
+ if (numFds < numFdsInHandle) {
+ return NO_MEMORY;
+ }
+
+ if (numFdsInHandle) {
+ *nh = native_handle_create_from_fd(*fds);
+ if (*nh == nullptr) {
+ return NO_MEMORY;
+ }
+ *fence = *nh;
+ ++fds;
+ --numFds;
+ } else {
+ *nh = nullptr;
+ *fence = hidl_handle();
+ }
+
+ return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] fence The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t flattenFence(hidl_handle const& fence,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFenceFlattenedSize(fence) ||
+ numFds < getFenceFdCount(fence)) {
+ return NO_MEMORY;
+ }
+ // Cast to uint32_t since the size of a size_t can vary between 32- and
+ // 64-bit processes
+ FlattenableUtils::write(buffer, size,
+ static_cast<uint32_t>(getFenceFdCount(fence)));
+ int fd = native_handle_read_fd(fence);
+ if (fd != -1) {
+ *fds = fd;
+ ++fds;
+ --numFds;
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
+ != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+inline bool convertTo(Fence* l, hidl_handle const& t) {
+ int fd = native_handle_read_fd(t);
+ if (fd != -1) {
+ fd = dup(fd);
+ if (fd == -1) {
+ return false;
+ }
+ }
+ native_handle_t* nh = native_handle_create_from_fd(fd);
+ if (nh == nullptr) {
+ if (fd != -1) {
+ close(fd);
+ }
+ return false;
+ }
+
+ size_t const baseSize = getFenceFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ native_handle_delete(nh);
+ return false;
+ }
+
+ size_t const baseNumFds = getFenceFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ native_handle_delete(nh);
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
+ native_handle_delete(nh);
+ return false;
+ }
+ native_handle_delete(nh);
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Unflatten `HRegion`.
+ *
+ * \param[out] t The destination `HRegion`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t unflatten(HRegion* t, void const*& buffer, size_t& size) {
+ if (size < sizeof(uint32_t)) {
+ return NO_MEMORY;
+ }
+
+ uint32_t numRects = 0;
+ FlattenableUtils::read(buffer, size, numRects);
+ if (size < numRects * sizeof(HRect)) {
+ return NO_MEMORY;
+ }
+ if (numRects > (UINT32_MAX / sizeof(HRect))) {
+ return NO_MEMORY;
+ }
+
+ t->resize(numRects);
+ for (size_t r = 0; r < numRects; ++r) {
+ ::android::Rect rect(::android::Rect::EMPTY_RECT);
+ status_t status = rect.unflatten(buffer, size);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ FlattenableUtils::advance(buffer, size, sizeof(rect));
+ (*t)[r] = HRect{
+ static_cast<int32_t>(rect.left),
+ static_cast<int32_t>(rect.top),
+ static_cast<int32_t>(rect.right),
+ static_cast<int32_t>(rect.bottom)};
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+// IGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return a lower bound on the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+ HGraphicBufferProducer::QueueBufferInput const& /* t */) {
+ return sizeof(int64_t) + // timestamp
+ sizeof(int) + // isAutoTimestamp
+ sizeof(android_dataspace) + // dataSpace
+ sizeof(::android::Rect) + // crop
+ sizeof(int) + // scalingMode
+ sizeof(uint32_t) + // transform
+ sizeof(uint32_t) + // stickyTransform
+ sizeof(bool); // getFrameTimestamps
+}
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflatten(
+ HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+ void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+ if (size < minFlattenedSize(*t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, t->timestamp);
+ int lIsAutoTimestamp;
+ FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
+ t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
+ android_dataspace_t lDataSpace;
+ FlattenableUtils::read(buffer, size, lDataSpace);
+ t->dataSpace = static_cast<Dataspace>(lDataSpace);
+ ::android::Rect lCrop;
+ FlattenableUtils::read(buffer, size, lCrop);
+ t->crop = HRect{
+ static_cast<int32_t>(lCrop.left),
+ static_cast<int32_t>(lCrop.top),
+ static_cast<int32_t>(lCrop.right),
+ static_cast<int32_t>(lCrop.bottom)};
+ int lScalingMode;
+ FlattenableUtils::read(buffer, size, lScalingMode);
+ t->scalingMode = static_cast<int32_t>(lScalingMode);
+ FlattenableUtils::read(buffer, size, t->transform);
+ FlattenableUtils::read(buffer, size, t->stickyTransform);
+ FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
+
+ status_t status = unflattenFence(&(t->fence), nh,
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ return unflatten(&(t->surfaceDamage), buffer, size);
+}
+
+/**
+ * \brief Wrap `IGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `IGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline bool wrapAs(
+ HGraphicBufferProducer::QueueBufferInput* t,
+ native_handle_t** nh,
+ BGraphicBufferProducer::QueueBufferInput const& l) {
+
+ size_t const baseSize = l.getFlattenedSize();
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = l.getFdCount();
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = baseFds.get();
+ size_t numFds = baseNumFds;
+ if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+ HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+ constexpr size_t min = sizeof(t.state);
+ switch (t.state) {
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+ return min;
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+ return min + getFenceFlattenedSize(t.fence);
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+ return min + sizeof(
+ ::android::FenceTime::Snapshot::signalTime);
+ }
+ return 0;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+inline size_t getFdCount(
+ HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+ return t.state ==
+ HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
+ getFenceFdCount(t.fence) : 0;
+}
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[out] nh The cloned native handle, if necessary.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`, in which case \p nh will be returned.
+ */
+inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+ native_handle_t** nh,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ *nh = nullptr;
+ switch (t.state) {
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::EMPTY);
+ return NO_ERROR;
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::FENCE);
+ *nh = t.fence.getNativeHandle() == nullptr ?
+ nullptr : native_handle_clone(t.fence);
+ return flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
+ case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+ FlattenableUtils::write(buffer, size,
+ ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
+ FlattenableUtils::write(buffer, size, t.signalTimeNs);
+ return NO_ERROR;
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return a lower bound on the size of the non-fd buffer required to
+ * flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+ HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
+ return sizeof(uint64_t) + // mFrameNumber
+ sizeof(uint8_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddRetireCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(nsecs_t) + // mPostedTime
+ sizeof(nsecs_t) + // mRequestedPresentTime
+ sizeof(nsecs_t) + // mLatchTime
+ sizeof(nsecs_t) + // mFirstRefreshStartTime
+ sizeof(nsecs_t); // mLastRefreshStartTime
+}
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventsDelta const& t) {
+ return minFlattenedSize(t) +
+ getFlattenedSize(t.gpuCompositionDoneFence) +
+ getFlattenedSize(t.displayPresentFence) +
+ getFlattenedSize(t.displayRetireFence) +
+ getFlattenedSize(t.releaseFence);
+};
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+ HGraphicBufferProducer::FrameEventsDelta const& t) {
+ return getFdCount(t.gpuCompositionDoneFence) +
+ getFdCount(t.displayPresentFence) +
+ getFdCount(t.displayRetireFence) +
+ getFdCount(t.releaseFence);
+};
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[out] nh The array of native handles that are cloned.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. These native handles will
+ * need to be closed by the caller.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+// FrameEventsDelta::flatten
+inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+ std::vector<native_handle_t*>* nh,
+ void*& buffer, size_t& size, int*& fds, size_t numFds) {
+ // Check that t.index is within a valid range.
+ if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
+ || t.index > std::numeric_limits<uint8_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, t.frameNumber);
+
+ // These are static_cast to uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addRetireCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, t.postedTimeNs);
+ FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
+ FlattenableUtils::write(buffer, size, t.latchTimeNs);
+ FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
+ FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
+ FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
+
+ // Fences
+ HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
+ tSnapshot[0] = &t.gpuCompositionDoneFence;
+ tSnapshot[1] = &t.displayPresentFence;
+ tSnapshot[2] = &t.displayRetireFence;
+ tSnapshot[3] = &t.releaseFence;
+ nh->resize(4);
+ for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+ status_t status = flatten(
+ *(tSnapshot[snapshotIndex]),
+ &((*nh)[snapshotIndex]),
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ while (snapshotIndex > 0) {
+ --snapshotIndex;
+ native_handle_close((*nh)[snapshotIndex]);
+ native_handle_delete((*nh)[snapshotIndex]);
+ (*nh)[snapshotIndex] = nullptr;
+ }
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+ size_t size = 4 + // mDeltas.size()
+ sizeof(t.compositorTiming);
+ for (size_t i = 0; i < t.deltas.size(); ++i) {
+ size += getFlattenedSize(t.deltas[i]);
+ }
+ return size;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+ size_t numFds = 0;
+ for (size_t i = 0; i < t.deltas.size(); ++i) {
+ numFds += getFdCount(t.deltas[i]);
+ }
+ return numFds;
+}
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[out] nh The array of arrays of cloned native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * On success, this function will duplicate file descriptors contained in \p t.
+ * The cloned native handles will be stored in \p nh. Before making the call, \p
+ * nh should have enough space to store `n` pointers to arrays of native
+ * handles, where `n` is the length of `t.deltas`, and each `nh[i]` should have
+ * enough space to store `4` native handles.
+ */
+inline status_t flatten(
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+ std::vector<std::vector<native_handle_t*> >* nh,
+ void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+ if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize(t)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, t.compositorTiming);
+
+ FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
+ nh->resize(t.deltas.size());
+ for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
+ status_t status = flatten(
+ t.deltas[deltaIndex], &((*nh)[deltaIndex]),
+ buffer, size, fds, numFds);
+ if (status != NO_ERROR) {
+ while (deltaIndex > 0) {
+ --deltaIndex;
+ for (size_t snapshotIndex = 0;
+ snapshotIndex < 4; ++snapshotIndex) {
+ native_handle_close((*nh)[deltaIndex][snapshotIndex]);
+ native_handle_delete((*nh)[deltaIndex][snapshotIndex]);
+ (*nh)[deltaIndex][snapshotIndex] = nullptr;
+ }
+ }
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+inline bool convertTo(
+ ::android::FrameEventHistoryDelta* l,
+ HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+
+ size_t const baseSize = getFlattenedSize(t);
+ std::unique_ptr<uint8_t[]> baseBuffer(
+ new (std::nothrow) uint8_t[baseSize]);
+ if (!baseBuffer) {
+ return false;
+ }
+
+ size_t const baseNumFds = getFdCount(t);
+ std::unique_ptr<int[]> baseFds(
+ new (std::nothrow) int[baseNumFds]);
+ if (!baseFds) {
+ return false;
+ }
+
+ void* buffer = static_cast<void*>(baseBuffer.get());
+ size_t size = baseSize;
+ int* fds = static_cast<int*>(baseFds.get());
+ size_t numFds = baseNumFds;
+ std::vector<std::vector<native_handle_t*> > nhAA;
+ if (flatten(t, &nhAA, buffer, size, fds, numFds) != NO_ERROR) {
+ return false;
+ }
+
+ void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+ size = baseSize;
+ int const* constFds = static_cast<int const*>(baseFds.get());
+ numFds = baseNumFds;
+ if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+ for (auto nhA : nhAA) {
+ for (auto nh : nhA) {
+ if (nh != nullptr) {
+ native_handle_close(nh);
+ native_handle_delete(nh);
+ }
+ }
+ }
+ return false;
+ }
+
+ for (auto nhA : nhAA) {
+ for (auto nh : nhA) {
+ if (nh != nullptr) {
+ native_handle_delete(nh);
+ }
+ }
+ }
+ return true;
+}
+
+// Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp:
+// IGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `IGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `IGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// IGraphicBufferProducer::QueueBufferOutput
+inline bool convertTo(
+ BGraphicBufferProducer::QueueBufferOutput* l,
+ HGraphicBufferProducer::QueueBufferOutput const& t) {
+ if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
+ return false;
+ }
+ l->width = t.width;
+ l->height = t.height;
+ l->transformHint = t.transformHint;
+ l->numPendingBuffers = t.numPendingBuffers;
+ l->nextFrameNumber = t.nextFrameNumber;
+ l->bufferReplaced = t.bufferReplaced;
+ return true;
+}
+
+/**
+ * \brief Convert `IGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `IGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode(
+ BGraphicBufferProducer::DisconnectMode l) {
+ switch (l) {
+ case BGraphicBufferProducer::DisconnectMode::Api:
+ return HGraphicBufferProducer::DisconnectMode::API;
+ case BGraphicBufferProducer::DisconnectMode::AllLocal:
+ return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
+ }
+ return HGraphicBufferProducer::DisconnectMode::API;
+}
+
+// H2BGraphicBufferProducer
+
+status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+ *buf = new GraphicBuffer();
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->requestBuffer(
+ static_cast<int32_t>(slot),
+ [&fnStatus, &buf] (Status status, AnwBuffer const& buffer) {
+ fnStatus = toStatusT(status);
+ if (!convertTo(buf->get(), buffer)) {
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount(
+ int maxDequeuedBuffers) {
+ return toStatusT(mBase->setMaxDequeuedBufferCount(
+ static_cast<int32_t>(maxDequeuedBuffers)));
+}
+
+status_t H2BGraphicBufferProducer::setAsyncMode(bool async) {
+ return toStatusT(mBase->setAsyncMode(async));
+}
+
+status_t H2BGraphicBufferProducer::dequeueBuffer(
+ int* slot, sp<Fence>* fence,
+ uint32_t w, uint32_t h, ::android::PixelFormat format,
+ uint32_t usage, FrameEventHistoryDelta* outTimestamps) {
+ *fence = new Fence();
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->dequeueBuffer(
+ w, h, static_cast<PixelFormat>(format), usage,
+ outTimestamps != nullptr,
+ [&fnStatus, slot, fence, outTimestamps] (
+ Status status,
+ int32_t tSlot,
+ hidl_handle const& tFence,
+ HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
+ fnStatus = toStatusT(status);
+ *slot = tSlot;
+ if (!convertTo(fence->get(), tFence)) {
+ ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+ "Invalid output fence");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ if (outTimestamps && !convertTo(outTimestamps, tTs)) {
+ ALOGE("H2BGraphicBufferProducer::dequeueBuffer - "
+ "Invalid output timestamps");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::detachBuffer(int slot) {
+ return toStatusT(mBase->detachBuffer(static_cast<int>(slot)));
+}
+
+status_t H2BGraphicBufferProducer::detachNextBuffer(
+ sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+ *outBuffer = new GraphicBuffer();
+ *outFence = new Fence();
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->detachNextBuffer(
+ [&fnStatus, outBuffer, outFence] (
+ Status status,
+ AnwBuffer const& tBuffer,
+ hidl_handle const& tFence) {
+ fnStatus = toStatusT(status);
+ if (!convertTo(outFence->get(), tFence)) {
+ ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+ "Invalid output fence");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ if (!convertTo(outBuffer->get(), tBuffer)) {
+ ALOGE("H2BGraphicBufferProducer::detachNextBuffer - "
+ "Invalid output buffer");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::attachBuffer(
+ int* outSlot, const sp<GraphicBuffer>& buffer) {
+ AnwBuffer tBuffer;
+ wrapAs(&tBuffer, *buffer);
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->attachBuffer(tBuffer,
+ [&fnStatus, outSlot] (Status status, int32_t slot) {
+ fnStatus = toStatusT(status);
+ *outSlot = slot;
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::queueBuffer(
+ int slot,
+ const QueueBufferInput& input,
+ QueueBufferOutput* output) {
+ HGraphicBufferProducer::QueueBufferInput tInput;
+ native_handle_t* nh;
+ if (!wrapAs(&tInput, &nh, input)) {
+ ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+ "Invalid input");
+ return BAD_VALUE;
+ }
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->queueBuffer(slot, tInput,
+ [&fnStatus, output] (
+ Status status,
+ HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+ fnStatus = toStatusT(status);
+ if (!convertTo(output, tOutput)) {
+ ALOGE("H2BGraphicBufferProducer::queueBuffer - "
+ "Invalid output");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ native_handle_delete(nh);
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+ hidl_handle tFence;
+ native_handle_t* nh = nullptr;
+ if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) {
+ ALOGE("H2BGraphicBufferProducer::cancelBuffer - "
+ "Invalid input fence");
+ return BAD_VALUE;
+ }
+
+ status_t status = toStatusT(mBase->cancelBuffer(
+ static_cast<int32_t>(slot), tFence));
+ native_handle_delete(nh);
+ return status;
+}
+
+int H2BGraphicBufferProducer::query(int what, int* value) {
+ int result;
+ status_t transStatus = toStatusT(mBase->query(
+ static_cast<int32_t>(what),
+ [&result, value] (int32_t tResult, int32_t tValue) {
+ result = static_cast<int>(tResult);
+ *value = static_cast<int>(tValue);
+ }));
+ return transStatus == NO_ERROR ? result : static_cast<int>(transStatus);
+}
+
+status_t H2BGraphicBufferProducer::connect(
+ const sp<IProducerListener>& listener, int api,
+ bool producerControlledByApp, QueueBufferOutput* output) {
+ sp<HProducerListener> tListener = listener == nullptr ?
+ nullptr : new B2HProducerListener(listener);
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->connect(
+ tListener, static_cast<int32_t>(api), producerControlledByApp,
+ [&fnStatus, output] (
+ Status status,
+ HGraphicBufferProducer::QueueBufferOutput const& tOutput) {
+ fnStatus = toStatusT(status);
+ if (!convertTo(output, tOutput)) {
+ ALOGE("H2BGraphicBufferProducer::connect - "
+ "Invalid output");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) {
+ return toStatusT(mBase->disconnect(
+ static_cast<int32_t>(api), toHDisconnectMode(mode)));
+}
+
+status_t H2BGraphicBufferProducer::setSidebandStream(
+ const sp<NativeHandle>& stream) {
+ return toStatusT(mBase->setSidebandStream(stream == nullptr ? nullptr : stream->handle()));
+}
+
+void H2BGraphicBufferProducer::allocateBuffers(uint32_t width, uint32_t height,
+ ::android::PixelFormat format, uint32_t usage) {
+ mBase->allocateBuffers(
+ width, height, static_cast<PixelFormat>(format), usage);
+}
+
+status_t H2BGraphicBufferProducer::allowAllocation(bool allow) {
+ return toStatusT(mBase->allowAllocation(allow));
+}
+
+status_t H2BGraphicBufferProducer::setGenerationNumber(uint32_t generationNumber) {
+ return toStatusT(mBase->setGenerationNumber(generationNumber));
+}
+
+String8 H2BGraphicBufferProducer::getConsumerName() const {
+ String8 lName;
+ mBase->getConsumerName([&lName] (hidl_string const& name) {
+ lName = name.c_str();
+ });
+ return lName;
+}
+
+status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) {
+ return toStatusT(mBase->setSharedBufferMode(sharedBufferMode));
+}
+
+status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) {
+ return toStatusT(mBase->setAutoRefresh(autoRefresh));
+}
+
+status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) {
+ return toStatusT(mBase->setDequeueTimeout(static_cast<int64_t>(timeout)));
+}
+
+status_t H2BGraphicBufferProducer::getLastQueuedBuffer(
+ sp<GraphicBuffer>* outBuffer,
+ sp<Fence>* outFence,
+ float outTransformMatrix[16]) {
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->getLastQueuedBuffer(
+ [&fnStatus, outBuffer, outFence, &outTransformMatrix] (
+ Status status,
+ AnwBuffer const& buffer,
+ hidl_handle const& fence,
+ hidl_array<float, 16> const& transformMatrix) {
+ fnStatus = toStatusT(status);
+ *outBuffer = new GraphicBuffer();
+ if (!convertTo(outBuffer->get(), buffer)) {
+ ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+ "Invalid output buffer");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ *outFence = new Fence();
+ if (!convertTo(outFence->get(), fence)) {
+ ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
+ "Invalid output fence");
+ fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
+ }
+ std::copy(transformMatrix.data(),
+ transformMatrix.data() + 16,
+ outTransformMatrix);
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
+ mBase->getFrameTimestamps([outDelta] (
+ HGraphicBufferProducer::FrameEventHistoryDelta const& tDelta) {
+ convertTo(outDelta, tDelta);
+ });
+}
+
+status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const {
+ status_t fnStatus;
+ status_t transStatus = toStatusT(mBase->getUniqueId(
+ [&fnStatus, outId] (Status status, uint64_t id) {
+ fnStatus = toStatusT(status);
+ *outId = id;
+ }));
+ return transStatus == NO_ERROR ? fnStatus : transStatus;
+}
+
+} // namespace utils
+} // namespace V1_0
+} // namespace bufferqueue
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/libs/gui/include/private/gui/BitTube.h b/libs/gui/include/private/gui/BitTube.h
new file mode 100644
index 0000000000..13c01623b7
--- /dev/null
+++ b/libs/gui/include/private/gui/BitTube.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcelable.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class Parcel;
+
+namespace gui {
+
+class BitTube : public Parcelable {
+public:
+ // creates an uninitialized BitTube (to unparcel into)
+ BitTube() = default;
+
+ // creates a BitTube with a a specified send and receive buffer size
+ explicit BitTube(size_t bufsize);
+
+ // creates a BitTube with a default (4KB) send buffer
+ struct DefaultSizeType {};
+ static constexpr DefaultSizeType DefaultSize{};
+ explicit BitTube(DefaultSizeType);
+
+ explicit BitTube(const Parcel& data);
+
+ virtual ~BitTube() = default;
+
+ // check state after construction
+ status_t initCheck() const;
+
+ // get receive file-descriptor
+ int getFd() const;
+
+ // get the send file-descriptor.
+ int getSendFd() const;
+
+ // moves the receive file descriptor out of this BitTube
+ base::unique_fd moveReceiveFd();
+
+ // resets this BitTube's receive file descriptor to receiveFd
+ void setReceiveFd(base::unique_fd&& receiveFd);
+
+ // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
+ template <typename T>
+ static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) {
+ return sendObjects(tube, events, count, sizeof(T));
+ }
+
+ // receive objects (sized blobs). If the receiving buffer isn't large enough, excess messages
+ // are silently discarded.
+ template <typename T>
+ static ssize_t recvObjects(BitTube* tube, T* events, size_t count) {
+ return recvObjects(tube, events, count, sizeof(T));
+ }
+
+ // implement the Parcelable protocol. Only parcels the receive file descriptor
+ status_t writeToParcel(Parcel* reply) const;
+ status_t readFromParcel(const Parcel* parcel);
+
+private:
+ void init(size_t rcvbuf, size_t sndbuf);
+
+ // send a message. The write is guaranteed to send the whole message or fail.
+ ssize_t write(void const* vaddr, size_t size);
+
+ // receive a message. the passed buffer must be at least as large as the write call used to send
+ // the message, excess data is silently discarded.
+ ssize_t read(void* vaddr, size_t size);
+
+ base::unique_fd mSendFd;
+ mutable base::unique_fd mReceiveFd;
+
+ static ssize_t sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize);
+
+ static ssize_t recvObjects(BitTube* tube, void* events, size_t count, size_t objSize);
+};
+
+} // namespace gui
+} // namespace android
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 477900377a..fa87f29aa1 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -9,14 +9,14 @@ cc_test {
clang: true,
srcs: [
+ "BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
"FillBuffer.cpp",
"GLTest.cpp",
"IGraphicBufferProducer_test.cpp",
+ "Malicious.cpp",
"MultiTextureConsumer_test.cpp",
- "Sensor_test.cpp",
- "SRGB_test.cpp",
"StreamSplitter_test.cpp",
"SurfaceTextureClient_test.cpp",
"SurfaceTextureFBO_test.cpp",
@@ -29,6 +29,8 @@ cc_test {
],
shared_libs: [
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore-utils",
"liblog",
"libEGL",
"libGLESv1_CM",
@@ -36,8 +38,10 @@ cc_test {
"libbinder",
"libcutils",
"libgui",
- "libsync",
+ "libhidlbase",
+ "libhidltransport",
"libui",
"libutils",
+ "libnativewindow"
],
}
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
new file mode 100644
index 0000000000..d64e530488
--- /dev/null
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferItemConsumer_test"
+//#define LOG_NDEBUG 0
+
+#include <gtest/gtest.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+
+namespace android {
+
+static constexpr int kWidth = 100;
+static constexpr int kHeight = 100;
+static constexpr int kMaxLockedBuffers = 3;
+static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+static constexpr int kFrameSleepUs = 30 * 1000;
+
+class BufferItemConsumerTest : public ::testing::Test {
+ protected:
+ struct BufferFreedListener
+ : public BufferItemConsumer::BufferFreedListener {
+ explicit BufferFreedListener(BufferItemConsumerTest* test)
+ : mTest(test) {}
+ void onBufferFreed(const wp<GraphicBuffer>& /* gBuffer */) override {
+ mTest->HandleBufferFreed();
+ }
+ BufferItemConsumerTest* mTest;
+ };
+
+ void SetUp() override {
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mBIC =
+ new BufferItemConsumer(mConsumer, kFormat, kMaxLockedBuffers, true);
+ String8 name("BufferItemConsumer_Under_Test");
+ mBIC->setName(name);
+ mBFL = new BufferFreedListener(this);
+ mBIC->setBufferFreedListener(mBFL);
+
+ sp<IProducerListener> producerListener = new DummyProducerListener();
+ IGraphicBufferProducer::QueueBufferOutput bufferOutput;
+ ASSERT_EQ(NO_ERROR,
+ mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
+ true, &bufferOutput));
+ ASSERT_EQ(NO_ERROR,
+ mProducer->setMaxDequeuedBufferCount(kMaxLockedBuffers));
+ }
+
+ int GetFreedBufferCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mFreedBufferCount;
+ }
+
+ void HandleBufferFreed() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFreedBufferCount++;
+ ALOGV("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount);
+ }
+
+ void DequeueBuffer(int* outSlot) {
+ ASSERT_NE(outSlot, nullptr);
+
+ int slot;
+ sp<Fence> outFence;
+ status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth,
+ kHeight, 0, 0, nullptr);
+ ASSERT_GE(ret, 0);
+
+ ALOGV("dequeueBuffer: slot=%d", slot);
+ if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ ret = mProducer->requestBuffer(slot, &mBuffers[slot]);
+ ASSERT_EQ(NO_ERROR, ret);
+ }
+ *outSlot = slot;
+ }
+
+ void QueueBuffer(int slot) {
+ ALOGV("enqueueBuffer: slot=%d", slot);
+ IGraphicBufferProducer::QueueBufferInput bufferInput(
+ 0ULL, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ IGraphicBufferProducer::QueueBufferOutput bufferOutput;
+ status_t ret = mProducer->queueBuffer(slot, bufferInput, &bufferOutput);
+ ASSERT_EQ(NO_ERROR, ret);
+ }
+
+ void AcquireBuffer(int* outSlot) {
+ ASSERT_NE(outSlot, nullptr);
+ BufferItem buffer;
+ status_t ret = mBIC->acquireBuffer(&buffer, 0, false);
+ ASSERT_EQ(NO_ERROR, ret);
+
+ ALOGV("acquireBuffer: slot=%d", buffer.mSlot);
+ *outSlot = buffer.mSlot;
+ }
+
+ void ReleaseBuffer(int slot) {
+ ALOGV("releaseBuffer: slot=%d", slot);
+ BufferItem buffer;
+ buffer.mSlot = slot;
+ buffer.mGraphicBuffer = mBuffers[slot];
+ status_t ret = mBIC->releaseBuffer(buffer, Fence::NO_FENCE);
+ ASSERT_EQ(NO_ERROR, ret);
+ }
+
+
+ std::mutex mMutex;
+ int mFreedBufferCount{0};
+
+ sp<BufferItemConsumer> mBIC;
+ sp<BufferFreedListener> mBFL;
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+// Test that detaching buffer from consumer side triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) {
+ int slot;
+ // Producer: generate a dummy buffer.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+
+ ASSERT_EQ(0, GetFreedBufferCount());
+ // Consumer: acquire the buffer and then detach it.
+ AcquireBuffer(&slot);
+ status_t ret = mBIC->detachBuffer(slot);
+ ASSERT_EQ(NO_ERROR, ret);
+
+ // Sleep to give some time for callbacks to happen.
+ usleep(kFrameSleepUs);
+ ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that detaching buffer from producer side triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromProducer) {
+ int slot;
+ // Let buffer go through the cycle at least once.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+ ReleaseBuffer(slot);
+
+ ASSERT_EQ(0, GetFreedBufferCount());
+
+ // Producer: generate the buffer again.
+ DequeueBuffer(&slot);
+
+ // Producer: detach the buffer.
+ status_t ret = mProducer->detachBuffer(slot);
+ ASSERT_EQ(NO_ERROR, ret);
+
+ // Sleep to give some time for callbacks to happen.
+ usleep(kFrameSleepUs);
+ ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that abandoning BufferItemConsumer triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_AbandonBufferItemConsumer) {
+ int slot;
+ // Let buffer go through the cycle at least once.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+ ReleaseBuffer(slot);
+
+ // Abandon the BufferItemConsumer.
+ mBIC->abandon();
+
+ // Sleep to give some time for callbacks to happen.
+ usleep(kFrameSleepUs);
+ ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+// Test that delete BufferItemConsumer triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) {
+ int slot;
+ // Let buffer go through the cycle at least once.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+ ReleaseBuffer(slot);
+
+ // Delete the BufferItemConsumer.
+ mBIC.clear();
+
+ // Sleep to give some time for callbacks to happen.
+ usleep(kFrameSleepUs);
+ ASSERT_EQ(1, GetFreedBufferCount());
+}
+
+} // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 65df7dc991..60c1277db0 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -98,7 +98,11 @@ static const uint32_t TEST_DATA = 0x12345678u;
// XXX: Tests that fork a process to hold the BufferQueue must run before tests
// that use a local BufferQueue, or else Binder will get unhappy
-TEST_F(BufferQueueTest, BufferQueueInAnotherProcess) {
+//
+// In one instance this was a crash in the createBufferQueue where the
+// binder call to create a buffer allocator apparently got garbage back.
+// See b/36592665.
+TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) {
const String16 PRODUCER_NAME = String16("BQTestProducer");
const String16 CONSUMER_NAME = String16("BQTestConsumer");
@@ -139,7 +143,7 @@ TEST_F(BufferQueueTest, BufferQueueInAnotherProcess) {
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -183,7 +187,7 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -191,7 +195,7 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
@@ -234,7 +238,7 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError)
for (int i = 0; i < 3; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -270,7 +274,7 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -280,7 +284,7 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) {
for (int i = 0; i < 2; i++) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0,
- GRALLOC_USAGE_SW_READ_OFTEN));
+ GRALLOC_USAGE_SW_READ_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -330,7 +334,7 @@ TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) {
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -379,7 +383,7 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) {
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0, false,
HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
@@ -415,7 +419,7 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataOut;
@@ -438,7 +442,7 @@ TEST_F(BufferQueueTest, MoveFromConsumerToProducer) {
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -487,13 +491,13 @@ TEST_F(BufferQueueTest, TestDisallowingAllocation) {
// This should return an error since it would require an allocation
ASSERT_EQ(OK, mProducer->allowAllocation(false));
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, 0, 0,
- 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+ 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
// This should succeed, now that we've lifted the prohibition
ASSERT_EQ(OK, mProducer->allowAllocation(true));
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
// Release the previous buffer back to the BufferQueue
mProducer->cancelBuffer(slot, fence);
@@ -501,7 +505,7 @@ TEST_F(BufferQueueTest, TestDisallowingAllocation) {
// This should fail since we're requesting a different size
ASSERT_EQ(OK, mProducer->allowAllocation(false));
ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence,
- WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+ WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(BufferQueueTest, TestGenerationNumbers) {
@@ -518,7 +522,7 @@ TEST_F(BufferQueueTest, TestGenerationNumbers) {
int slot;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
@@ -561,7 +565,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) {
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -575,7 +579,8 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) {
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -612,7 +617,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) {
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Queue the buffer
@@ -639,7 +644,8 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) {
// always return the same one.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -678,7 +684,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) {
sp<Fence> fence;
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer));
// Enable shared buffer mode
@@ -695,7 +701,8 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) {
// always the same one and because async mode gets enabled.
int slot;
for (int i = 0; i < 5; i++) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(sharedSlot, slot);
ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output));
}
@@ -730,7 +737,8 @@ TEST_F(BufferQueueTest, TestTimeouts) {
for (int i = 0; i < 5; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0);
+ auto result = mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr);
if (i < 2) {
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
result);
@@ -757,7 +765,8 @@ TEST_F(BufferQueueTest, TestTimeouts) {
for (int i = 0; i < 2; ++i) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input(0ull, true,
HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
@@ -768,7 +777,8 @@ TEST_F(BufferQueueTest, TestTimeouts) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence = Fence::NO_FENCE;
auto startTime = systemTime();
- ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_GE(systemTime() - startTime, TIMEOUT);
// We're technically attaching the same buffer multiple times (since we
@@ -789,7 +799,7 @@ TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> sourceFence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> buffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(OK, mProducer->detachBuffer(slot));
@@ -812,7 +822,7 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) {
int slot = BufferQueue::INVALID_BUFFER_SLOT;
sp<Fence> fence;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> firstBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
@@ -824,7 +834,7 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) {
// Dequeue a second buffer
slot = BufferQueue::INVALID_BUFFER_SLOT;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
- mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
sp<GraphicBuffer> secondBuffer;
ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
@@ -876,7 +886,7 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) {
mProducer->setMaxDequeuedBufferCount(3);
for (size_t i = 0; i < 3; ++i) {
status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -889,7 +899,8 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) {
// The first segment is a two-buffer segment, so we only put one buffer into
// the queue at a time
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -904,16 +915,17 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) {
// two-buffer segment, but then at the end, we put two buffers in the queue
// at the same time before draining it.
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
std::this_thread::sleep_for(16ms);
}
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -928,10 +940,11 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) {
// The third segment is a triple-buffer segment, so the queue is switching
// between one buffer and two buffers deep.
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
for (size_t i = 0; i < 5; ++i) {
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(
+ &slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
@@ -1012,7 +1025,7 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
mProducer->setMaxDequeuedBufferCount(4);
for (size_t i = 0; i < 4; ++i) {
status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, nullptr);
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
}
@@ -1023,14 +1036,14 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
// Get buffers in all states: dequeued, filled, acquired, free
// Fill 3 buffers
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
// Dequeue 1 buffer
- ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
// Acquire and free 1 buffer
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
@@ -1044,7 +1057,7 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
// Check no free buffers in dump
String8 dumpString;
- mConsumer->dumpState(dumpString, nullptr);
+ mConsumer->dumpState(String8{}, &dumpString);
// Parse the dump to ensure that all buffer slots that are FREE also
// have a null GraphicBuffer
@@ -1067,4 +1080,122 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
}
}
+TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ BufferItem item{};
+
+ // Preallocate, dequeue, request, and cancel 2 buffers so we don't get
+ // BUFFER_NEEDS_REALLOCATION below
+ int slots[2] = {};
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
+ for (size_t i = 0; i < 2; ++i) {
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+ 0, 0, 0, 0, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+ }
+ for (size_t i = 0; i < 2; ++i) {
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+ }
+
+ // Fill 2 buffers without consumer consuming them. Verify that all
+ // queued buffer returns proper bufferReplaced flag
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(false, output.bufferReplaced);
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(true, output.bufferReplaced);
+}
+
+TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<IProducerListener> dummyListener(new DummyProducerListener);
+ ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU,
+ true, &output));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+
+ // Dequeue, request, and queue one buffer
+ status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0,
+ nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Acquire and release the buffer. Upon acquiring, the buffer handle should
+ // be non-null since this is the first time we've acquired this slot.
+ BufferItem item;
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_NE(nullptr, item.mGraphicBuffer.get());
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Dequeue and queue the buffer again
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Acquire and release the buffer again. Upon acquiring, the buffer handle
+ // should be null since this is not the first time we've acquired this slot.
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_EQ(nullptr, item.mGraphicBuffer.get());
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Dequeue and queue the buffer again
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Disconnect the producer end. This should clear all of the slots and mark
+ // the buffer in the queue as stale.
+ ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+
+ // Acquire the buffer again. Upon acquiring, the buffer handle should not be
+ // null since the queued buffer should have been marked as stale, which
+ // should trigger the BufferQueue to resend the buffer handle.
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_NE(nullptr, item.mGraphicBuffer.get());
+}
+
+TEST_F(BufferQueueTest, TestProducerConnectDisconnect) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<IProducerListener> dummyListener(new DummyProducerListener);
+ ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(OK, mProducer->connect(
+ dummyListener, NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(BAD_VALUE, mProducer->connect(
+ dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output));
+
+ ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA));
+ ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
} // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 9c2e838b09..5848c74b8c 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -490,7 +490,7 @@ void produceOneFrame(const sp<ANativeWindow>& anw,
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
*stride = buf->getStride();
uint8_t* img = NULL;
diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/tests/DummyConsumer.h
index 0511e165c2..502bdf981b 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/tests/DummyConsumer.h
@@ -19,9 +19,9 @@
namespace android {
struct DummyConsumer : public BnConsumerListener {
- virtual void onFrameAvailable(const BufferItem& /* item */) {}
- virtual void onBuffersReleased() {}
- virtual void onSidebandStreamChanged() {}
+ void onFrameAvailable(const BufferItem& /* item */) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
};
} // namespace android
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp
index 079962c8b9..ccd674fcb8 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -95,7 +95,7 @@ void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) {
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
uint8_t* img = NULL;
ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 9f3304731e..aa071f68b0 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "IGraphicBufferProducer_test"
//#define LOG_NDEBUG 0
+#include "DummyConsumer.h"
+
#include <gtest/gtest.h>
#include <utils/String8.h>
@@ -64,12 +66,6 @@ namespace {
const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
}; // namespace anonymous
-struct DummyConsumer : public BnConsumerListener {
- virtual void onFrameAvailable(const BufferItem& /* item */) {}
- virtual void onBuffersReleased() {}
- virtual void onSidebandStreamChanged() {}
-};
-
class IGraphicBufferProducerTest : public ::testing::Test {
protected:
@@ -196,7 +192,7 @@ protected:
};
status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
- return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage);
+ return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr);
}
void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -210,7 +206,7 @@ protected:
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)));
EXPECT_LE(0, *slot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot);
@@ -349,7 +345,7 @@ TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) {
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
EXPECT_LE(0, dequeuedSlot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
@@ -366,20 +362,12 @@ TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) {
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
{
- uint32_t width;
- uint32_t height;
- uint32_t transformHint;
- uint32_t numPendingBuffers;
- uint64_t nextFrameNumber;
-
- output.deflate(&width, &height, &transformHint, &numPendingBuffers,
- &nextFrameNumber);
-
- EXPECT_EQ(DEFAULT_WIDTH, width);
- EXPECT_EQ(DEFAULT_HEIGHT, height);
- EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
- EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
- EXPECT_EQ(2u, nextFrameNumber);
+ EXPECT_EQ(DEFAULT_WIDTH, output.width);
+ EXPECT_EQ(DEFAULT_HEIGHT, output.height);
+ EXPECT_EQ(DEFAULT_TRANSFORM_HINT, output.transformHint);
+ // Since queueBuffer was called exactly once
+ EXPECT_EQ(1u, output.numPendingBuffers);
+ EXPECT_EQ(2u, output.nextFrameNumber);
}
// Buffer was not in the dequeued state
@@ -416,7 +404,7 @@ TEST_F(IGraphicBufferProducerTest, Queue_ReturnsError) {
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
// Slot was enqueued without requesting a buffer
{
@@ -485,7 +473,7 @@ TEST_F(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) {
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)));
+ TEST_PRODUCER_USAGE_BITS, nullptr)));
// No return code, but at least test that it doesn't blow up...
// TODO: add a return code
@@ -534,7 +522,7 @@ TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)))
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
<< "iteration: " << i << ", slot: " << dequeuedSlot;
}
@@ -571,7 +559,7 @@ TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Fails) {
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT,
DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS)))
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
<< "slot: " << dequeuedSlot;
}
@@ -606,7 +594,8 @@ TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) {
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS))) << "slot : " << dequeuedSlot;
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
+ << "slot : " << dequeuedSlot;
ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
}
@@ -622,7 +611,8 @@ TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Fails) {
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
- TEST_PRODUCER_USAGE_BITS))) << "slot: " << dequeuedSlot;
+ TEST_PRODUCER_USAGE_BITS, nullptr)))
+ << "slot: " << dequeuedSlot;
}
// Abandon buffer queue
@@ -639,7 +629,7 @@ TEST_F(IGraphicBufferProducerTest,
sp<Fence> fence;
ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr));
}
TEST_F(IGraphicBufferProducerTest,
@@ -659,7 +649,8 @@ TEST_F(IGraphicBufferProducerTest,
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
- DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+ DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
+ nullptr)));
EXPECT_LE(0, slot);
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot);
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
new file mode 100644
index 0000000000..7ecf08cdb0
--- /dev/null
+++ b/libs/gui/tests/Malicious.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2017 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 <gui/BufferQueue.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+
+#include <android/native_window.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace test {
+
+class ProxyBQP : public BnGraphicBufferProducer {
+public:
+ ProxyBQP(const sp<IGraphicBufferProducer>& producer) : mProducer(producer) {}
+
+ // Pass through calls to mProducer
+ status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override {
+ return mProducer->requestBuffer(slot, buf);
+ }
+ status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
+ return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+ }
+ status_t setAsyncMode(bool async) override { return mProducer->setAsyncMode(async); }
+ status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t usage, FrameEventHistoryDelta* outTimestamps) override {
+ return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outTimestamps);
+ }
+ status_t detachBuffer(int slot) override { return mProducer->detachBuffer(slot); }
+ status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
+ return mProducer->detachNextBuffer(outBuffer, outFence);
+ }
+ status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) override {
+ return mProducer->attachBuffer(outSlot, buffer);
+ }
+ status_t queueBuffer(int slot, const QueueBufferInput& input,
+ QueueBufferOutput* output) override {
+ return mProducer->queueBuffer(slot, input, output);
+ }
+ status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
+ return mProducer->cancelBuffer(slot, fence);
+ }
+ int query(int what, int* value) override { return mProducer->query(what, value); }
+ status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
+ QueueBufferOutput* output) override {
+ return mProducer->connect(listener, api, producerControlledByApp, output);
+ }
+ status_t disconnect(int api, DisconnectMode mode) override {
+ return mProducer->disconnect(api, mode);
+ }
+ status_t setSidebandStream(const sp<NativeHandle>& stream) override {
+ return mProducer->setSidebandStream(stream);
+ }
+ void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t usage) override {
+ mProducer->allocateBuffers(width, height, format, usage);
+ }
+ status_t allowAllocation(bool allow) override { return mProducer->allowAllocation(allow); }
+ status_t setGenerationNumber(uint32_t generationNumber) override {
+ return mProducer->setGenerationNumber(generationNumber);
+ }
+ String8 getConsumerName() const override { return mProducer->getConsumerName(); }
+ status_t setSharedBufferMode(bool sharedBufferMode) override {
+ return mProducer->setSharedBufferMode(sharedBufferMode);
+ }
+ status_t setAutoRefresh(bool autoRefresh) override {
+ return mProducer->setAutoRefresh(autoRefresh);
+ }
+ status_t setDequeueTimeout(nsecs_t timeout) override {
+ return mProducer->setDequeueTimeout(timeout);
+ }
+ status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+ float outTransformMatrix[16]) override {
+ return mProducer->getLastQueuedBuffer(outBuffer, outFence, outTransformMatrix);
+ }
+ void getFrameTimestamps(FrameEventHistoryDelta*) override {}
+ status_t getUniqueId(uint64_t* outId) const override { return mProducer->getUniqueId(outId); }
+
+protected:
+ sp<IGraphicBufferProducer> mProducer;
+};
+
+class MaliciousBQP : public ProxyBQP {
+public:
+ MaliciousBQP(const sp<IGraphicBufferProducer>& producer) : ProxyBQP(producer) {}
+
+ void beMalicious(int32_t value) { mMaliciousValue = value; }
+
+ void setExpectedSlot(int32_t slot) { mExpectedSlot = slot; }
+
+ // Override dequeueBuffer, optionally corrupting the returned slot number
+ status_t dequeueBuffer(int* buf, sp<Fence>* fence, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) override {
+ EXPECT_EQ(BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(buf, fence, width, height, format, usage,
+ outTimestamps));
+ EXPECT_EQ(mExpectedSlot, *buf);
+ if (mMaliciousValue != 0) {
+ *buf = mMaliciousValue;
+ return NO_ERROR;
+ } else {
+ return BUFFER_NEEDS_REALLOCATION;
+ }
+ }
+
+private:
+ int32_t mMaliciousValue = 0;
+ int32_t mExpectedSlot = 0;
+};
+
+class DummyListener : public BnConsumerListener {
+public:
+ void onFrameAvailable(const BufferItem&) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+};
+
+sp<MaliciousBQP> getMaliciousBQP() {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ sp<IConsumerListener> listener = new DummyListener;
+ consumer->consumerConnect(listener, false);
+
+ sp<MaliciousBQP> malicious = new MaliciousBQP(producer);
+ return malicious;
+}
+
+TEST(Malicious, Bug36991414Max) {
+ sp<MaliciousBQP> malicious = getMaliciousBQP();
+ sp<Surface> surface = new Surface(malicious);
+
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+ ANativeWindow_Buffer buffer;
+ ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+ malicious->setExpectedSlot(1);
+ malicious->beMalicious(std::numeric_limits<int32_t>::max());
+ ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414Min) {
+ sp<MaliciousBQP> malicious = getMaliciousBQP();
+ sp<Surface> surface = new Surface(malicious);
+
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+ ANativeWindow_Buffer buffer;
+ ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+ malicious->setExpectedSlot(1);
+ malicious->beMalicious(std::numeric_limits<int32_t>::min());
+ ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414NegativeOne) {
+ sp<MaliciousBQP> malicious = getMaliciousBQP();
+ sp<Surface> surface = new Surface(malicious);
+
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+ ANativeWindow_Buffer buffer;
+ ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+ malicious->setExpectedSlot(1);
+ malicious->beMalicious(-1);
+ ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+TEST(Malicious, Bug36991414NumSlots) {
+ sp<MaliciousBQP> malicious = getMaliciousBQP();
+ sp<Surface> surface = new Surface(malicious);
+
+ ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false));
+ ANativeWindow_Buffer buffer;
+ ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr));
+ ASSERT_EQ(NO_ERROR, surface->unlockAndPost());
+
+ malicious->setExpectedSlot(1);
+ malicious->beMalicious(BufferQueueDefs::NUM_BUFFER_SLOTS);
+ ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr));
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/gui/tests/SRGB_test.cpp b/libs/gui/tests/SRGB_test.cpp
deleted file mode 100644
index c2640cdceb..0000000000
--- a/libs/gui/tests/SRGB_test.cpp
+++ /dev/null
@@ -1,486 +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_TAG "SRGB_test"
-//#define LOG_NDEBUG 0
-
-// Ignore for this file because it flags every instance of
-// ASSERT_EQ(GL_NO_ERROR, glGetError());
-#pragma clang diagnostic ignored "-Wsign-compare"
-
-#include "GLTest.h"
-
-#include <math.h>
-
-#include <gui/CpuConsumer.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-
-#include <android/native_window.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-
-class SRGBTest : public ::testing::Test {
-protected:
- // Class constants
- enum {
- DISPLAY_WIDTH = 512,
- DISPLAY_HEIGHT = 512,
- PIXEL_SIZE = 4, // bytes or components
- DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT * PIXEL_SIZE,
- ALPHA_VALUE = 223, // should be in [0, 255]
- TOLERANCE = 1,
- };
- static const char SHOW_DEBUG_STRING[];
-
- SRGBTest() :
- mInputSurface(), mCpuConsumer(), mLockedBuffer(),
- mEglDisplay(EGL_NO_DISPLAY), mEglConfig(),
- mEglContext(EGL_NO_CONTEXT), mEglSurface(EGL_NO_SURFACE),
- mComposerClient(), mSurfaceControl(), mOutputSurface() {
- }
-
- virtual ~SRGBTest() {
- if (mEglDisplay != EGL_NO_DISPLAY) {
- if (mEglSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEglDisplay, mEglSurface);
- }
- if (mEglContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEglDisplay, mEglContext);
- }
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT);
- eglTerminate(mEglDisplay);
- }
- }
-
- virtual void SetUp() {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize(
- DISPLAY_WIDTH, DISPLAY_HEIGHT));
- mCpuConsumer = new CpuConsumer(consumer, 1);
- String8 name("CpuConsumer_for_SRGBTest");
- mCpuConsumer->setName(name);
- mInputSurface = new Surface(producer);
-
- ASSERT_NO_FATAL_FAILURE(createEGLSurface(mInputSurface.get()));
- ASSERT_NO_FATAL_FAILURE(createDebugSurface());
- }
-
- virtual void TearDown() {
- ASSERT_NO_FATAL_FAILURE(copyToDebugSurface());
- ASSERT_TRUE(mLockedBuffer.data != NULL);
- ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
- }
-
- static float linearToSRGB(float l) {
- if (l <= 0.0031308f) {
- return l * 12.92f;
- } else {
- return 1.055f * pow(l, (1 / 2.4f)) - 0.055f;
- }
- }
-
- static float srgbToLinear(float s) {
- if (s <= 0.04045) {
- return s / 12.92f;
- } else {
- return pow(((s + 0.055f) / 1.055f), 2.4f);
- }
- }
-
- static uint8_t srgbToLinear(uint8_t u) {
- float f = u / 255.0f;
- return static_cast<uint8_t>(srgbToLinear(f) * 255.0f + 0.5f);
- }
-
- void fillTexture(bool writeAsSRGB) {
- uint8_t* textureData = new uint8_t[DISPLAY_SIZE];
-
- for (int y = 0; y < DISPLAY_HEIGHT; ++y) {
- for (int x = 0; x < DISPLAY_WIDTH; ++x) {
- float realValue = static_cast<float>(x) / (DISPLAY_WIDTH - 1);
- realValue *= ALPHA_VALUE / 255.0f; // Premultiply by alpha
- if (writeAsSRGB) {
- realValue = linearToSRGB(realValue);
- }
-
- int offset = (y * DISPLAY_WIDTH + x) * PIXEL_SIZE;
- for (int c = 0; c < 3; ++c) {
- uint8_t intValue = static_cast<uint8_t>(
- realValue * 255.0f + 0.5f);
- textureData[offset + c] = intValue;
- }
- textureData[offset + 3] = ALPHA_VALUE;
- }
- }
-
- glTexImage2D(GL_TEXTURE_2D, 0, writeAsSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- textureData);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
-
- delete[] textureData;
- }
-
- void initShaders() {
- static const char vertexSource[] =
- "attribute vec4 vPosition;\n"
- "varying vec2 texCoords;\n"
- "void main() {\n"
- " texCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
- " gl_Position = vPosition;\n"
- "}\n";
-
- static const char fragmentSource[] =
- "precision mediump float;\n"
- "uniform sampler2D texSampler;\n"
- "varying vec2 texCoords;\n"
- "void main() {\n"
- " gl_FragColor = texture2D(texSampler, texCoords);\n"
- "}\n";
-
- GLuint program;
- {
- SCOPED_TRACE("Creating shader program");
- ASSERT_NO_FATAL_FAILURE(GLTest::createProgram(
- vertexSource, fragmentSource, &program));
- }
-
- GLint positionHandle = glGetAttribLocation(program, "vPosition");
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- ASSERT_NE(-1, positionHandle);
-
- GLint samplerHandle = glGetUniformLocation(program, "texSampler");
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- ASSERT_NE(-1, samplerHandle);
-
- static const GLfloat vertices[] = {
- -1.0f, 1.0f,
- -1.0f, -1.0f,
- 1.0f, -1.0f,
- 1.0f, 1.0f,
- };
-
- glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glEnableVertexAttribArray(positionHandle);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
-
- glUseProgram(program);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glUniform1i(samplerHandle, 0);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
-
- GLuint textureHandle;
- glGenTextures(1, &textureHandle);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glBindTexture(GL_TEXTURE_2D, textureHandle);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- }
-
- void drawTexture(bool asSRGB, GLint x, GLint y, GLsizei width,
- GLsizei height) {
- ASSERT_NO_FATAL_FAILURE(fillTexture(asSRGB));
- glViewport(x, y, width, height);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- ASSERT_EQ(GL_NO_ERROR, glGetError());
- }
-
- void checkLockedBuffer(PixelFormat format, android_dataspace dataSpace) {
- ASSERT_EQ(mLockedBuffer.format, format);
- ASSERT_EQ(mLockedBuffer.width, DISPLAY_WIDTH);
- ASSERT_EQ(mLockedBuffer.height, DISPLAY_HEIGHT);
- ASSERT_EQ(mLockedBuffer.dataSpace, dataSpace);
- }
-
- static bool withinTolerance(int a, int b) {
- int diff = a - b;
- return diff >= 0 ? diff <= TOLERANCE : -diff <= TOLERANCE;
- }
-
- // Primary producer and consumer
- sp<Surface> mInputSurface;
- sp<CpuConsumer> mCpuConsumer;
- CpuConsumer::LockedBuffer mLockedBuffer;
-
- EGLDisplay mEglDisplay;
- EGLConfig mEglConfig;
- EGLContext mEglContext;
- EGLSurface mEglSurface;
-
- // Auxiliary display output
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mSurfaceControl;
- sp<Surface> mOutputSurface;
-
-private:
- void createEGLSurface(Surface* inputSurface) {
- mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
-
- EXPECT_TRUE(eglInitialize(mEglDisplay, NULL, NULL));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- static const EGLint configAttribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_NONE };
-
- EGLint numConfigs = 0;
- EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1,
- &numConfigs));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_GT(numConfigs, 0);
-
- static const EGLint contextAttribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 3,
- EGL_NONE } ;
-
- mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
- contextAttribs);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
-
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
- inputSurface, NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- }
-
- void createDebugSurface() {
- if (getenv(SHOW_DEBUG_STRING) == NULL) return;
-
- mComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- mSurfaceControl = mComposerClient->createSurface(
- String8("SRGBTest Surface"), DISPLAY_WIDTH, DISPLAY_HEIGHT,
- PIXEL_FORMAT_RGBA_8888);
-
- ASSERT_TRUE(mSurfaceControl != NULL);
- ASSERT_TRUE(mSurfaceControl->isValid());
-
- SurfaceComposerClient::openGlobalTransaction();
- ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
- ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
- SurfaceComposerClient::closeGlobalTransaction();
-
- ANativeWindow_Buffer outBuffer;
- ARect inOutDirtyBounds;
- mOutputSurface = mSurfaceControl->getSurface();
- mOutputSurface->lock(&outBuffer, &inOutDirtyBounds);
- uint8_t* bytePointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
- for (int y = 0; y < outBuffer.height; ++y) {
- int rowOffset = y * outBuffer.stride; // pixels
- for (int x = 0; x < outBuffer.width; ++x) {
- int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
- for (int c = 0; c < PIXEL_SIZE; ++c) {
- int offset = colOffset + c;
- bytePointer[offset] = ((c + 1) * 56) - 1;
- }
- }
- }
- mOutputSurface->unlockAndPost();
- }
-
- void copyToDebugSurface() {
- if (!mOutputSurface.get()) return;
-
- size_t bufferSize = mLockedBuffer.height * mLockedBuffer.stride *
- PIXEL_SIZE;
-
- ANativeWindow_Buffer outBuffer;
- ARect outBufferBounds;
- mOutputSurface->lock(&outBuffer, &outBufferBounds);
- ASSERT_EQ(mLockedBuffer.width, static_cast<uint32_t>(outBuffer.width));
- ASSERT_EQ(mLockedBuffer.height, static_cast<uint32_t>(outBuffer.height));
- ASSERT_EQ(mLockedBuffer.stride, static_cast<uint32_t>(outBuffer.stride));
-
- if (mLockedBuffer.format == outBuffer.format) {
- memcpy(outBuffer.bits, mLockedBuffer.data, bufferSize);
- } else {
- ASSERT_EQ(mLockedBuffer.format, PIXEL_FORMAT_RGBA_8888);
- ASSERT_EQ(mLockedBuffer.dataSpace, HAL_DATASPACE_SRGB);
- ASSERT_EQ(outBuffer.format, PIXEL_FORMAT_RGBA_8888);
- uint8_t* outPointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
- for (int y = 0; y < outBuffer.height; ++y) {
- int rowOffset = y * outBuffer.stride; // pixels
- for (int x = 0; x < outBuffer.width; ++x) {
- int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
-
- // RGB are converted
- for (int c = 0; c < (PIXEL_SIZE - 1); ++c) {
- outPointer[colOffset + c] = srgbToLinear(
- mLockedBuffer.data[colOffset + c]);
- }
-
- // Alpha isn't converted
- outPointer[colOffset + 3] =
- mLockedBuffer.data[colOffset + 3];
- }
- }
- }
- mOutputSurface->unlockAndPost();
-
- int sleepSeconds = atoi(getenv(SHOW_DEBUG_STRING));
- sleep(sleepSeconds);
- }
-};
-
-const char SRGBTest::SHOW_DEBUG_STRING[] = "DEBUG_OUTPUT_SECONDS";
-
-TEST_F(SRGBTest, GLRenderFromSRGBTexture) {
- ASSERT_NO_FATAL_FAILURE(initShaders());
-
- // The RGB texture is displayed in the top half
- ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, DISPLAY_HEIGHT / 2,
- DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
-
- // The SRGB texture is displayed in the bottom half
- ASSERT_NO_FATAL_FAILURE(drawTexture(true, 0, 0,
- DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
-
- eglSwapBuffers(mEglDisplay, mEglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Lock
- ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
- ASSERT_NO_FATAL_FAILURE(
- checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
-
- // Compare a pixel in the middle of each texture
- int midSRGBOffset = (DISPLAY_HEIGHT / 4) * mLockedBuffer.stride *
- PIXEL_SIZE;
- int midRGBOffset = midSRGBOffset * 3;
- midRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
- midSRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
- for (int c = 0; c < PIXEL_SIZE; ++c) {
- int expectedValue = mLockedBuffer.data[midRGBOffset + c];
- int actualValue = mLockedBuffer.data[midSRGBOffset + c];
- ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
- }
-
- // mLockedBuffer is unlocked in TearDown so we can copy data from it to
- // the debug surface if necessary
-}
-
-// XXX: Disabled since we don't currently expect this to work
-TEST_F(SRGBTest, DISABLED_RenderToSRGBSurface) {
- ASSERT_NO_FATAL_FAILURE(initShaders());
-
- // By default, the first buffer we write into will be RGB
-
- // Render an RGB texture across the whole surface
- ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
- DISPLAY_WIDTH, DISPLAY_HEIGHT));
- eglSwapBuffers(mEglDisplay, mEglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Lock
- ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
- ASSERT_NO_FATAL_FAILURE(
- checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
-
- // Save the values of the middle pixel for later comparison against SRGB
- uint8_t values[PIXEL_SIZE] = {};
- int middleOffset = (DISPLAY_HEIGHT / 2) * mLockedBuffer.stride *
- PIXEL_SIZE;
- middleOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
- for (int c = 0; c < PIXEL_SIZE; ++c) {
- values[c] = mLockedBuffer.data[middleOffset + c];
- }
-
- // Unlock
- ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
-
- // Switch to SRGB window surface
- static const int srgbAttribs[] = {
- EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
- EGL_NONE,
- };
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
- mInputSurface.get(), srgbAttribs);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Render the texture again
- ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
- DISPLAY_WIDTH, DISPLAY_HEIGHT));
- eglSwapBuffers(mEglDisplay, mEglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Lock
- ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
-
- // Make sure we actually got the SRGB buffer on the consumer side
- ASSERT_NO_FATAL_FAILURE(
- checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_SRGB));
-
- // Verify that the stored value is the same, accounting for RGB/SRGB
- for (int c = 0; c < PIXEL_SIZE; ++c) {
- // The alpha value should be equivalent before linear->SRGB
- float rgbAsSRGB = (c == 3) ? values[c] / 255.0f :
- linearToSRGB(values[c] / 255.0f);
- int expectedValue = rgbAsSRGB * 255.0f + 0.5f;
- int actualValue = mLockedBuffer.data[middleOffset + c];
- ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
- }
-
- // mLockedBuffer is unlocked in TearDown so we can copy data from it to
- // the debug surface if necessary
-}
-
-} // namespace android
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 498492e179..80e30da84a 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -81,7 +81,7 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) {
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -115,7 +115,7 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) {
// received the buffer back from the output BufferQueue
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
@@ -153,7 +153,7 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
uint32_t* dataIn;
@@ -190,7 +190,7 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
// received the buffer back from the output BufferQueues
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
TEST_F(StreamSplitterTest, OutputAbandonment) {
@@ -217,7 +217,7 @@ TEST_F(StreamSplitterTest, OutputAbandonment) {
sp<GraphicBuffer> buffer;
ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
// Abandon the output
@@ -230,7 +230,7 @@ TEST_F(StreamSplitterTest, OutputAbandonment) {
// Input should be abandoned
ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
- GRALLOC_USAGE_SW_WRITE_OFTEN));
+ GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
}
} // namespace android
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index b10d4ebde7..bd598e419e 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -23,6 +23,7 @@
#include <gtest/gtest.h>
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
+#include <gui/BufferQueue.h>
#include <system/graphics.h>
#include <utils/Log.h>
#include <utils/Thread.h>
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index 0606839506..0134273a07 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -41,7 +41,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) {
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with green
uint8_t* img = NULL;
@@ -65,7 +65,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) {
&anb));
ASSERT_TRUE(anb != NULL);
- buf = new GraphicBuffer(anb, false);
+ buf = GraphicBuffer::from(anb);
// Fill the buffer with red
ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 308bd7d69c..c6745d034d 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -42,7 +42,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) {
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with the a checkerboard pattern
uint8_t* img = NULL;
@@ -92,7 +92,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) {
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
// Fill the buffer with the a checkerboard pattern
uint8_t* img = NULL;
@@ -157,7 +157,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
&anb));
ASSERT_TRUE(anb != NULL);
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
uint8_t* img = NULL;
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
@@ -238,7 +238,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) {
return false;
}
- sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ sp<GraphicBuffer> buf(GraphicBuffer::from(anb));
const int yuvTexOffsetY = 0;
int stride = buf->getStride();
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0de60c983f..e18af17bde 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -18,19 +18,38 @@
#include <gtest/gtest.h>
-#include <binder/IMemory.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <binder/ProcessState.h>
+#include <configstore/Utils.h>
+#include <cutils/properties.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IDisplayEventConnection.h>
+#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <gui/BufferItemConsumer.h>
+#include <private/gui/ComposerService.h>
#include <ui/Rect.h>
#include <utils/String8.h>
-#include <private/gui/ComposerService.h>
-#include <binder/ProcessState.h>
+#include <limits>
+#include <thread>
namespace android {
+using namespace std::chrono_literals;
+// retrieve wide-color and hdr settings from configstore
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+static bool hasWideColorDisplay =
+ getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
+class FakeSurfaceComposer;
+class FakeProducerFrameEventHistory;
+
+static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
+
class SurfaceTest : public ::testing::Test {
protected:
@@ -42,6 +61,8 @@ protected:
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+ // TODO(brianderson): The following sometimes fails and is a source of
+ // test flakiness.
mSurfaceControl = mComposerClient->createSurface(
String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
@@ -77,6 +98,8 @@ TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenVisible) {
TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) {
mSurfaceControl.clear();
+ // Wait for the async clean-up to complete.
+ std::this_thread::sleep_for(50ms);
sp<ANativeWindow> anw(mSurface);
int result = -123;
@@ -96,7 +119,8 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) {
BufferQueue::createBufferQueue(&producer, &consumer);
sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ sp<IBinder> display(sf->getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(),
64, 64, 0, 0x7fffffff, false));
@@ -140,6 +164,14 @@ TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
EXPECT_EQ(NATIVE_WINDOW_SURFACE, result);
}
+TEST_F(SurfaceTest, LayerCountIsOne) {
+ sp<ANativeWindow> anw(mSurface);
+ int result = -123;
+ int err = anw->query(anw.get(), NATIVE_WINDOW_LAYER_COUNT, &result);
+ EXPECT_EQ(NO_ERROR, err);
+ EXPECT_EQ(1, result);
+}
+
TEST_F(SurfaceTest, QueryConsumerUsage) {
const int TEST_USAGE_FLAGS =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
@@ -232,6 +264,37 @@ TEST_F(SurfaceTest, GetConsumerName) {
EXPECT_STREQ("TestConsumer", surface->getConsumerName().string());
}
+TEST_F(SurfaceTest, GetWideColorSupport) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+ consumer->consumerConnect(dummyConsumer, false);
+ consumer->setConsumerName(String8("TestConsumer"));
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+
+ bool supported;
+ surface->getWideColorSupport(&supported);
+
+ // NOTE: This test assumes that device that supports
+ // wide-color (as indicated by BoardConfig) must also
+ // have a wide-color primary display.
+ // That assumption allows this test to cover devices
+ // that advertised a wide-color color mode without
+ // actually supporting wide-color to pass this test
+ // as well as the case of a device that does support
+ // wide-color (via BoardConfig) and has a wide-color
+ // primary display.
+ // NOT covered at this time is a device that supports
+ // wide color in the BoardConfig but does not support
+ // a wide-color color mode on the primary display.
+ ASSERT_EQ(hasWideColorDisplay, supported);
+}
+
TEST_F(SurfaceTest, DynamicSetBufferCount) {
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
@@ -258,4 +321,1268 @@ TEST_F(SurfaceTest, DynamicSetBufferCount) {
ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
}
+TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+ consumer->consumerConnect(dummyConsumer, false);
+ consumer->setConsumerName(String8("TestConsumer"));
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+ sp<DummyProducerListener> listener = new DummyProducerListener();
+ ASSERT_EQ(OK, surface->connect(
+ NATIVE_WINDOW_API_CPU,
+ /*listener*/listener,
+ /*reportBufferRemoval*/true));
+ const int BUFFER_COUNT = 4;
+ ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+ sp<GraphicBuffer> detachedBuffer;
+ sp<Fence> outFence;
+ int fences[BUFFER_COUNT];
+ ANativeWindowBuffer* buffers[BUFFER_COUNT];
+ // Allocate buffers because detachNextBuffer requires allocated buffers
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i]));
+ }
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i]));
+ }
+
+ // Test detached buffer is correctly reported
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ std::vector<sp<GraphicBuffer>> removedBuffers;
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ ASSERT_EQ(1u, removedBuffers.size());
+ ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle);
+ // Test the list is flushed one getAndFlushRemovedBuffers returns
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ ASSERT_EQ(0u, removedBuffers.size());
+
+
+ // Test removed buffer list is cleanup after next dequeueBuffer call
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[0], &fences[0]));
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ ASSERT_EQ(0u, removedBuffers.size());
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[0], fences[0]));
+
+ // Test removed buffer list is cleanup after next detachNextBuffer call
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ ASSERT_EQ(1u, removedBuffers.size());
+ ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle);
+
+ // Re-allocate buffers since all buffers are detached up to now
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i]));
+ }
+ for (int i = 0; i < BUFFER_COUNT; i++) {
+ ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i]));
+ }
+
+ ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence));
+ ASSERT_EQ(NO_ERROR, surface->attachBuffer(detachedBuffer.get()));
+ ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers));
+ // Depends on which slot GraphicBufferProducer impl pick, the attach call might
+ // get 0 or 1 buffer removed.
+ ASSERT_LE(removedBuffers.size(), 1u);
+}
+
+TEST_F(SurfaceTest, TestGetLastDequeueStartTime) {
+ sp<ANativeWindow> anw(mSurface);
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
+
+ ANativeWindowBuffer* buffer = nullptr;
+ int32_t fenceFd = -1;
+
+ nsecs_t before = systemTime(CLOCK_MONOTONIC);
+ anw->dequeueBuffer(anw.get(), &buffer, &fenceFd);
+ nsecs_t after = systemTime(CLOCK_MONOTONIC);
+
+ nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime();
+ ASSERT_LE(before, lastDequeueTime);
+ ASSERT_GE(after, lastDequeueTime);
}
+
+class FakeConsumer : public BnConsumerListener {
+public:
+ void onFrameAvailable(const BufferItem& /*item*/) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+
+ void addAndGetFrameTimestamps(
+ const NewFrameEventsEntry* newTimestamps,
+ FrameEventHistoryDelta* outDelta) override {
+ if (newTimestamps) {
+ if (mGetFrameTimestampsEnabled) {
+ EXPECT_GT(mNewFrameEntryOverride.frameNumber, 0u) <<
+ "Test should set mNewFrameEntryOverride before queuing "
+ "a frame.";
+ EXPECT_EQ(newTimestamps->frameNumber,
+ mNewFrameEntryOverride.frameNumber) <<
+ "Test attempting to add NewFrameEntryOverride with "
+ "incorrect frame number.";
+ mFrameEventHistory.addQueue(mNewFrameEntryOverride);
+ mNewFrameEntryOverride.frameNumber = 0;
+ }
+ mAddFrameTimestampsCount++;
+ mLastAddedFrameNumber = newTimestamps->frameNumber;
+ }
+ if (outDelta) {
+ mFrameEventHistory.getAndResetDelta(outDelta);
+ mGetFrameTimestampsCount++;
+ }
+ mAddAndGetFrameTimestampsCallCount++;
+ }
+
+ bool mGetFrameTimestampsEnabled = false;
+
+ ConsumerFrameEventHistory mFrameEventHistory;
+ int mAddAndGetFrameTimestampsCallCount = 0;
+ int mAddFrameTimestampsCount = 0;
+ int mGetFrameTimestampsCount = 0;
+ uint64_t mLastAddedFrameNumber = NO_FRAME_INDEX;
+
+ NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr };
+};
+
+
+class FakeSurfaceComposer : public ISurfaceComposer{
+public:
+ ~FakeSurfaceComposer() override {}
+
+ void setSupportsPresent(bool supportsPresent) {
+ mSupportsPresent = supportsPresent;
+ }
+
+ sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
+ sp<ISurfaceComposerClient> createScopedConnection(
+ const sp<IGraphicBufferProducer>& /* parent */) override {
+ return nullptr;
+ }
+ sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource)
+ override {
+ return nullptr;
+ }
+ sp<IBinder> createDisplay(const String8& /*displayName*/,
+ bool /*secure*/) override { return nullptr; }
+ void destroyDisplay(const sp<IBinder>& /*display */) override {}
+ sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; }
+ void setTransactionState(const Vector<ComposerState>& /*state*/,
+ const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/)
+ override {}
+ void bootFinished() override {}
+ bool authenticateSurfaceTexture(
+ const sp<IGraphicBufferProducer>& /*surface*/) const override {
+ return false;
+ }
+
+ status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported)
+ const override {
+ *outSupported = {
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
+ FrameEvent::GPU_COMPOSITION_DONE,
+ FrameEvent::DEQUEUE_READY,
+ FrameEvent::RELEASE
+ };
+ if (mSupportsPresent) {
+ outSupported->push_back(
+ FrameEvent::DISPLAY_PRESENT);
+ }
+ return NO_ERROR;
+ }
+
+ void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
+ status_t getDisplayConfigs(const sp<IBinder>& /*display*/,
+ Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
+ status_t getDisplayStats(const sp<IBinder>& /*display*/,
+ DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
+ int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
+ status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/)
+ override {
+ return NO_ERROR;
+ }
+ status_t getDisplayColorModes(const sp<IBinder>& /*display*/,
+ Vector<android_color_mode_t>* /*outColorModes*/) override {
+ return NO_ERROR;
+ }
+ android_color_mode_t getActiveColorMode(const sp<IBinder>& /*display*/)
+ override {
+ return HAL_COLOR_MODE_NATIVE;
+ }
+ status_t setActiveColorMode(const sp<IBinder>& /*display*/,
+ android_color_mode_t /*colorMode*/) override { return NO_ERROR; }
+ status_t captureScreen(const sp<IBinder>& /*display*/,
+ const sp<IGraphicBufferProducer>& /*producer*/,
+ Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
+ int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/,
+ bool /*useIdentityTransform*/,
+ Rotation /*rotation*/) override { return NO_ERROR; }
+ status_t clearAnimationFrameStats() override { return NO_ERROR; }
+ status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
+ return NO_ERROR;
+ }
+ status_t getHdrCapabilities(const sp<IBinder>& /*display*/,
+ HdrCapabilities* /*outCapabilities*/) const override {
+ return NO_ERROR;
+ }
+ status_t enableVSyncInjections(bool /*enable*/) override {
+ return NO_ERROR;
+ }
+ status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
+
+protected:
+ IBinder* onAsBinder() override { return nullptr; }
+
+private:
+ bool mSupportsPresent{true};
+};
+
+class FakeProducerFrameEventHistory : public ProducerFrameEventHistory {
+public:
+ FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap)
+ : mFenceMap(fenceMap) {}
+
+ ~FakeProducerFrameEventHistory() {}
+
+ void updateAcquireFence(uint64_t frameNumber,
+ std::shared_ptr<FenceTime>&& acquire) override {
+ // Verify the acquire fence being added isn't the one from the consumer.
+ EXPECT_NE(mConsumerAcquireFence, acquire);
+ // Override the fence, so we can verify this was called by the
+ // producer after the frame is queued.
+ ProducerFrameEventHistory::updateAcquireFence(frameNumber,
+ std::shared_ptr<FenceTime>(mAcquireFenceOverride));
+ }
+
+ void setAcquireFenceOverride(
+ const std::shared_ptr<FenceTime>& acquireFenceOverride,
+ const std::shared_ptr<FenceTime>& consumerAcquireFence) {
+ mAcquireFenceOverride = acquireFenceOverride;
+ mConsumerAcquireFence = consumerAcquireFence;
+ }
+
+protected:
+ std::shared_ptr<FenceTime> createFenceTime(const sp<Fence>& fence)
+ const override {
+ return mFenceMap->createFenceTimeForTest(fence);
+ }
+
+ FenceToFenceTimeMap* mFenceMap{nullptr};
+
+ std::shared_ptr<FenceTime> mAcquireFenceOverride{FenceTime::NO_FENCE};
+ std::shared_ptr<FenceTime> mConsumerAcquireFence{FenceTime::NO_FENCE};
+};
+
+
+class TestSurface : public Surface {
+public:
+ TestSurface(const sp<IGraphicBufferProducer>& bufferProducer,
+ FenceToFenceTimeMap* fenceMap)
+ : Surface(bufferProducer),
+ mFakeSurfaceComposer(new FakeSurfaceComposer) {
+ mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap);
+ mFrameEventHistory.reset(mFakeFrameEventHistory);
+ }
+
+ ~TestSurface() override {}
+
+ sp<ISurfaceComposer> composerService() const override {
+ return mFakeSurfaceComposer;
+ }
+
+ nsecs_t now() const override {
+ return mNow;
+ }
+
+ void setNow(nsecs_t now) {
+ mNow = now;
+ }
+
+public:
+ sp<FakeSurfaceComposer> mFakeSurfaceComposer;
+ nsecs_t mNow = 0;
+
+ // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory,
+ // but this raw pointer gives access to test functionality.
+ FakeProducerFrameEventHistory* mFakeFrameEventHistory;
+};
+
+
+class GetFrameTimestampsTest : public ::testing::Test {
+protected:
+ struct FenceAndFenceTime {
+ explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap)
+ : mFence(new Fence),
+ mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {}
+ sp<Fence> mFence { nullptr };
+ std::shared_ptr<FenceTime> mFenceTime { nullptr };
+ };
+
+ struct RefreshEvents {
+ RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart)
+ : mFenceMap(fenceMap),
+ kCompositorTiming(
+ {refreshStart, refreshStart + 1, refreshStart + 2 }),
+ kStartTime(refreshStart + 3),
+ kGpuCompositionDoneTime(refreshStart + 4),
+ kPresentTime(refreshStart + 5) {}
+
+ void signalPostCompositeFences() {
+ mFenceMap.signalAllForTest(
+ mGpuCompositionDone.mFence, kGpuCompositionDoneTime);
+ mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime);
+ }
+
+ FenceToFenceTimeMap& mFenceMap;
+
+ FenceAndFenceTime mGpuCompositionDone { mFenceMap };
+ FenceAndFenceTime mPresent { mFenceMap };
+
+ const CompositorTiming kCompositorTiming;
+
+ const nsecs_t kStartTime;
+ const nsecs_t kGpuCompositionDoneTime;
+ const nsecs_t kPresentTime;
+ };
+
+ struct FrameEvents {
+ FrameEvents(FenceToFenceTimeMap& fenceMap, nsecs_t frameStartTime)
+ : mFenceMap(fenceMap),
+ kPostedTime(frameStartTime + 100),
+ kRequestedPresentTime(frameStartTime + 200),
+ kProducerAcquireTime(frameStartTime + 300),
+ kConsumerAcquireTime(frameStartTime + 301),
+ kLatchTime(frameStartTime + 500),
+ kDequeueReadyTime(frameStartTime + 600),
+ kReleaseTime(frameStartTime + 700),
+ mRefreshes {
+ { mFenceMap, frameStartTime + 410 },
+ { mFenceMap, frameStartTime + 420 },
+ { mFenceMap, frameStartTime + 430 } } {}
+
+ void signalQueueFences() {
+ mFenceMap.signalAllForTest(
+ mAcquireConsumer.mFence, kConsumerAcquireTime);
+ mFenceMap.signalAllForTest(
+ mAcquireProducer.mFence, kProducerAcquireTime);
+ }
+
+ void signalRefreshFences() {
+ for (auto& re : mRefreshes) {
+ re.signalPostCompositeFences();
+ }
+ }
+
+ void signalReleaseFences() {
+ mFenceMap.signalAllForTest(mRelease.mFence, kReleaseTime);
+ }
+
+ FenceToFenceTimeMap& mFenceMap;
+
+ FenceAndFenceTime mAcquireConsumer { mFenceMap };
+ FenceAndFenceTime mAcquireProducer { mFenceMap };
+ FenceAndFenceTime mRelease { mFenceMap };
+
+ const nsecs_t kPostedTime;
+ const nsecs_t kRequestedPresentTime;
+ const nsecs_t kProducerAcquireTime;
+ const nsecs_t kConsumerAcquireTime;
+ const nsecs_t kLatchTime;
+ const nsecs_t kDequeueReadyTime;
+ const nsecs_t kReleaseTime;
+
+ RefreshEvents mRefreshes[3];
+ };
+
+ GetFrameTimestampsTest() {}
+
+ virtual void SetUp() {
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mFakeConsumer = new FakeConsumer;
+ mCfeh = &mFakeConsumer->mFrameEventHistory;
+ mConsumer->consumerConnect(mFakeConsumer, false);
+ mConsumer->setConsumerName(String8("TestConsumer"));
+ mSurface = new TestSurface(mProducer, &mFenceMap);
+ mWindow = mSurface;
+
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(),
+ NATIVE_WINDOW_API_CPU));
+ native_window_set_buffer_count(mWindow.get(), 4);
+ }
+
+ void disableFrameTimestamps() {
+ mFakeConsumer->mGetFrameTimestampsEnabled = false;
+ native_window_enable_frame_timestamps(mWindow.get(), 0);
+ mFrameTimestampsEnabled = false;
+ }
+
+ void enableFrameTimestamps() {
+ mFakeConsumer->mGetFrameTimestampsEnabled = true;
+ native_window_enable_frame_timestamps(mWindow.get(), 1);
+ mFrameTimestampsEnabled = true;
+ }
+
+ int getAllFrameTimestamps(uint64_t frameId) {
+ return native_window_get_frame_timestamps(mWindow.get(), frameId,
+ &outRequestedPresentTime, &outAcquireTime, &outLatchTime,
+ &outFirstRefreshStartTime, &outLastRefreshStartTime,
+ &outGpuCompositionDoneTime, &outDisplayPresentTime,
+ &outDequeueReadyTime, &outReleaseTime);
+ }
+
+ void resetTimestamps() {
+ outRequestedPresentTime = -1;
+ outAcquireTime = -1;
+ outLatchTime = -1;
+ outFirstRefreshStartTime = -1;
+ outLastRefreshStartTime = -1;
+ outGpuCompositionDoneTime = -1;
+ outDisplayPresentTime = -1;
+ outDequeueReadyTime = -1;
+ outReleaseTime = -1;
+ }
+
+ uint64_t getNextFrameId() {
+ uint64_t frameId = -1;
+ int status = native_window_get_next_frame_id(mWindow.get(), &frameId);
+ EXPECT_EQ(status, NO_ERROR);
+ return frameId;
+ }
+
+ void dequeueAndQueue(uint64_t frameIndex) {
+ int fence = -1;
+ ANativeWindowBuffer* buffer = nullptr;
+ ASSERT_EQ(NO_ERROR,
+ mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+
+ int oldAddFrameTimestampsCount =
+ mFakeConsumer->mAddFrameTimestampsCount;
+
+ FrameEvents* frame = &mFrames[frameIndex];
+ uint64_t frameNumber = frameIndex + 1;
+
+ NewFrameEventsEntry fe;
+ fe.frameNumber = frameNumber;
+ fe.postedTime = frame->kPostedTime;
+ fe.requestedPresentTime = frame->kRequestedPresentTime;
+ fe.acquireFence = frame->mAcquireConsumer.mFenceTime;
+ mFakeConsumer->mNewFrameEntryOverride = fe;
+
+ mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+ frame->mAcquireProducer.mFenceTime,
+ frame->mAcquireConsumer.mFenceTime);
+
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+
+ EXPECT_EQ(frameNumber, mFakeConsumer->mLastAddedFrameNumber);
+
+ EXPECT_EQ(
+ oldAddFrameTimestampsCount + (mFrameTimestampsEnabled ? 1 : 0),
+ mFakeConsumer->mAddFrameTimestampsCount);
+ }
+
+ void addFrameEvents(
+ bool gpuComposited, uint64_t iOldFrame, int64_t iNewFrame) {
+ FrameEvents* oldFrame =
+ (iOldFrame == NO_FRAME_INDEX) ? nullptr : &mFrames[iOldFrame];
+ FrameEvents* newFrame = &mFrames[iNewFrame];
+
+ uint64_t nOldFrame = iOldFrame + 1;
+ uint64_t nNewFrame = iNewFrame + 1;
+
+ // Latch, Composite, and Release the frames in a plausible order.
+ // Note: The timestamps won't necessarily match the order, but
+ // that's okay for the purposes of this test.
+ std::shared_ptr<FenceTime> gpuDoneFenceTime = FenceTime::NO_FENCE;
+
+ // Composite the previous frame one more time, which helps verify
+ // LastRefresh is updated properly.
+ if (oldFrame != nullptr) {
+ mCfeh->addPreComposition(nOldFrame,
+ oldFrame->mRefreshes[2].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime,
+ oldFrame->mRefreshes[2].mPresent.mFenceTime,
+ oldFrame->mRefreshes[2].kCompositorTiming);
+ }
+
+ // Latch the new frame.
+ mCfeh->addLatch(nNewFrame, newFrame->kLatchTime);
+
+ mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[0].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ // HWC2 releases the previous buffer after a new latch just before
+ // calling postComposition.
+ if (oldFrame != nullptr) {
+ mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime,
+ std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime));
+ }
+ mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+ newFrame->mRefreshes[0].mPresent.mFenceTime,
+ newFrame->mRefreshes[0].kCompositorTiming);
+
+ mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[1].kStartTime);
+ gpuDoneFenceTime = gpuComposited ?
+ newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime :
+ FenceTime::NO_FENCE;
+ mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+ newFrame->mRefreshes[1].mPresent.mFenceTime,
+ newFrame->mRefreshes[1].kCompositorTiming);
+ }
+
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<FakeConsumer> mFakeConsumer;
+ ConsumerFrameEventHistory* mCfeh;
+ sp<TestSurface> mSurface;
+ sp<ANativeWindow> mWindow;
+
+ FenceToFenceTimeMap mFenceMap;
+
+ bool mFrameTimestampsEnabled = false;
+
+ int64_t outRequestedPresentTime = -1;
+ int64_t outAcquireTime = -1;
+ int64_t outLatchTime = -1;
+ int64_t outFirstRefreshStartTime = -1;
+ int64_t outLastRefreshStartTime = -1;
+ int64_t outGpuCompositionDoneTime = -1;
+ int64_t outDisplayPresentTime = -1;
+ int64_t outDequeueReadyTime = -1;
+ int64_t outReleaseTime = -1;
+
+ FrameEvents mFrames[3] {
+ { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } };
+};
+
+
+// This test verifies that the frame timestamps are not retrieved when not
+// explicitly enabled via native_window_enable_frame_timestamps.
+// We want to check this to make sure there's no overhead for users
+// that don't need the timestamp information.
+TEST_F(GetFrameTimestampsTest, DefaultDisabled) {
+ int fence;
+ ANativeWindowBuffer* buffer;
+
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ const uint64_t fId = getNextFrameId();
+
+ // Verify the producer doesn't get frame timestamps piggybacked on dequeue.
+ ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify the producer doesn't get frame timestamps piggybacked on queue.
+ // It is okay that frame timestamps are added in the consumer since it is
+ // still needed for SurfaceFlinger dumps.
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+ EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify attempts to get frame timestamps fail.
+ int result = getAllFrameTimestamps(fId);
+ EXPECT_EQ(INVALID_OPERATION, result);
+ EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify compositor timing query fails.
+ nsecs_t compositeDeadline = 0;
+ nsecs_t compositeInterval = 0;
+ nsecs_t compositeToPresentLatency = 0;
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(INVALID_OPERATION, result);
+}
+
+// This test verifies that the frame timestamps are retrieved if explicitly
+// enabled via native_window_enable_frame_timestamps.
+TEST_F(GetFrameTimestampsTest, EnabledSimple) {
+ CompositorTiming initialCompositorTiming {
+ 1000000000, // 1s deadline
+ 16666667, // 16ms interval
+ 50000000, // 50ms present latency
+ };
+ mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+ enableFrameTimestamps();
+
+ // Verify the compositor timing query gets the initial compositor values
+ // after timststamps are enabled; even before the first frame is queued
+ // or dequeued.
+ nsecs_t compositeDeadline = 0;
+ nsecs_t compositeInterval = 0;
+ nsecs_t compositeToPresentLatency = 0;
+ mSurface->setNow(initialCompositorTiming.deadline - 1);
+ int result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+ EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+ EXPECT_EQ(initialCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ int fence;
+ ANativeWindowBuffer* buffer;
+
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount);
+
+ const uint64_t fId1 = getNextFrameId();
+
+ // Verify getFrameTimestamps is piggybacked on dequeue.
+ ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+ EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount);
+
+ NewFrameEventsEntry f1;
+ f1.frameNumber = 1;
+ f1.postedTime = mFrames[0].kPostedTime;
+ f1.requestedPresentTime = mFrames[0].kRequestedPresentTime;
+ f1.acquireFence = mFrames[0].mAcquireConsumer.mFenceTime;
+ mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+ mFrames[0].mAcquireProducer.mFenceTime,
+ mFrames[0].mAcquireConsumer.mFenceTime);
+ mFakeConsumer->mNewFrameEntryOverride = f1;
+ mFrames[0].signalQueueFences();
+
+ // Verify getFrameTimestamps is piggybacked on queue.
+ ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+ EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+ EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber);
+ EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount);
+
+ // Verify queries for timestamps that the producer doesn't know about
+ // triggers a call to see if the consumer has any new timestamps.
+ result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentSupported) {
+ bool displayPresentSupported = true;
+ mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported);
+
+ // Verify supported bits are forwarded.
+ int supportsPresent = -1;
+ mWindow.get()->query(mWindow.get(),
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+ EXPECT_EQ(displayPresentSupported, supportsPresent);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentNotSupported) {
+ bool displayPresentSupported = false;
+ mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported);
+
+ // Verify supported bits are forwarded.
+ int supportsPresent = -1;
+ mWindow.get()->query(mWindow.get(),
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+ EXPECT_EQ(displayPresentSupported, supportsPresent);
+}
+
+TEST_F(GetFrameTimestampsTest, SnapToNextTickBasic) {
+ nsecs_t phase = 4000;
+ nsecs_t interval = 1000;
+
+ // Timestamp in previous interval.
+ nsecs_t timestamp = 3500;
+ EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp in next interval.
+ timestamp = 4500;
+ EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp multiple intervals before.
+ timestamp = 2500;
+ EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp multiple intervals after.
+ timestamp = 6500;
+ EXPECT_EQ(7000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp on previous interval.
+ timestamp = 3000;
+ EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp on next interval.
+ timestamp = 5000;
+ EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+
+ // Timestamp equal to phase.
+ timestamp = 4000;
+ EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick(
+ timestamp, phase, interval));
+}
+
+// int(big_timestamp / interval) < 0, which can cause a crash or invalid result
+// if the number of intervals elapsed is internally stored in an int.
+TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) {
+ nsecs_t phase = 0;
+ nsecs_t interval = 4000;
+ nsecs_t big_timestamp = 8635916564000;
+ int32_t intervals = big_timestamp / interval;
+
+ EXPECT_LT(intervals, 0);
+ EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick(
+ big_timestamp, phase, interval));
+ EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick(
+ big_timestamp, big_timestamp, interval));
+}
+
+// This verifies the compositor timing is updated by refresh events
+// and piggy backed on a queue, dequeue, and enabling of timestamps..
+TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) {
+ CompositorTiming initialCompositorTiming {
+ 1000000000, // 1s deadline
+ 16666667, // 16ms interval
+ 50000000, // 50ms present latency
+ };
+ mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+ enableFrameTimestamps();
+
+ // We get the initial values before any frames are submitted.
+ nsecs_t compositeDeadline = 0;
+ nsecs_t compositeInterval = 0;
+ nsecs_t compositeToPresentLatency = 0;
+ mSurface->setNow(initialCompositorTiming.deadline - 1);
+ int result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+ EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+ EXPECT_EQ(initialCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+
+ // Still get the initial values because the frame events for frame 0
+ // didn't get a chance to piggyback on a queue or dequeue yet.
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+ EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+ EXPECT_EQ(initialCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ const uint64_t fId2 = getNextFrameId();
+ dequeueAndQueue(1);
+ addFrameEvents(true, 0, 1);
+
+ // Now expect the composite values associated with frame 1.
+ mSurface->setNow(mFrames[0].mRefreshes[1].kCompositorTiming.deadline);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.deadline,
+ compositeDeadline);
+ EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.interval,
+ compositeInterval);
+ EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ dequeueAndQueue(2);
+ addFrameEvents(true, 1, 2);
+
+ // Now expect the composite values associated with frame 2.
+ mSurface->setNow(mFrames[1].mRefreshes[1].kCompositorTiming.deadline);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.deadline,
+ compositeDeadline);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.interval,
+ compositeInterval);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+
+ // Re-enabling frame timestamps should get the latest values.
+ disableFrameTimestamps();
+ enableFrameTimestamps();
+
+ // Now expect the composite values associated with frame 3.
+ mSurface->setNow(mFrames[2].mRefreshes[1].kCompositorTiming.deadline);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.deadline,
+ compositeDeadline);
+ EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.interval,
+ compositeInterval);
+ EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.presentLatency,
+ compositeToPresentLatency);
+}
+
+// This verifies the compositor deadline properly snaps to the the next
+// deadline based on the current time.
+TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) {
+ CompositorTiming initialCompositorTiming {
+ 1000000000, // 1s deadline
+ 16666667, // 16ms interval
+ 50000000, // 50ms present latency
+ };
+ mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+ enableFrameTimestamps();
+
+ nsecs_t compositeDeadline = 0;
+ nsecs_t compositeInterval = 0;
+ nsecs_t compositeToPresentLatency = 0;
+
+ // A "now" just before the deadline snaps to the deadline.
+ mSurface->setNow(initialCompositorTiming.deadline - 1);
+ int result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+ nsecs_t expectedDeadline = initialCompositorTiming.deadline;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+
+ // A "now" just after the deadline snaps properly.
+ mSurface->setNow(initialCompositorTiming.deadline + 1);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ expectedDeadline =
+ initialCompositorTiming.deadline +initialCompositorTiming.interval;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+ const uint64_t fId2 = getNextFrameId();
+ dequeueAndQueue(1);
+ addFrameEvents(true, 0, 1);
+
+ // A "now" just after the next interval snaps properly.
+ mSurface->setNow(
+ mFrames[0].mRefreshes[1].kCompositorTiming.deadline +
+ mFrames[0].mRefreshes[1].kCompositorTiming.interval + 1);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ expectedDeadline =
+ mFrames[0].mRefreshes[1].kCompositorTiming.deadline +
+ mFrames[0].mRefreshes[1].kCompositorTiming.interval * 2;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+ dequeueAndQueue(2);
+ addFrameEvents(true, 1, 2);
+
+ // A "now" over 1 interval before the deadline snaps properly.
+ mSurface->setNow(
+ mFrames[1].mRefreshes[1].kCompositorTiming.deadline -
+ mFrames[1].mRefreshes[1].kCompositorTiming.interval - 1);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ expectedDeadline =
+ mFrames[1].mRefreshes[1].kCompositorTiming.deadline -
+ mFrames[1].mRefreshes[1].kCompositorTiming.interval;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+ // Re-enabling frame timestamps should get the latest values.
+ disableFrameTimestamps();
+ enableFrameTimestamps();
+
+ // A "now" over 2 intervals before the deadline snaps properly.
+ mSurface->setNow(
+ mFrames[2].mRefreshes[1].kCompositorTiming.deadline -
+ mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2 - 1);
+ result = native_window_get_compositor_timing(mWindow.get(),
+ &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+ EXPECT_EQ(NO_ERROR, result);
+ expectedDeadline =
+ mFrames[2].mRefreshes[1].kCompositorTiming.deadline -
+ mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2;
+ EXPECT_EQ(expectedDeadline, compositeDeadline);
+}
+
+// This verifies the timestamps recorded in the consumer's
+// FrameTimestampsHistory are properly retrieved by the producer for the
+// correct frames.
+TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) {
+ enableFrameTimestamps();
+
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ const uint64_t fId2 = getNextFrameId();
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ mFrames[0].signalRefreshFences();
+ addFrameEvents(true, 0, 1);
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify timestamps are correct for frame 1.
+ resetTimestamps();
+ int result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+
+ // Verify timestamps are correct for frame 2.
+ resetTimestamps();
+ result = getAllFrameTimestamps(fId2);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+}
+
+// This test verifies the acquire fence recorded by the consumer is not sent
+// back to the producer and the producer saves its own fence.
+TEST_F(GetFrameTimestampsTest, QueueTimestampsNoSync) {
+ enableFrameTimestamps();
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+
+ // Verify queue-related timestamps for f1 are available immediately in the
+ // producer without asking the consumer again, even before signaling the
+ // acquire fence.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outAcquireTime);
+
+ // Signal acquire fences. Verify a sync call still isn't necessary.
+ mFrames[0].signalQueueFences();
+
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+
+ // Dequeue and queue frame 2.
+ const uint64_t fId2 = getNextFrameId();
+ dequeueAndQueue(1);
+
+ // Verify queue-related timestamps for f2 are available immediately in the
+ // producer without asking the consumer again, even before signaling the
+ // acquire fence.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outAcquireTime);
+
+ // Signal acquire fences. Verify a sync call still isn't necessary.
+ mFrames[1].signalQueueFences();
+
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+ &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+}
+
+TEST_F(GetFrameTimestampsTest, ZeroRequestedTimestampsNoSync) {
+ enableFrameTimestamps();
+
+ // Dequeue and queue frame 1.
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ const uint64_t fId2 = getNextFrameId();
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ mFrames[0].signalRefreshFences();
+ addFrameEvents(true, 0, 1);
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify a request for no timestamps doesn't result in a sync call.
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+// This test verifies that fences can signal and update timestamps producer
+// side without an additional sync call to the consumer.
+TEST_F(GetFrameTimestampsTest, FencesInProducerNoSync) {
+ enableFrameTimestamps();
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(true, NO_FRAME_INDEX, 0);
+ addFrameEvents(true, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+ // Verify available timestamps are correct for frame 1 again, before any
+ // fence has been signaled.
+ // This time a sync call should not be necessary.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+ // Signal the fences for frame 1.
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+
+ // Verify all timestamps are available without a sync call.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+ outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+}
+
+// This test verifies that if the frame wasn't GPU composited but has a refresh
+// event a sync call isn't made to get the GPU composite done time since it will
+// never exist.
+TEST_F(GetFrameTimestampsTest, NoGpuNoSync) {
+ enableFrameTimestamps();
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(false, NO_FRAME_INDEX, 0);
+ addFrameEvents(false, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+ // Signal the fences for frame 1.
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+
+ // Verify all timestamps, except GPU composition, are available without a
+ // sync call.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+}
+
+// This test verifies that if the certain timestamps can't possibly exist for
+// the most recent frame, then a sync call is not done.
+TEST_F(GetFrameTimestampsTest, NoReleaseNoSync) {
+ enableFrameTimestamps();
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+ mFrames[0].signalQueueFences();
+
+ // Dequeue and queue frame 2.
+ const uint64_t fId2 = getNextFrameId();
+ dequeueAndQueue(1);
+ mFrames[1].signalQueueFences();
+
+ addFrameEvents(false, NO_FRAME_INDEX, 0);
+ addFrameEvents(false, 0, 1);
+
+ // Verify available timestamps are correct for frame 1, before any
+ // fence has been signaled.
+ // Note: A sync call is necessary here since the events triggered by
+ // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = getAllFrameTimestamps(fId1);
+ EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime);
+ EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+
+ mFrames[0].signalRefreshFences();
+ mFrames[0].signalReleaseFences();
+ mFrames[1].signalRefreshFences();
+
+ // Verify querying for all timestmaps of f2 does not do a sync call. Even
+ // though the lastRefresh, dequeueReady, and release times aren't
+ // available, a sync call should not occur because it's not possible for f2
+ // to encounter the final value for those events until another frame is
+ // queued.
+ resetTimestamps();
+ oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ result = getAllFrameTimestamps(fId2);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(NO_ERROR, result);
+ EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+ EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+ EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime);
+ EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime);
+ EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime);
+}
+
+// This test verifies there are no sync calls for present times
+// when they aren't supported and that an error is returned.
+
+TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) {
+ enableFrameTimestamps();
+ mSurface->mFakeSurfaceComposer->setSupportsPresent(false);
+
+ // Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
+ dequeueAndQueue(0);
+
+ // Verify a query for the Present times do not trigger a sync call if they
+ // are not supported.
+ resetTimestamps();
+ int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+ int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ &outDisplayPresentTime, nullptr, nullptr);
+ EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+ EXPECT_EQ(BAD_VALUE, result);
+ EXPECT_EQ(-1, outDisplayPresentTime);
+}
+
+} // namespace android
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
new file mode 100644
index 0000000000..5ed3d3bebb
--- /dev/null
+++ b/libs/gui/view/Surface.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Surface"
+
+#include <gui/view/Surface.h>
+
+#include <binder/Parcel.h>
+
+#include <utils/Log.h>
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+namespace view {
+
+status_t Surface::writeToParcel(Parcel* parcel) const {
+ return writeToParcel(parcel, false);
+}
+
+status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
+ if (parcel == nullptr) return BAD_VALUE;
+
+ status_t res = OK;
+
+ if (!nameAlreadyWritten) {
+ res = parcel->writeString16(name);
+ if (res != OK) return res;
+
+ /* isSingleBuffered defaults to no */
+ res = parcel->writeInt32(0);
+ if (res != OK) return res;
+ }
+
+ res = parcel->writeStrongBinder(
+ IGraphicBufferProducer::asBinder(graphicBufferProducer));
+
+ return res;
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel) {
+ return readFromParcel(parcel, false);
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
+ if (parcel == nullptr) return BAD_VALUE;
+
+ status_t res = OK;
+ if (!nameAlreadyRead) {
+ name = readMaybeEmptyString16(parcel);
+ // Discard this for now
+ int isSingleBuffered;
+ res = parcel->readInt32(&isSingleBuffered);
+ if (res != OK) {
+ ALOGE("Can't read isSingleBuffered");
+ return res;
+ }
+ }
+
+ sp<IBinder> binder;
+
+ res = parcel->readNullableStrongBinder(&binder);
+ if (res != OK) {
+ ALOGE("%s: Can't read strong binder", __FUNCTION__);
+ return res;
+ }
+
+ graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
+
+ return OK;
+}
+
+String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
+ size_t len;
+ const char16_t* str = parcel->readString16Inplace(&len);
+ if (str != nullptr) {
+ return String16(str, len);
+ } else {
+ return String16();
+ }
+}
+
+} // namespace view
+} // namespace android
diff --git a/libs/hwc2on1adapter/Android.bp b/libs/hwc2on1adapter/Android.bp
new file mode 100644
index 0000000000..ec9cbf8429
--- /dev/null
+++ b/libs/hwc2on1adapter/Android.bp
@@ -0,0 +1,72 @@
+// Copyright 2010 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_library_shared {
+ name: "libhwc2on1adapter",
+ vendor: true,
+
+ clang: true,
+ cppflags: [
+ "-Weverything",
+ "-Wall",
+ "-Wunused",
+ "-Wunreachable-code",
+
+ // The static constructors and destructors in this library have not been noted to
+ // introduce significant overheads
+ "-Wno-exit-time-destructors",
+ "-Wno-global-constructors",
+
+ // We only care about compiling as C++14
+ "-Wno-c++98-compat-pedantic",
+
+ // android/sensors.h uses nested anonymous unions and anonymous structs
+ "-Wno-nested-anon-types",
+ "-Wno-gnu-anonymous-struct",
+
+ // Don't warn about struct padding
+ "-Wno-padded",
+
+ // hwcomposer2.h features switch covering all cases.
+ "-Wno-covered-switch-default",
+
+ // hwcomposer.h features zero size array.
+ "-Wno-zero-length-array",
+
+ // Disabling warning specific to hwc2on1adapter code
+ "-Wno-double-promotion",
+ "-Wno-sign-conversion",
+ "-Wno-switch-enum",
+ "-Wno-float-equal",
+ "-Wno-shorten-64-to-32",
+ "-Wno-sign-compare",
+ "-Wno-missing-prototypes",
+ ],
+
+ srcs: [
+ "HWC2On1Adapter.cpp",
+ "MiniFence.cpp",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "libcutils",
+ "liblog",
+ "libhardware",
+ ],
+
+ export_include_dirs: ["include"],
+
+ export_shared_lib_headers: ["libutils"],
+}
diff --git a/libs/hwc2on1adapter/CleanSpec.mk b/libs/hwc2on1adapter/CleanSpec.mk
new file mode 100644
index 0000000000..7fc22161fa
--- /dev/null
+++ b/libs/hwc2on1adapter/CleanSpec.mk
@@ -0,0 +1,52 @@
+# Copyright (C) 2017 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhwc2on1adapter_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwc2on1adapter.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libhwc2on1adapter.so)
diff --git a/libs/hwc2on1adapter/HWC2On1Adapter.cpp b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
new file mode 100644
index 0000000000..8c6ef691a2
--- /dev/null
+++ b/libs/hwc2on1adapter/HWC2On1Adapter.cpp
@@ -0,0 +1,2620 @@
+/*
+ * Copyright 2015 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 "hwc2on1adapter/HWC2On1Adapter.h"
+
+//#define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "HWC2On1Adapter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+
+#include <inttypes.h>
+
+#include <chrono>
+#include <cstdlib>
+#include <sstream>
+
+#include <hardware/hwcomposer.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+using namespace std::chrono_literals;
+
+static uint8_t getMinorVersion(struct hwc_composer_device_1* device)
+{
+ auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
+ return (version >> 16) & 0xF;
+}
+
+template <typename PFN, typename T>
+static hwc2_function_pointer_t asFP(T function)
+{
+ static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
+ return reinterpret_cast<hwc2_function_pointer_t>(function);
+}
+
+using namespace HWC2;
+
+static constexpr Attribute ColorMode = static_cast<Attribute>(6);
+
+namespace android {
+
+class HWC2On1Adapter::Callbacks : public hwc_procs_t {
+ public:
+ explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
+ invalidate = &invalidateHook;
+ vsync = &vsyncHook;
+ hotplug = &hotplugHook;
+ }
+
+ static void invalidateHook(const hwc_procs_t* procs) {
+ auto callbacks = static_cast<const Callbacks*>(procs);
+ callbacks->mAdapter.hwc1Invalidate();
+ }
+
+ static void vsyncHook(const hwc_procs_t* procs, int display,
+ int64_t timestamp) {
+ auto callbacks = static_cast<const Callbacks*>(procs);
+ callbacks->mAdapter.hwc1Vsync(display, timestamp);
+ }
+
+ static void hotplugHook(const hwc_procs_t* procs, int display,
+ int connected) {
+ auto callbacks = static_cast<const Callbacks*>(procs);
+ callbacks->mAdapter.hwc1Hotplug(display, connected);
+ }
+
+ private:
+ HWC2On1Adapter& mAdapter;
+};
+
+static int closeHook(hw_device_t* /*device*/)
+{
+ // Do nothing, since the real work is done in the class destructor, but we
+ // need to provide a valid function pointer for hwc2_close to call
+ return 0;
+}
+
+HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device)
+ : mDumpString(),
+ mHwc1Device(hwc1Device),
+ mHwc1MinorVersion(getMinorVersion(hwc1Device)),
+ mHwc1SupportsVirtualDisplays(false),
+ mHwc1SupportsBackgroundColor(false),
+ mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
+ mCapabilities(),
+ mLayers(),
+ mHwc1VirtualDisplay(),
+ mStateMutex(),
+ mCallbacks(),
+ mHasPendingInvalidate(false),
+ mPendingVsyncs(),
+ mPendingHotplugs(),
+ mDisplays(),
+ mHwc1DisplayMap()
+{
+ common.close = closeHook;
+ getCapabilities = getCapabilitiesHook;
+ getFunction = getFunctionHook;
+ populateCapabilities();
+ populatePrimary();
+ mHwc1Device->registerProcs(mHwc1Device,
+ static_cast<const hwc_procs_t*>(mHwc1Callbacks.get()));
+}
+
+HWC2On1Adapter::~HWC2On1Adapter() {
+ hwc_close_1(mHwc1Device);
+}
+
+void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount,
+ int32_t* outCapabilities) {
+ if (outCapabilities == nullptr) {
+ *outCount = mCapabilities.size();
+ return;
+ }
+
+ auto capabilityIter = mCapabilities.cbegin();
+ for (size_t written = 0; written < *outCount; ++written) {
+ if (capabilityIter == mCapabilities.cend()) {
+ return;
+ }
+ outCapabilities[written] = static_cast<int32_t>(*capabilityIter);
+ ++capabilityIter;
+ }
+}
+
+hwc2_function_pointer_t HWC2On1Adapter::doGetFunction(
+ FunctionDescriptor descriptor) {
+ switch (descriptor) {
+ // Device functions
+ case FunctionDescriptor::CreateVirtualDisplay:
+ return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(
+ createVirtualDisplayHook);
+ case FunctionDescriptor::DestroyVirtualDisplay:
+ return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(
+ destroyVirtualDisplayHook);
+ case FunctionDescriptor::Dump:
+ return asFP<HWC2_PFN_DUMP>(dumpHook);
+ case FunctionDescriptor::GetMaxVirtualDisplayCount:
+ return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(
+ getMaxVirtualDisplayCountHook);
+ case FunctionDescriptor::RegisterCallback:
+ return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook);
+
+ // Display functions
+ case FunctionDescriptor::AcceptDisplayChanges:
+ return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
+ displayHook<decltype(&Display::acceptChanges),
+ &Display::acceptChanges>);
+ case FunctionDescriptor::CreateLayer:
+ return asFP<HWC2_PFN_CREATE_LAYER>(
+ displayHook<decltype(&Display::createLayer),
+ &Display::createLayer, hwc2_layer_t*>);
+ case FunctionDescriptor::DestroyLayer:
+ return asFP<HWC2_PFN_DESTROY_LAYER>(
+ displayHook<decltype(&Display::destroyLayer),
+ &Display::destroyLayer, hwc2_layer_t>);
+ case FunctionDescriptor::GetActiveConfig:
+ return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>(
+ displayHook<decltype(&Display::getActiveConfig),
+ &Display::getActiveConfig, hwc2_config_t*>);
+ case FunctionDescriptor::GetChangedCompositionTypes:
+ return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
+ displayHook<decltype(&Display::getChangedCompositionTypes),
+ &Display::getChangedCompositionTypes, uint32_t*,
+ hwc2_layer_t*, int32_t*>);
+ case FunctionDescriptor::GetColorModes:
+ return asFP<HWC2_PFN_GET_COLOR_MODES>(
+ displayHook<decltype(&Display::getColorModes),
+ &Display::getColorModes, uint32_t*, int32_t*>);
+ case FunctionDescriptor::GetDisplayAttribute:
+ return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
+ getDisplayAttributeHook);
+ case FunctionDescriptor::GetDisplayConfigs:
+ return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>(
+ displayHook<decltype(&Display::getConfigs),
+ &Display::getConfigs, uint32_t*, hwc2_config_t*>);
+ case FunctionDescriptor::GetDisplayName:
+ return asFP<HWC2_PFN_GET_DISPLAY_NAME>(
+ displayHook<decltype(&Display::getName),
+ &Display::getName, uint32_t*, char*>);
+ case FunctionDescriptor::GetDisplayRequests:
+ return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>(
+ displayHook<decltype(&Display::getRequests),
+ &Display::getRequests, int32_t*, uint32_t*, hwc2_layer_t*,
+ int32_t*>);
+ case FunctionDescriptor::GetDisplayType:
+ return asFP<HWC2_PFN_GET_DISPLAY_TYPE>(
+ displayHook<decltype(&Display::getType),
+ &Display::getType, int32_t*>);
+ case FunctionDescriptor::GetDozeSupport:
+ return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(
+ displayHook<decltype(&Display::getDozeSupport),
+ &Display::getDozeSupport, int32_t*>);
+ case FunctionDescriptor::GetHdrCapabilities:
+ return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>(
+ displayHook<decltype(&Display::getHdrCapabilities),
+ &Display::getHdrCapabilities, uint32_t*, int32_t*, float*,
+ float*, float*>);
+ case FunctionDescriptor::GetReleaseFences:
+ return asFP<HWC2_PFN_GET_RELEASE_FENCES>(
+ displayHook<decltype(&Display::getReleaseFences),
+ &Display::getReleaseFences, uint32_t*, hwc2_layer_t*,
+ int32_t*>);
+ case FunctionDescriptor::PresentDisplay:
+ return asFP<HWC2_PFN_PRESENT_DISPLAY>(
+ displayHook<decltype(&Display::present),
+ &Display::present, int32_t*>);
+ case FunctionDescriptor::SetActiveConfig:
+ return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>(
+ displayHook<decltype(&Display::setActiveConfig),
+ &Display::setActiveConfig, hwc2_config_t>);
+ case FunctionDescriptor::SetClientTarget:
+ return asFP<HWC2_PFN_SET_CLIENT_TARGET>(
+ displayHook<decltype(&Display::setClientTarget),
+ &Display::setClientTarget, buffer_handle_t, int32_t,
+ int32_t, hwc_region_t>);
+ case FunctionDescriptor::SetColorMode:
+ return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook);
+ case FunctionDescriptor::SetColorTransform:
+ return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook);
+ case FunctionDescriptor::SetOutputBuffer:
+ return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(
+ displayHook<decltype(&Display::setOutputBuffer),
+ &Display::setOutputBuffer, buffer_handle_t, int32_t>);
+ case FunctionDescriptor::SetPowerMode:
+ return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook);
+ case FunctionDescriptor::SetVsyncEnabled:
+ return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook);
+ case FunctionDescriptor::ValidateDisplay:
+ return asFP<HWC2_PFN_VALIDATE_DISPLAY>(
+ displayHook<decltype(&Display::validate),
+ &Display::validate, uint32_t*, uint32_t*>);
+ case FunctionDescriptor::GetClientTargetSupport:
+ return asFP<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(
+ displayHook<decltype(&Display::getClientTargetSupport),
+ &Display::getClientTargetSupport, uint32_t, uint32_t,
+ int32_t, int32_t>);
+
+ // Layer functions
+ case FunctionDescriptor::SetCursorPosition:
+ return asFP<HWC2_PFN_SET_CURSOR_POSITION>(
+ layerHook<decltype(&Layer::setCursorPosition),
+ &Layer::setCursorPosition, int32_t, int32_t>);
+ case FunctionDescriptor::SetLayerBuffer:
+ return asFP<HWC2_PFN_SET_LAYER_BUFFER>(
+ layerHook<decltype(&Layer::setBuffer), &Layer::setBuffer,
+ buffer_handle_t, int32_t>);
+ case FunctionDescriptor::SetLayerSurfaceDamage:
+ return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
+ layerHook<decltype(&Layer::setSurfaceDamage),
+ &Layer::setSurfaceDamage, hwc_region_t>);
+
+ // Layer state functions
+ case FunctionDescriptor::SetLayerBlendMode:
+ return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>(
+ setLayerBlendModeHook);
+ case FunctionDescriptor::SetLayerColor:
+ return asFP<HWC2_PFN_SET_LAYER_COLOR>(
+ layerHook<decltype(&Layer::setColor), &Layer::setColor,
+ hwc_color_t>);
+ case FunctionDescriptor::SetLayerCompositionType:
+ return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
+ setLayerCompositionTypeHook);
+ case FunctionDescriptor::SetLayerDataspace:
+ return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerDataspaceHook);
+ case FunctionDescriptor::SetLayerDisplayFrame:
+ return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
+ layerHook<decltype(&Layer::setDisplayFrame),
+ &Layer::setDisplayFrame, hwc_rect_t>);
+ case FunctionDescriptor::SetLayerPlaneAlpha:
+ return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
+ layerHook<decltype(&Layer::setPlaneAlpha),
+ &Layer::setPlaneAlpha, float>);
+ case FunctionDescriptor::SetLayerSidebandStream:
+ return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(
+ layerHook<decltype(&Layer::setSidebandStream),
+ &Layer::setSidebandStream, const native_handle_t*>);
+ case FunctionDescriptor::SetLayerSourceCrop:
+ return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
+ layerHook<decltype(&Layer::setSourceCrop),
+ &Layer::setSourceCrop, hwc_frect_t>);
+ case FunctionDescriptor::SetLayerTransform:
+ return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerTransformHook);
+ case FunctionDescriptor::SetLayerVisibleRegion:
+ return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
+ layerHook<decltype(&Layer::setVisibleRegion),
+ &Layer::setVisibleRegion, hwc_region_t>);
+ case FunctionDescriptor::SetLayerZOrder:
+ return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerZOrderHook);
+
+ default:
+ ALOGE("doGetFunction: Unknown function descriptor: %d (%s)",
+ static_cast<int32_t>(descriptor),
+ to_string(descriptor).c_str());
+ return nullptr;
+ }
+}
+
+// Device functions
+
+Error HWC2On1Adapter::createVirtualDisplay(uint32_t width,
+ uint32_t height, hwc2_display_t* outDisplay) {
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ if (mHwc1VirtualDisplay) {
+ // We have already allocated our only HWC1 virtual display
+ ALOGE("createVirtualDisplay: HWC1 virtual display already allocated");
+ return Error::NoResources;
+ }
+
+ mHwc1VirtualDisplay = std::make_shared<HWC2On1Adapter::Display>(*this,
+ HWC2::DisplayType::Virtual);
+ mHwc1VirtualDisplay->populateConfigs(width, height);
+ const auto displayId = mHwc1VirtualDisplay->getId();
+ mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL] = displayId;
+ mHwc1VirtualDisplay->setHwc1Id(HWC_DISPLAY_VIRTUAL);
+ mDisplays.emplace(displayId, mHwc1VirtualDisplay);
+ *outDisplay = displayId;
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId) {
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) {
+ return Error::BadDisplay;
+ }
+
+ mHwc1VirtualDisplay.reset();
+ mHwc1DisplayMap.erase(HWC_DISPLAY_VIRTUAL);
+ mDisplays.erase(displayId);
+
+ return Error::None;
+}
+
+void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer) {
+ if (outBuffer != nullptr) {
+ auto copiedBytes = mDumpString.copy(outBuffer, *outSize);
+ *outSize = static_cast<uint32_t>(copiedBytes);
+ return;
+ }
+
+ std::stringstream output;
+
+ output << "-- HWC2On1Adapter --\n";
+
+ output << "Adapting to a HWC 1." << static_cast<int>(mHwc1MinorVersion) <<
+ " device\n";
+
+ // Attempt to acquire the lock for 1 second, but proceed without the lock
+ // after that, so we can still get some information if we're deadlocked
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex,
+ std::defer_lock);
+ lock.try_lock_for(1s);
+
+ if (mCapabilities.empty()) {
+ output << "Capabilities: None\n";
+ } else {
+ output << "Capabilities:\n";
+ for (auto capability : mCapabilities) {
+ output << " " << to_string(capability) << '\n';
+ }
+ }
+
+ output << "Displays:\n";
+ for (const auto& element : mDisplays) {
+ const auto& display = element.second;
+ output << display->dump();
+ }
+ output << '\n';
+
+ // Release the lock before calling into HWC1, and since we no longer require
+ // mutual exclusion to access mCapabilities or mDisplays
+ lock.unlock();
+
+ if (mHwc1Device->dump) {
+ output << "HWC1 dump:\n";
+ std::vector<char> hwc1Dump(4096);
+ // Call with size - 1 to preserve a null character at the end
+ mHwc1Device->dump(mHwc1Device, hwc1Dump.data(),
+ static_cast<int>(hwc1Dump.size() - 1));
+ output << hwc1Dump.data();
+ }
+
+ mDumpString = output.str();
+ *outSize = static_cast<uint32_t>(mDumpString.size());
+}
+
+uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount() {
+ return mHwc1SupportsVirtualDisplays ? 1 : 0;
+}
+
+static bool isValid(Callback descriptor) {
+ switch (descriptor) {
+ case Callback::Hotplug: // Fall-through
+ case Callback::Refresh: // Fall-through
+ case Callback::Vsync: return true;
+ default: return false;
+ }
+}
+
+Error HWC2On1Adapter::registerCallback(Callback descriptor,
+ hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) {
+ if (!isValid(descriptor)) {
+ return Error::BadParameter;
+ }
+
+ ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(),
+ callbackData, pointer);
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ mCallbacks[descriptor] = {callbackData, pointer};
+
+ bool hasPendingInvalidate = false;
+ std::vector<hwc2_display_t> displayIds;
+ std::vector<std::pair<hwc2_display_t, int64_t>> pendingVsyncs;
+ std::vector<std::pair<hwc2_display_t, int>> pendingHotplugs;
+
+ if (descriptor == Callback::Refresh) {
+ hasPendingInvalidate = mHasPendingInvalidate;
+ if (hasPendingInvalidate) {
+ for (auto& displayPair : mDisplays) {
+ displayIds.emplace_back(displayPair.first);
+ }
+ }
+ mHasPendingInvalidate = false;
+ } else if (descriptor == Callback::Vsync) {
+ for (auto pending : mPendingVsyncs) {
+ auto hwc1DisplayId = pending.first;
+ if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+ ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d",
+ hwc1DisplayId);
+ continue;
+ }
+ auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+ auto timestamp = pending.second;
+ pendingVsyncs.emplace_back(displayId, timestamp);
+ }
+ mPendingVsyncs.clear();
+ } else if (descriptor == Callback::Hotplug) {
+ // Hotplug the primary display
+ pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY],
+ static_cast<int32_t>(Connection::Connected));
+
+ for (auto pending : mPendingHotplugs) {
+ auto hwc1DisplayId = pending.first;
+ if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+ ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d",
+ hwc1DisplayId);
+ continue;
+ }
+ auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+ auto connected = pending.second;
+ pendingHotplugs.emplace_back(displayId, connected);
+ }
+ }
+
+ // Call pending callbacks without the state lock held
+ lock.unlock();
+
+ if (hasPendingInvalidate) {
+ auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(pointer);
+ for (auto displayId : displayIds) {
+ refresh(callbackData, displayId);
+ }
+ }
+ if (!pendingVsyncs.empty()) {
+ auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
+ for (auto& pendingVsync : pendingVsyncs) {
+ vsync(callbackData, pendingVsync.first, pendingVsync.second);
+ }
+ }
+ if (!pendingHotplugs.empty()) {
+ auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer);
+ for (auto& pendingHotplug : pendingHotplugs) {
+ hotplug(callbackData, pendingHotplug.first, pendingHotplug.second);
+ }
+ }
+ return Error::None;
+}
+
+// Display functions
+
+std::atomic<hwc2_display_t> HWC2On1Adapter::Display::sNextId(1);
+
+HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type)
+ : mId(sNextId++),
+ mDevice(device),
+ mStateMutex(),
+ mHwc1RequestedContents(nullptr),
+ mRetireFence(),
+ mChanges(),
+ mHwc1Id(-1),
+ mConfigs(),
+ mActiveConfig(nullptr),
+ mActiveColorMode(static_cast<android_color_mode_t>(-1)),
+ mName(),
+ mType(type),
+ mPowerMode(PowerMode::Off),
+ mVsyncEnabled(Vsync::Invalid),
+ mClientTarget(),
+ mOutputBuffer(),
+ mHasColorTransform(false),
+ mLayers(),
+ mHwc1LayerMap(),
+ mNumAvailableRects(0),
+ mNextAvailableRect(nullptr),
+ mGeometryChanged(false)
+ {}
+
+Error HWC2On1Adapter::Display::acceptChanges() {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges) {
+ ALOGV("[%" PRIu64 "] acceptChanges failed, not validated", mId);
+ return Error::NotValidated;
+ }
+
+ ALOGV("[%" PRIu64 "] acceptChanges", mId);
+
+ for (auto& change : mChanges->getTypeChanges()) {
+ auto layerId = change.first;
+ auto type = change.second;
+ if (mDevice.mLayers.count(layerId) == 0) {
+ // This should never happen but somehow does.
+ ALOGW("Cannot accept change for unknown layer (%" PRIu64 ")",
+ layerId);
+ continue;
+ }
+ auto layer = mDevice.mLayers[layerId];
+ layer->setCompositionType(type);
+ }
+
+ mChanges->clearTypeChanges();
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto layer = *mLayers.emplace(std::make_shared<Layer>(*this));
+ mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer));
+ *outLayerId = layer->getId();
+ ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId);
+ markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ const auto mapLayer = mDevice.mLayers.find(layerId);
+ if (mapLayer == mDevice.mLayers.end()) {
+ ALOGV("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer",
+ mId, layerId);
+ return Error::BadLayer;
+ }
+ const auto layer = mapLayer->second;
+ mDevice.mLayers.erase(mapLayer);
+ const auto zRange = mLayers.equal_range(layer);
+ for (auto current = zRange.first; current != zRange.second; ++current) {
+ if (**current == *layer) {
+ current = mLayers.erase(current);
+ break;
+ }
+ }
+ ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId);
+ markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mActiveConfig) {
+ ALOGV("[%" PRIu64 "] getActiveConfig --> %s", mId,
+ to_string(Error::BadConfig).c_str());
+ return Error::BadConfig;
+ }
+ auto configId = mActiveConfig->getId();
+ ALOGV("[%" PRIu64 "] getActiveConfig --> %u", mId, configId);
+ *outConfig = configId;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId,
+ Attribute attribute, int32_t* outValue) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
+ ALOGV("[%" PRIu64 "] getAttribute failed: bad config (%u)", mId,
+ configId);
+ return Error::BadConfig;
+ }
+ *outValue = mConfigs[configId]->getAttribute(attribute);
+ ALOGV("[%" PRIu64 "] getAttribute(%u, %s) --> %d", mId, configId,
+ to_string(attribute).c_str(), *outValue);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getChangedCompositionTypes(
+ uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges) {
+ ALOGE("[%" PRIu64 "] getChangedCompositionTypes failed: not validated",
+ mId);
+ return Error::NotValidated;
+ }
+
+ if ((outLayers == nullptr) || (outTypes == nullptr)) {
+ *outNumElements = mChanges->getTypeChanges().size();
+ return Error::None;
+ }
+
+ uint32_t numWritten = 0;
+ for (const auto& element : mChanges->getTypeChanges()) {
+ if (numWritten == *outNumElements) {
+ break;
+ }
+ auto layerId = element.first;
+ auto intType = static_cast<int32_t>(element.second);
+ ALOGV("Adding %" PRIu64 " %s", layerId,
+ to_string(element.second).c_str());
+ outLayers[numWritten] = layerId;
+ outTypes[numWritten] = intType;
+ ++numWritten;
+ }
+ *outNumElements = numWritten;
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getColorModes(uint32_t* outNumModes,
+ int32_t* outModes) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!outModes) {
+ *outNumModes = mColorModes.size();
+ return Error::None;
+ }
+ uint32_t numModes = std::min(*outNumModes,
+ static_cast<uint32_t>(mColorModes.size()));
+ std::copy_n(mColorModes.cbegin(), numModes, outModes);
+ *outNumModes = numModes;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs,
+ hwc2_config_t* outConfigs) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!outConfigs) {
+ *outNumConfigs = mConfigs.size();
+ return Error::None;
+ }
+ uint32_t numWritten = 0;
+ for (const auto& config : mConfigs) {
+ if (numWritten == *outNumConfigs) {
+ break;
+ }
+ outConfigs[numWritten] = config->getId();
+ ++numWritten;
+ }
+ *outNumConfigs = numWritten;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) {
+ *outSupport = 0;
+ } else {
+ *outSupport = 1;
+ }
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getHdrCapabilities(uint32_t* outNumTypes,
+ int32_t* /*outTypes*/, float* /*outMaxLuminance*/,
+ float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
+ // This isn't supported on HWC1, so per the HWC2 header, return numTypes = 0
+ *outNumTypes = 0;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!outName) {
+ *outSize = mName.size();
+ return Error::None;
+ }
+ auto numCopied = mName.copy(outName, *outSize);
+ *outSize = numCopied;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements,
+ hwc2_layer_t* outLayers, int32_t* outFences) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ uint32_t numWritten = 0;
+ bool outputsNonNull = (outLayers != nullptr) && (outFences != nullptr);
+ for (const auto& layer : mLayers) {
+ if (outputsNonNull && (numWritten == *outNumElements)) {
+ break;
+ }
+
+ auto releaseFence = layer->getReleaseFence();
+ if (releaseFence != MiniFence::NO_FENCE) {
+ if (outputsNonNull) {
+ outLayers[numWritten] = layer->getId();
+ outFences[numWritten] = releaseFence->dup();
+ }
+ ++numWritten;
+ }
+ }
+ *outNumElements = numWritten;
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests,
+ uint32_t* outNumElements, hwc2_layer_t* outLayers,
+ int32_t* outLayerRequests) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges) {
+ return Error::NotValidated;
+ }
+
+ if (outLayers == nullptr || outLayerRequests == nullptr) {
+ *outNumElements = mChanges->getNumLayerRequests();
+ return Error::None;
+ }
+
+ // Display requests (HWC2::DisplayRequest) are not supported by hwc1:
+ // A hwc1 has always zero requests for the client.
+ *outDisplayRequests = 0;
+
+ uint32_t numWritten = 0;
+ for (const auto& request : mChanges->getLayerRequests()) {
+ if (numWritten == *outNumElements) {
+ break;
+ }
+ outLayers[numWritten] = request.first;
+ outLayerRequests[numWritten] = static_cast<int32_t>(request.second);
+ ++numWritten;
+ }
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getType(int32_t* outType) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ *outType = static_cast<int32_t>(mType);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::present(int32_t* outRetireFence) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (mChanges) {
+ Error error = mDevice.setAllDisplays();
+ if (error != Error::None) {
+ ALOGE("[%" PRIu64 "] present: setAllDisplaysFailed (%s)", mId,
+ to_string(error).c_str());
+ return error;
+ }
+ }
+
+ *outRetireFence = mRetireFence.get()->dup();
+ ALOGV("[%" PRIu64 "] present returning retire fence %d", mId,
+ *outRetireFence);
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto config = getConfig(configId);
+ if (!config) {
+ return Error::BadConfig;
+ }
+ if (config == mActiveConfig) {
+ return Error::None;
+ }
+
+ if (mDevice.mHwc1MinorVersion >= 4) {
+ uint32_t hwc1Id = 0;
+ auto error = config->getHwc1IdForColorMode(mActiveColorMode, &hwc1Id);
+ if (error != Error::None) {
+ return error;
+ }
+
+ int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+ mHwc1Id, static_cast<int>(hwc1Id));
+ if (intError != 0) {
+ ALOGE("setActiveConfig: Failed to set active config on HWC1 (%d)",
+ intError);
+ return Error::BadConfig;
+ }
+ mActiveConfig = config;
+ }
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target,
+ int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence);
+ mClientTarget.setBuffer(target);
+ mClientTarget.setFence(acquireFence);
+ // dataspace and damage can't be used by HWC1, so ignore them
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode) {
+ std::unique_lock<std::recursive_mutex> lock (mStateMutex);
+
+ ALOGV("[%" PRIu64 "] setColorMode(%d)", mId, mode);
+
+ if (mode == mActiveColorMode) {
+ return Error::None;
+ }
+ if (mColorModes.count(mode) == 0) {
+ ALOGE("[%" PRIu64 "] Mode %d not found in mColorModes", mId, mode);
+ return Error::Unsupported;
+ }
+
+ uint32_t hwc1Config = 0;
+ auto error = mActiveConfig->getHwc1IdForColorMode(mode, &hwc1Config);
+ if (error != Error::None) {
+ return error;
+ }
+
+ ALOGV("[%" PRIu64 "] Setting HWC1 config %u", mId, hwc1Config);
+ int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+ mHwc1Id, hwc1Config);
+ if (intError != 0) {
+ ALOGE("[%" PRIu64 "] Failed to set HWC1 config (%d)", mId, intError);
+ return Error::Unsupported;
+ }
+
+ mActiveColorMode = mode;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ ALOGV("%" PRIu64 "] setColorTransform(%d)", mId,
+ static_cast<int32_t>(hint));
+ mHasColorTransform = (hint != HAL_COLOR_TRANSFORM_IDENTITY);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer,
+ int32_t releaseFence) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence);
+ mOutputBuffer.setBuffer(buffer);
+ mOutputBuffer.setFence(releaseFence);
+ return Error::None;
+}
+
+static bool isValid(PowerMode mode) {
+ switch (mode) {
+ case PowerMode::Off: // Fall-through
+ case PowerMode::DozeSuspend: // Fall-through
+ case PowerMode::Doze: // Fall-through
+ case PowerMode::On: return true;
+ }
+}
+
+static int getHwc1PowerMode(PowerMode mode) {
+ switch (mode) {
+ case PowerMode::Off: return HWC_POWER_MODE_OFF;
+ case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND;
+ case PowerMode::Doze: return HWC_POWER_MODE_DOZE;
+ case PowerMode::On: return HWC_POWER_MODE_NORMAL;
+ }
+}
+
+Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode) {
+ if (!isValid(mode)) {
+ return Error::BadParameter;
+ }
+ if (mode == mPowerMode) {
+ return Error::None;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ int error = 0;
+ if (mDevice.mHwc1MinorVersion < 4) {
+ error = mDevice.mHwc1Device->blank(mDevice.mHwc1Device, mHwc1Id,
+ mode == PowerMode::Off);
+ } else {
+ error = mDevice.mHwc1Device->setPowerMode(mDevice.mHwc1Device,
+ mHwc1Id, getHwc1PowerMode(mode));
+ }
+ ALOGE_IF(error != 0, "setPowerMode: Failed to set power mode on HWC1 (%d)",
+ error);
+
+ ALOGV("[%" PRIu64 "] setPowerMode(%s)", mId, to_string(mode).c_str());
+ mPowerMode = mode;
+ return Error::None;
+}
+
+static bool isValid(Vsync enable) {
+ switch (enable) {
+ case Vsync::Enable: // Fall-through
+ case Vsync::Disable: return true;
+ case Vsync::Invalid: return false;
+ }
+}
+
+Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable) {
+ if (!isValid(enable)) {
+ return Error::BadParameter;
+ }
+ if (enable == mVsyncEnabled) {
+ return Error::None;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ int error = mDevice.mHwc1Device->eventControl(mDevice.mHwc1Device,
+ mHwc1Id, HWC_EVENT_VSYNC, enable == Vsync::Enable);
+ ALOGE_IF(error != 0, "setVsyncEnabled: Failed to set vsync on HWC1 (%d)",
+ error);
+
+ mVsyncEnabled = enable;
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges) {
+ if (!mDevice.prepareAllDisplays()) {
+ return Error::BadDisplay;
+ }
+ } else {
+ ALOGE("Validate was called more than once!");
+ }
+
+ *outNumTypes = mChanges->getNumTypes();
+ *outNumRequests = mChanges->getNumLayerRequests();
+ ALOGV("[%" PRIu64 "] validate --> %u types, %u requests", mId, *outNumTypes,
+ *outNumRequests);
+ for (auto request : mChanges->getTypeChanges()) {
+ ALOGV("Layer %" PRIu64 " --> %s", request.first,
+ to_string(request.second).c_str());
+ }
+ return *outNumTypes > 0 ? Error::HasChanges : Error::None;
+}
+
+Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ const auto mapLayer = mDevice.mLayers.find(layerId);
+ if (mapLayer == mDevice.mLayers.end()) {
+ ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", mId);
+ return Error::BadLayer;
+ }
+
+ const auto layer = mapLayer->second;
+ const auto zRange = mLayers.equal_range(layer);
+ bool layerOnDisplay = false;
+ for (auto current = zRange.first; current != zRange.second; ++current) {
+ if (**current == *layer) {
+ if ((*current)->getZ() == z) {
+ // Don't change anything if the Z hasn't changed
+ return Error::None;
+ }
+ current = mLayers.erase(current);
+ layerOnDisplay = true;
+ break;
+ }
+ }
+
+ if (!layerOnDisplay) {
+ ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display",
+ mId);
+ return Error::BadLayer;
+ }
+
+ layer->setZ(z);
+ mLayers.emplace(std::move(layer));
+ markGeometryChanged();
+
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getClientTargetSupport(uint32_t width, uint32_t height,
+ int32_t format, int32_t dataspace){
+ if (mActiveConfig == nullptr) {
+ return Error::Unsupported;
+ }
+
+ if (width == mActiveConfig->getAttribute(Attribute::Width) &&
+ height == mActiveConfig->getAttribute(Attribute::Height) &&
+ format == HAL_PIXEL_FORMAT_RGBA_8888 &&
+ dataspace == HAL_DATASPACE_UNKNOWN) {
+ return Error::None;
+ }
+
+ return Error::Unsupported;
+}
+
+static constexpr uint32_t ATTRIBUTES_WITH_COLOR[] = {
+ HWC_DISPLAY_VSYNC_PERIOD,
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_DPI_Y,
+ HWC_DISPLAY_COLOR_TRANSFORM,
+ HWC_DISPLAY_NO_ATTRIBUTE,
+};
+
+static constexpr uint32_t ATTRIBUTES_WITHOUT_COLOR[] = {
+ HWC_DISPLAY_VSYNC_PERIOD,
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_DPI_Y,
+ HWC_DISPLAY_NO_ATTRIBUTE,
+};
+
+static constexpr size_t NUM_ATTRIBUTES_WITH_COLOR =
+ sizeof(ATTRIBUTES_WITH_COLOR) / sizeof(uint32_t);
+static_assert(sizeof(ATTRIBUTES_WITH_COLOR) > sizeof(ATTRIBUTES_WITHOUT_COLOR),
+ "Attribute tables have unexpected sizes");
+
+static constexpr uint32_t ATTRIBUTE_MAP_WITH_COLOR[] = {
+ 6, // HWC_DISPLAY_NO_ATTRIBUTE = 0
+ 0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
+ 1, // HWC_DISPLAY_WIDTH = 2,
+ 2, // HWC_DISPLAY_HEIGHT = 3,
+ 3, // HWC_DISPLAY_DPI_X = 4,
+ 4, // HWC_DISPLAY_DPI_Y = 5,
+ 5, // HWC_DISPLAY_COLOR_TRANSFORM = 6,
+};
+
+static constexpr uint32_t ATTRIBUTE_MAP_WITHOUT_COLOR[] = {
+ 5, // HWC_DISPLAY_NO_ATTRIBUTE = 0
+ 0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
+ 1, // HWC_DISPLAY_WIDTH = 2,
+ 2, // HWC_DISPLAY_HEIGHT = 3,
+ 3, // HWC_DISPLAY_DPI_X = 4,
+ 4, // HWC_DISPLAY_DPI_Y = 5,
+};
+
+template <uint32_t attribute>
+static constexpr bool attributesMatch()
+{
+ bool match = (attribute ==
+ ATTRIBUTES_WITH_COLOR[ATTRIBUTE_MAP_WITH_COLOR[attribute]]);
+ if (attribute == HWC_DISPLAY_COLOR_TRANSFORM) {
+ return match;
+ }
+
+ return match && (attribute ==
+ ATTRIBUTES_WITHOUT_COLOR[ATTRIBUTE_MAP_WITHOUT_COLOR[attribute]]);
+}
+static_assert(attributesMatch<HWC_DISPLAY_VSYNC_PERIOD>(),
+ "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_WIDTH>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_HEIGHT>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_DPI_X>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_DPI_Y>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_COLOR_TRANSFORM>(),
+ "Tables out of sync");
+
+void HWC2On1Adapter::Display::populateConfigs() {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ ALOGV("[%" PRIu64 "] populateConfigs", mId);
+
+ if (mHwc1Id == -1) {
+ ALOGE("populateConfigs: HWC1 ID not set");
+ return;
+ }
+
+ const size_t MAX_NUM_CONFIGS = 128;
+ uint32_t configs[MAX_NUM_CONFIGS] = {};
+ size_t numConfigs = MAX_NUM_CONFIGS;
+ mDevice.mHwc1Device->getDisplayConfigs(mDevice.mHwc1Device, mHwc1Id,
+ configs, &numConfigs);
+
+ for (size_t c = 0; c < numConfigs; ++c) {
+ uint32_t hwc1ConfigId = configs[c];
+ auto newConfig = std::make_shared<Config>(*this);
+
+ int32_t values[NUM_ATTRIBUTES_WITH_COLOR] = {};
+ bool hasColor = true;
+ auto result = mDevice.mHwc1Device->getDisplayAttributes(
+ mDevice.mHwc1Device, mHwc1Id, hwc1ConfigId,
+ ATTRIBUTES_WITH_COLOR, values);
+ if (result != 0) {
+ mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device,
+ mHwc1Id, hwc1ConfigId, ATTRIBUTES_WITHOUT_COLOR, values);
+ hasColor = false;
+ }
+
+ auto attributeMap = hasColor ?
+ ATTRIBUTE_MAP_WITH_COLOR : ATTRIBUTE_MAP_WITHOUT_COLOR;
+
+ newConfig->setAttribute(Attribute::VsyncPeriod,
+ values[attributeMap[HWC_DISPLAY_VSYNC_PERIOD]]);
+ newConfig->setAttribute(Attribute::Width,
+ values[attributeMap[HWC_DISPLAY_WIDTH]]);
+ newConfig->setAttribute(Attribute::Height,
+ values[attributeMap[HWC_DISPLAY_HEIGHT]]);
+ newConfig->setAttribute(Attribute::DpiX,
+ values[attributeMap[HWC_DISPLAY_DPI_X]]);
+ newConfig->setAttribute(Attribute::DpiY,
+ values[attributeMap[HWC_DISPLAY_DPI_Y]]);
+ if (hasColor) {
+ // In HWC1, color modes are referred to as color transforms. To avoid confusion with
+ // the HWC2 concept of color transforms, we internally refer to them as color modes for
+ // both HWC1 and 2.
+ newConfig->setAttribute(ColorMode,
+ values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]);
+ }
+
+ // We can only do this after attempting to read the color mode
+ newConfig->setHwc1Id(hwc1ConfigId);
+
+ for (auto& existingConfig : mConfigs) {
+ if (existingConfig->merge(*newConfig)) {
+ ALOGV("Merged config %d with existing config %u: %s",
+ hwc1ConfigId, existingConfig->getId(),
+ existingConfig->toString().c_str());
+ newConfig.reset();
+ break;
+ }
+ }
+
+ // If it wasn't merged with any existing config, add it to the end
+ if (newConfig) {
+ newConfig->setId(static_cast<hwc2_config_t>(mConfigs.size()));
+ ALOGV("Found new config %u: %s", newConfig->getId(),
+ newConfig->toString().c_str());
+ mConfigs.emplace_back(std::move(newConfig));
+ }
+ }
+
+ initializeActiveConfig();
+ populateColorModes();
+}
+
+void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ mConfigs.emplace_back(std::make_shared<Config>(*this));
+ auto& config = mConfigs[0];
+
+ config->setAttribute(Attribute::Width, static_cast<int32_t>(width));
+ config->setAttribute(Attribute::Height, static_cast<int32_t>(height));
+ config->setHwc1Id(0);
+ config->setId(0);
+ mActiveConfig = config;
+}
+
+bool HWC2On1Adapter::Display::prepare() {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ // Only prepare display contents for displays HWC1 knows about
+ if (mHwc1Id == -1) {
+ return true;
+ }
+
+ // It doesn't make sense to prepare a display for which there is no active
+ // config, so return early
+ if (!mActiveConfig) {
+ ALOGE("[%" PRIu64 "] Attempted to prepare, but no config active", mId);
+ return false;
+ }
+
+ allocateRequestedContents();
+ assignHwc1LayerIds();
+
+ mHwc1RequestedContents->retireFenceFd = -1;
+ mHwc1RequestedContents->flags = 0;
+ if (mGeometryChanged) {
+ mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED;
+ }
+ mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer();
+ mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence();
+
+ // +1 is for framebuffer target layer.
+ mHwc1RequestedContents->numHwLayers = mLayers.size() + 1;
+ for (auto& layer : mLayers) {
+ auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
+ hwc1Layer.releaseFenceFd = -1;
+ hwc1Layer.acquireFenceFd = -1;
+ ALOGV("Applying states for layer %" PRIu64 " ", layer->getId());
+ layer->applyState(hwc1Layer);
+ }
+
+ prepareFramebufferTarget();
+
+ resetGeometryMarker();
+
+ return true;
+}
+
+void HWC2On1Adapter::Display::generateChanges() {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ mChanges.reset(new Changes);
+
+ size_t numLayers = mHwc1RequestedContents->numHwLayers;
+ for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
+ const auto& receivedLayer = mHwc1RequestedContents->hwLayers[hwc1Id];
+ if (mHwc1LayerMap.count(hwc1Id) == 0) {
+ ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET,
+ "generateChanges: HWC1 layer %zd doesn't have a"
+ " matching HWC2 layer, and isn't the framebuffer target",
+ hwc1Id);
+ continue;
+ }
+
+ Layer& layer = *mHwc1LayerMap[hwc1Id];
+ updateTypeChanges(receivedLayer, layer);
+ updateLayerRequests(receivedLayer, layer);
+ }
+}
+
+bool HWC2On1Adapter::Display::hasChanges() const {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+ return mChanges != nullptr;
+}
+
+Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mChanges || (mChanges->getNumTypes() > 0)) {
+ ALOGE("[%" PRIu64 "] set failed: not validated", mId);
+ return Error::NotValidated;
+ }
+
+ // Set up the client/framebuffer target
+ auto numLayers = hwcContents.numHwLayers;
+
+ // Close acquire fences on FRAMEBUFFER layers, since they will not be used
+ // by HWC
+ for (size_t l = 0; l < numLayers - 1; ++l) {
+ auto& layer = hwcContents.hwLayers[l];
+ if (layer.compositionType == HWC_FRAMEBUFFER) {
+ ALOGV("Closing fence %d for layer %zd", layer.acquireFenceFd, l);
+ close(layer.acquireFenceFd);
+ layer.acquireFenceFd = -1;
+ }
+ }
+
+ auto& clientTargetLayer = hwcContents.hwLayers[numLayers - 1];
+ if (clientTargetLayer.compositionType == HWC_FRAMEBUFFER_TARGET) {
+ clientTargetLayer.handle = mClientTarget.getBuffer();
+ clientTargetLayer.acquireFenceFd = mClientTarget.getFence();
+ } else {
+ ALOGE("[%" PRIu64 "] set: last HWC layer wasn't FRAMEBUFFER_TARGET",
+ mId);
+ }
+
+ mChanges.reset();
+
+ return Error::None;
+}
+
+void HWC2On1Adapter::Display::addRetireFence(int fenceFd) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+ mRetireFence.add(fenceFd);
+}
+
+void HWC2On1Adapter::Display::addReleaseFences(
+ const hwc_display_contents_1_t& hwcContents) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ size_t numLayers = hwcContents.numHwLayers;
+ for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
+ const auto& receivedLayer = hwcContents.hwLayers[hwc1Id];
+ if (mHwc1LayerMap.count(hwc1Id) == 0) {
+ if (receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET) {
+ ALOGE("addReleaseFences: HWC1 layer %zd doesn't have a"
+ " matching HWC2 layer, and isn't the framebuffer"
+ " target", hwc1Id);
+ }
+ // Close the framebuffer target release fence since we will use the
+ // display retire fence instead
+ if (receivedLayer.releaseFenceFd != -1) {
+ close(receivedLayer.releaseFenceFd);
+ }
+ continue;
+ }
+
+ Layer& layer = *mHwc1LayerMap[hwc1Id];
+ ALOGV("Adding release fence %d to layer %" PRIu64,
+ receivedLayer.releaseFenceFd, layer.getId());
+ layer.addReleaseFence(receivedLayer.releaseFenceFd);
+ }
+}
+
+bool HWC2On1Adapter::Display::hasColorTransform() const {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+ return mHasColorTransform;
+}
+
+static std::string hwc1CompositionString(int32_t type) {
+ switch (type) {
+ case HWC_FRAMEBUFFER: return "Framebuffer";
+ case HWC_OVERLAY: return "Overlay";
+ case HWC_BACKGROUND: return "Background";
+ case HWC_FRAMEBUFFER_TARGET: return "FramebufferTarget";
+ case HWC_SIDEBAND: return "Sideband";
+ case HWC_CURSOR_OVERLAY: return "CursorOverlay";
+ default:
+ return std::string("Unknown (") + std::to_string(type) + ")";
+ }
+}
+
+static std::string hwc1TransformString(int32_t transform) {
+ switch (transform) {
+ case 0: return "None";
+ case HWC_TRANSFORM_FLIP_H: return "FlipH";
+ case HWC_TRANSFORM_FLIP_V: return "FlipV";
+ case HWC_TRANSFORM_ROT_90: return "Rotate90";
+ case HWC_TRANSFORM_ROT_180: return "Rotate180";
+ case HWC_TRANSFORM_ROT_270: return "Rotate270";
+ case HWC_TRANSFORM_FLIP_H_ROT_90: return "FlipHRotate90";
+ case HWC_TRANSFORM_FLIP_V_ROT_90: return "FlipVRotate90";
+ default:
+ return std::string("Unknown (") + std::to_string(transform) + ")";
+ }
+}
+
+static std::string hwc1BlendModeString(int32_t mode) {
+ switch (mode) {
+ case HWC_BLENDING_NONE: return "None";
+ case HWC_BLENDING_PREMULT: return "Premultiplied";
+ case HWC_BLENDING_COVERAGE: return "Coverage";
+ default:
+ return std::string("Unknown (") + std::to_string(mode) + ")";
+ }
+}
+
+static std::string rectString(hwc_rect_t rect) {
+ std::stringstream output;
+ output << "[" << rect.left << ", " << rect.top << ", ";
+ output << rect.right << ", " << rect.bottom << "]";
+ return output.str();
+}
+
+static std::string approximateFloatString(float f) {
+ if (static_cast<int32_t>(f) == f) {
+ return std::to_string(static_cast<int32_t>(f));
+ }
+ int32_t truncated = static_cast<int32_t>(f * 10);
+ bool approximate = (static_cast<float>(truncated) != f * 10);
+ const size_t BUFFER_SIZE = 32;
+ char buffer[BUFFER_SIZE] = {};
+ auto bytesWritten = snprintf(buffer, BUFFER_SIZE,
+ "%s%.1f", approximate ? "~" : "", f);
+ return std::string(buffer, bytesWritten);
+}
+
+static std::string frectString(hwc_frect_t frect) {
+ std::stringstream output;
+ output << "[" << approximateFloatString(frect.left) << ", ";
+ output << approximateFloatString(frect.top) << ", ";
+ output << approximateFloatString(frect.right) << ", ";
+ output << approximateFloatString(frect.bottom) << "]";
+ return output.str();
+}
+
+static std::string colorString(hwc_color_t color) {
+ std::stringstream output;
+ output << "RGBA [";
+ output << static_cast<int32_t>(color.r) << ", ";
+ output << static_cast<int32_t>(color.g) << ", ";
+ output << static_cast<int32_t>(color.b) << ", ";
+ output << static_cast<int32_t>(color.a) << "]";
+ return output.str();
+}
+
+static std::string alphaString(float f) {
+ const size_t BUFFER_SIZE = 8;
+ char buffer[BUFFER_SIZE] = {};
+ auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f);
+ return std::string(buffer, bytesWritten);
+}
+
+static std::string to_string(const hwc_layer_1_t& hwcLayer,
+ int32_t hwc1MinorVersion) {
+ const char* fill = " ";
+
+ std::stringstream output;
+
+ output << " Composition: " <<
+ hwc1CompositionString(hwcLayer.compositionType);
+
+ if (hwcLayer.compositionType == HWC_BACKGROUND) {
+ output << " Color: " << colorString(hwcLayer.backgroundColor) << '\n';
+ } else if (hwcLayer.compositionType == HWC_SIDEBAND) {
+ output << " Stream: " << hwcLayer.sidebandStream << '\n';
+ } else {
+ output << " Buffer: " << hwcLayer.handle << "/" <<
+ hwcLayer.acquireFenceFd << '\n';
+ }
+
+ output << fill << "Display frame: " << rectString(hwcLayer.displayFrame) <<
+ '\n';
+
+ output << fill << "Source crop: ";
+ if (hwc1MinorVersion >= 3) {
+ output << frectString(hwcLayer.sourceCropf) << '\n';
+ } else {
+ output << rectString(hwcLayer.sourceCropi) << '\n';
+ }
+
+ output << fill << "Transform: " << hwc1TransformString(hwcLayer.transform);
+ output << " Blend mode: " << hwc1BlendModeString(hwcLayer.blending);
+ if (hwcLayer.planeAlpha != 0xFF) {
+ output << " Alpha: " << alphaString(hwcLayer.planeAlpha / 255.0f);
+ }
+ output << '\n';
+
+ if (hwcLayer.hints != 0) {
+ output << fill << "Hints:";
+ if ((hwcLayer.hints & HWC_HINT_TRIPLE_BUFFER) != 0) {
+ output << " TripleBuffer";
+ }
+ if ((hwcLayer.hints & HWC_HINT_CLEAR_FB) != 0) {
+ output << " ClearFB";
+ }
+ output << '\n';
+ }
+
+ if (hwcLayer.flags != 0) {
+ output << fill << "Flags:";
+ if ((hwcLayer.flags & HWC_SKIP_LAYER) != 0) {
+ output << " SkipLayer";
+ }
+ if ((hwcLayer.flags & HWC_IS_CURSOR_LAYER) != 0) {
+ output << " IsCursorLayer";
+ }
+ output << '\n';
+ }
+
+ return output.str();
+}
+
+static std::string to_string(const hwc_display_contents_1_t& hwcContents,
+ int32_t hwc1MinorVersion) {
+ const char* fill = " ";
+
+ std::stringstream output;
+ output << fill << "Geometry changed: " <<
+ ((hwcContents.flags & HWC_GEOMETRY_CHANGED) != 0 ? "Y\n" : "N\n");
+
+ output << fill << hwcContents.numHwLayers << " Layer" <<
+ ((hwcContents.numHwLayers == 1) ? "\n" : "s\n");
+ for (size_t layer = 0; layer < hwcContents.numHwLayers; ++layer) {
+ output << fill << " Layer " << layer;
+ output << to_string(hwcContents.hwLayers[layer], hwc1MinorVersion);
+ }
+
+ if (hwcContents.outbuf != nullptr) {
+ output << fill << "Output buffer: " << hwcContents.outbuf << "/" <<
+ hwcContents.outbufAcquireFenceFd << '\n';
+ }
+
+ return output.str();
+}
+
+std::string HWC2On1Adapter::Display::dump() const {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ std::stringstream output;
+
+ output << " Display " << mId << ": ";
+ output << to_string(mType) << " ";
+ output << "HWC1 ID: " << mHwc1Id << " ";
+ output << "Power mode: " << to_string(mPowerMode) << " ";
+ output << "Vsync: " << to_string(mVsyncEnabled) << '\n';
+
+ output << " Color modes [active]:";
+ for (const auto& mode : mColorModes) {
+ if (mode == mActiveColorMode) {
+ output << " [" << mode << ']';
+ } else {
+ output << " " << mode;
+ }
+ }
+ output << '\n';
+
+ output << " " << mConfigs.size() << " Config" <<
+ (mConfigs.size() == 1 ? "" : "s") << " (* active)\n";
+ for (const auto& config : mConfigs) {
+ output << (config == mActiveConfig ? " * " : " ");
+ output << config->toString(true) << '\n';
+ }
+
+ output << " " << mLayers.size() << " Layer" <<
+ (mLayers.size() == 1 ? "" : "s") << '\n';
+ for (const auto& layer : mLayers) {
+ output << layer->dump();
+ }
+
+ output << " Client target: " << mClientTarget.getBuffer() << '\n';
+
+ if (mOutputBuffer.getBuffer() != nullptr) {
+ output << " Output buffer: " << mOutputBuffer.getBuffer() << '\n';
+ }
+
+ if (mHwc1RequestedContents) {
+ output << " Last requested HWC1 state\n";
+ output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion);
+ }
+
+ return output.str();
+}
+
+hwc_rect_t* HWC2On1Adapter::Display::GetRects(size_t numRects) {
+ if (numRects == 0) {
+ return nullptr;
+ }
+
+ if (numRects > mNumAvailableRects) {
+ // This should NEVER happen since we calculated how many rects the
+ // display would need.
+ ALOGE("Rect allocation failure! SF is likely to crash soon!");
+ return nullptr;
+
+ }
+ hwc_rect_t* rects = mNextAvailableRect;
+ mNextAvailableRect += numRects;
+ mNumAvailableRects -= numRects;
+ return rects;
+}
+
+hwc_display_contents_1* HWC2On1Adapter::Display::getDisplayContents() {
+ return mHwc1RequestedContents.get();
+}
+
+void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute,
+ int32_t value) {
+ mAttributes[attribute] = value;
+}
+
+int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const {
+ if (mAttributes.count(attribute) == 0) {
+ return -1;
+ }
+ return mAttributes.at(attribute);
+}
+
+void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id) {
+ android_color_mode_t colorMode = static_cast<android_color_mode_t>(getAttribute(ColorMode));
+ mHwc1Ids.emplace(colorMode, id);
+}
+
+bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const {
+ for (const auto& idPair : mHwc1Ids) {
+ if (id == idPair.second) {
+ return true;
+ }
+ }
+ return false;
+}
+
+Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id(
+ uint32_t id, android_color_mode_t* outMode) const {
+ for (const auto& idPair : mHwc1Ids) {
+ if (id == idPair.second) {
+ *outMode = idPair.first;
+ return Error::None;
+ }
+ }
+ ALOGE("Unable to find color mode for HWC ID %" PRIu32 " on config %u", id, mId);
+ return Error::BadParameter;
+}
+
+Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode,
+ uint32_t* outId) const {
+ for (const auto& idPair : mHwc1Ids) {
+ if (mode == idPair.first) {
+ *outId = idPair.second;
+ return Error::None;
+ }
+ }
+ ALOGE("Unable to find HWC1 ID for color mode %d on config %u", mode, mId);
+ return Error::BadParameter;
+}
+
+bool HWC2On1Adapter::Display::Config::merge(const Config& other) {
+ auto attributes = {HWC2::Attribute::Width, HWC2::Attribute::Height,
+ HWC2::Attribute::VsyncPeriod, HWC2::Attribute::DpiX,
+ HWC2::Attribute::DpiY};
+ for (auto attribute : attributes) {
+ if (getAttribute(attribute) != other.getAttribute(attribute)) {
+ return false;
+ }
+ }
+ android_color_mode_t otherColorMode =
+ static_cast<android_color_mode_t>(other.getAttribute(ColorMode));
+ if (mHwc1Ids.count(otherColorMode) != 0) {
+ ALOGE("Attempted to merge two configs (%u and %u) which appear to be "
+ "identical", mHwc1Ids.at(otherColorMode),
+ other.mHwc1Ids.at(otherColorMode));
+ return false;
+ }
+ mHwc1Ids.emplace(otherColorMode,
+ other.mHwc1Ids.at(otherColorMode));
+ return true;
+}
+
+std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const {
+ std::set<android_color_mode_t> colorModes;
+ for (const auto& idPair : mHwc1Ids) {
+ colorModes.emplace(idPair.first);
+ }
+ return colorModes;
+}
+
+std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const {
+ std::string output;
+
+ const size_t BUFFER_SIZE = 100;
+ char buffer[BUFFER_SIZE] = {};
+ auto writtenBytes = snprintf(buffer, BUFFER_SIZE,
+ "%u x %u", mAttributes.at(HWC2::Attribute::Width),
+ mAttributes.at(HWC2::Attribute::Height));
+ output.append(buffer, writtenBytes);
+
+ if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) {
+ std::memset(buffer, 0, BUFFER_SIZE);
+ writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz",
+ 1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod));
+ output.append(buffer, writtenBytes);
+ }
+
+ if (mAttributes.count(HWC2::Attribute::DpiX) != 0 &&
+ mAttributes.at(HWC2::Attribute::DpiX) != -1) {
+ std::memset(buffer, 0, BUFFER_SIZE);
+ writtenBytes = snprintf(buffer, BUFFER_SIZE,
+ ", DPI: %.1f x %.1f",
+ mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f,
+ mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f);
+ output.append(buffer, writtenBytes);
+ }
+
+ std::memset(buffer, 0, BUFFER_SIZE);
+ if (splitLine) {
+ writtenBytes = snprintf(buffer, BUFFER_SIZE,
+ "\n HWC1 ID/Color transform:");
+ } else {
+ writtenBytes = snprintf(buffer, BUFFER_SIZE,
+ ", HWC1 ID/Color transform:");
+ }
+ output.append(buffer, writtenBytes);
+
+
+ for (const auto& id : mHwc1Ids) {
+ android_color_mode_t colorMode = id.first;
+ uint32_t hwc1Id = id.second;
+ std::memset(buffer, 0, BUFFER_SIZE);
+ if (colorMode == mDisplay.mActiveColorMode) {
+ writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id,
+ colorMode);
+ } else {
+ writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id,
+ colorMode);
+ }
+ output.append(buffer, writtenBytes);
+ }
+
+ return output;
+}
+
+std::shared_ptr<const HWC2On1Adapter::Display::Config>
+ HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const {
+ if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
+ return nullptr;
+ }
+ return mConfigs[configId];
+}
+
+void HWC2On1Adapter::Display::populateColorModes() {
+ mColorModes = mConfigs[0]->getColorModes();
+ for (const auto& config : mConfigs) {
+ std::set<android_color_mode_t> intersection;
+ auto configModes = config->getColorModes();
+ std::set_intersection(mColorModes.cbegin(), mColorModes.cend(),
+ configModes.cbegin(), configModes.cend(),
+ std::inserter(intersection, intersection.begin()));
+ std::swap(intersection, mColorModes);
+ }
+}
+
+void HWC2On1Adapter::Display::initializeActiveConfig() {
+ if (mDevice.mHwc1Device->getActiveConfig == nullptr) {
+ ALOGV("getActiveConfig is null, choosing config 0");
+ mActiveConfig = mConfigs[0];
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ return;
+ }
+
+ auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
+ mDevice.mHwc1Device, mHwc1Id);
+
+ // Some devices startup without an activeConfig:
+ // We need to set one ourselves.
+ if (activeConfig == HWC_ERROR) {
+ ALOGV("There is no active configuration: Picking the first one: 0.");
+ const int defaultIndex = 0;
+ mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, defaultIndex);
+ activeConfig = defaultIndex;
+ }
+
+ for (const auto& config : mConfigs) {
+ if (config->hasHwc1Id(activeConfig)) {
+ ALOGE("Setting active config to %d for HWC1 config %u", config->getId(), activeConfig);
+ mActiveConfig = config;
+ if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) {
+ // This should never happen since we checked for the config's presence before
+ // setting it as active.
+ ALOGE("Unable to find color mode for active HWC1 config %d", config->getId());
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ }
+ break;
+ }
+ }
+ if (!mActiveConfig) {
+ ALOGV("Unable to find active HWC1 config %u, defaulting to "
+ "config 0", activeConfig);
+ mActiveConfig = mConfigs[0];
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ }
+
+
+
+
+}
+
+void HWC2On1Adapter::Display::allocateRequestedContents() {
+ // What needs to be allocated:
+ // 1 hwc_display_contents_1_t
+ // 1 hwc_layer_1_t for each layer
+ // 1 hwc_rect_t for each layer's surfaceDamage
+ // 1 hwc_rect_t for each layer's visibleRegion
+ // 1 hwc_layer_1_t for the framebuffer
+ // 1 hwc_rect_t for the framebuffer's visibleRegion
+
+ // Count # of surfaceDamage
+ size_t numSurfaceDamages = 0;
+ for (const auto& layer : mLayers) {
+ numSurfaceDamages += layer->getNumSurfaceDamages();
+ }
+
+ // Count # of visibleRegions (start at 1 for mandatory framebuffer target
+ // region)
+ size_t numVisibleRegion = 1;
+ for (const auto& layer : mLayers) {
+ numVisibleRegion += layer->getNumVisibleRegions();
+ }
+
+ size_t numRects = numVisibleRegion + numSurfaceDamages;
+ auto numLayers = mLayers.size() + 1;
+ size_t size = sizeof(hwc_display_contents_1_t) +
+ sizeof(hwc_layer_1_t) * numLayers +
+ sizeof(hwc_rect_t) * numRects;
+ auto contents = static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1));
+ mHwc1RequestedContents.reset(contents);
+ mNextAvailableRect = reinterpret_cast<hwc_rect_t*>(&contents->hwLayers[numLayers]);
+ mNumAvailableRects = numRects;
+}
+
+void HWC2On1Adapter::Display::assignHwc1LayerIds() {
+ mHwc1LayerMap.clear();
+ size_t nextHwc1Id = 0;
+ for (auto& layer : mLayers) {
+ mHwc1LayerMap[nextHwc1Id] = layer;
+ layer->setHwc1Id(nextHwc1Id++);
+ }
+}
+
+void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer,
+ const Layer& layer) {
+ auto layerId = layer.getId();
+ switch (hwc1Layer.compositionType) {
+ case HWC_FRAMEBUFFER:
+ if (layer.getCompositionType() != Composition::Client) {
+ mChanges->addTypeChange(layerId, Composition::Client);
+ }
+ break;
+ case HWC_OVERLAY:
+ if (layer.getCompositionType() != Composition::Device) {
+ mChanges->addTypeChange(layerId, Composition::Device);
+ }
+ break;
+ case HWC_BACKGROUND:
+ ALOGE_IF(layer.getCompositionType() != Composition::SolidColor,
+ "updateTypeChanges: HWC1 requested BACKGROUND, but HWC2"
+ " wasn't expecting SolidColor");
+ break;
+ case HWC_FRAMEBUFFER_TARGET:
+ // Do nothing, since it shouldn't be modified by HWC1
+ break;
+ case HWC_SIDEBAND:
+ ALOGE_IF(layer.getCompositionType() != Composition::Sideband,
+ "updateTypeChanges: HWC1 requested SIDEBAND, but HWC2"
+ " wasn't expecting Sideband");
+ break;
+ case HWC_CURSOR_OVERLAY:
+ ALOGE_IF(layer.getCompositionType() != Composition::Cursor,
+ "updateTypeChanges: HWC1 requested CURSOR_OVERLAY, but"
+ " HWC2 wasn't expecting Cursor");
+ break;
+ }
+}
+
+void HWC2On1Adapter::Display::updateLayerRequests(
+ const hwc_layer_1_t& hwc1Layer, const Layer& layer) {
+ if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) {
+ mChanges->addLayerRequest(layer.getId(),
+ LayerRequest::ClearClientTarget);
+ }
+}
+
+void HWC2On1Adapter::Display::prepareFramebufferTarget() {
+ // We check that mActiveConfig is valid in Display::prepare
+ int32_t width = mActiveConfig->getAttribute(Attribute::Width);
+ int32_t height = mActiveConfig->getAttribute(Attribute::Height);
+
+ auto& hwc1Target = mHwc1RequestedContents->hwLayers[mLayers.size()];
+ hwc1Target.compositionType = HWC_FRAMEBUFFER_TARGET;
+ hwc1Target.releaseFenceFd = -1;
+ hwc1Target.hints = 0;
+ hwc1Target.flags = 0;
+ hwc1Target.transform = 0;
+ hwc1Target.blending = HWC_BLENDING_PREMULT;
+ if (mDevice.getHwc1MinorVersion() < 3) {
+ hwc1Target.sourceCropi = {0, 0, width, height};
+ } else {
+ hwc1Target.sourceCropf = {0.0f, 0.0f, static_cast<float>(width),
+ static_cast<float>(height)};
+ }
+ hwc1Target.displayFrame = {0, 0, width, height};
+ hwc1Target.planeAlpha = 255;
+
+ hwc1Target.visibleRegionScreen.numRects = 1;
+ hwc_rect_t* rects = GetRects(1);
+ rects[0].left = 0;
+ rects[0].top = 0;
+ rects[0].right = width;
+ rects[0].bottom = height;
+ hwc1Target.visibleRegionScreen.rects = rects;
+
+ // We will set this to the correct value in set
+ hwc1Target.acquireFenceFd = -1;
+}
+
+// Layer functions
+
+std::atomic<hwc2_layer_t> HWC2On1Adapter::Layer::sNextId(1);
+
+HWC2On1Adapter::Layer::Layer(Display& display)
+ : mId(sNextId++),
+ mDisplay(display),
+ mBuffer(),
+ mSurfaceDamage(),
+ mBlendMode(BlendMode::None),
+ mColor({0, 0, 0, 0}),
+ mCompositionType(Composition::Invalid),
+ mDisplayFrame({0, 0, -1, -1}),
+ mPlaneAlpha(0.0f),
+ mSidebandStream(nullptr),
+ mSourceCrop({0.0f, 0.0f, -1.0f, -1.0f}),
+ mTransform(Transform::None),
+ mVisibleRegion(),
+ mZ(0),
+ mReleaseFence(),
+ mHwc1Id(0),
+ mHasUnsupportedPlaneAlpha(false) {}
+
+bool HWC2On1Adapter::SortLayersByZ::operator()(
+ const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs) {
+ return lhs->getZ() < rhs->getZ();
+}
+
+Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer,
+ int32_t acquireFence) {
+ ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId);
+ mBuffer.setBuffer(buffer);
+ mBuffer.setFence(acquireFence);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y) {
+ if (mCompositionType != Composition::Cursor) {
+ return Error::BadLayer;
+ }
+
+ if (mDisplay.hasChanges()) {
+ return Error::NotValidated;
+ }
+
+ auto displayId = mDisplay.getHwc1Id();
+ auto hwc1Device = mDisplay.getDevice().getHwc1Device();
+ hwc1Device->setCursorPositionAsync(hwc1Device, displayId, x, y);
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage) {
+ // HWC1 supports surface damage starting only with version 1.5.
+ if (mDisplay.getDevice().mHwc1MinorVersion < 5) {
+ return Error::None;
+ }
+ mSurfaceDamage.resize(damage.numRects);
+ std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin());
+ return Error::None;
+}
+
+// Layer state functions
+
+Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode) {
+ mBlendMode = mode;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setColor(hwc_color_t color) {
+ mColor = color;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setCompositionType(Composition type) {
+ mCompositionType = type;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t) {
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame) {
+ mDisplayFrame = frame;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha) {
+ mPlaneAlpha = alpha;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream) {
+ mSidebandStream = stream;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop) {
+ mSourceCrop = crop;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setTransform(Transform transform) {
+ mTransform = transform;
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) {
+ mVisibleRegion.resize(visible.numRects);
+ std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin());
+ mDisplay.markGeometryChanged();
+ return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setZ(uint32_t z) {
+ mZ = z;
+ return Error::None;
+}
+
+void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd) {
+ ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId);
+ mReleaseFence.add(fenceFd);
+}
+
+const sp<MiniFence>& HWC2On1Adapter::Layer::getReleaseFence() const {
+ return mReleaseFence.get();
+}
+
+void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer) {
+ applyCommonState(hwc1Layer);
+ applyCompositionType(hwc1Layer);
+ switch (mCompositionType) {
+ case Composition::SolidColor : applySolidColorState(hwc1Layer); break;
+ case Composition::Sideband : applySidebandState(hwc1Layer); break;
+ default: applyBufferState(hwc1Layer); break;
+ }
+}
+
+static std::string regionStrings(const std::vector<hwc_rect_t>& visibleRegion,
+ const std::vector<hwc_rect_t>& surfaceDamage) {
+ std::string regions;
+ regions += " Visible Region";
+ regions.resize(40, ' ');
+ regions += "Surface Damage\n";
+
+ size_t numPrinted = 0;
+ size_t maxSize = std::max(visibleRegion.size(), surfaceDamage.size());
+ while (numPrinted < maxSize) {
+ std::string line(" ");
+ if (visibleRegion.empty() && numPrinted == 0) {
+ line += "None";
+ } else if (numPrinted < visibleRegion.size()) {
+ line += rectString(visibleRegion[numPrinted]);
+ }
+ line.resize(40, ' ');
+ if (surfaceDamage.empty() && numPrinted == 0) {
+ line += "None";
+ } else if (numPrinted < surfaceDamage.size()) {
+ line += rectString(surfaceDamage[numPrinted]);
+ }
+ line += '\n';
+ regions += line;
+ ++numPrinted;
+ }
+ return regions;
+}
+
+std::string HWC2On1Adapter::Layer::dump() const {
+ std::stringstream output;
+ const char* fill = " ";
+
+ output << fill << to_string(mCompositionType);
+ output << " Layer HWC2/1: " << mId << "/" << mHwc1Id << " ";
+ output << "Z: " << mZ;
+ if (mCompositionType == HWC2::Composition::SolidColor) {
+ output << " " << colorString(mColor);
+ } else if (mCompositionType == HWC2::Composition::Sideband) {
+ output << " Handle: " << mSidebandStream << '\n';
+ } else {
+ output << " Buffer: " << mBuffer.getBuffer() << "/" <<
+ mBuffer.getFence() << '\n';
+ output << fill << " Display frame [LTRB]: " <<
+ rectString(mDisplayFrame) << '\n';
+ output << fill << " Source crop: " <<
+ frectString(mSourceCrop) << '\n';
+ output << fill << " Transform: " << to_string(mTransform);
+ output << " Blend mode: " << to_string(mBlendMode);
+ if (mPlaneAlpha != 1.0f) {
+ output << " Alpha: " <<
+ alphaString(mPlaneAlpha) << '\n';
+ } else {
+ output << '\n';
+ }
+ output << regionStrings(mVisibleRegion, mSurfaceDamage);
+ }
+ return output.str();
+}
+
+static int getHwc1Blending(HWC2::BlendMode blendMode) {
+ switch (blendMode) {
+ case BlendMode::Coverage: return HWC_BLENDING_COVERAGE;
+ case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT;
+ default: return HWC_BLENDING_NONE;
+ }
+}
+
+void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer) {
+ auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion();
+ hwc1Layer.blending = getHwc1Blending(mBlendMode);
+ hwc1Layer.displayFrame = mDisplayFrame;
+
+ auto pendingAlpha = mPlaneAlpha;
+ if (minorVersion < 2) {
+ mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f;
+ } else {
+ hwc1Layer.planeAlpha =
+ static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f);
+ }
+
+ if (minorVersion < 3) {
+ auto pending = mSourceCrop;
+ hwc1Layer.sourceCropi.left =
+ static_cast<int32_t>(std::ceil(pending.left));
+ hwc1Layer.sourceCropi.top =
+ static_cast<int32_t>(std::ceil(pending.top));
+ hwc1Layer.sourceCropi.right =
+ static_cast<int32_t>(std::floor(pending.right));
+ hwc1Layer.sourceCropi.bottom =
+ static_cast<int32_t>(std::floor(pending.bottom));
+ } else {
+ hwc1Layer.sourceCropf = mSourceCrop;
+ }
+
+ hwc1Layer.transform = static_cast<uint32_t>(mTransform);
+
+ auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen;
+ hwc1VisibleRegion.numRects = mVisibleRegion.size();
+ hwc_rect_t* rects = mDisplay.GetRects(hwc1VisibleRegion.numRects);
+ hwc1VisibleRegion.rects = rects;
+ for (size_t i = 0; i < mVisibleRegion.size(); i++) {
+ rects[i] = mVisibleRegion[i];
+ }
+}
+
+void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer) {
+ // If the device does not support background color it is likely to make
+ // assumption regarding backgroundColor and handle (both fields occupy
+ // the same location in hwc_layer_1_t union).
+ // To not confuse these devices we don't set background color and we
+ // make sure handle is a null pointer.
+ if (hasUnsupportedBackgroundColor()) {
+ hwc1Layer.handle = nullptr;
+ } else {
+ hwc1Layer.backgroundColor = mColor;
+ }
+}
+
+void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer) {
+ hwc1Layer.sidebandStream = mSidebandStream;
+}
+
+void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer) {
+ hwc1Layer.handle = mBuffer.getBuffer();
+ hwc1Layer.acquireFenceFd = mBuffer.getFence();
+}
+
+void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer) {
+ // HWC1 never supports color transforms or dataspaces and only sometimes
+ // supports plane alpha (depending on the version). These require us to drop
+ // some or all layers to client composition.
+ if (mHasUnsupportedPlaneAlpha || mDisplay.hasColorTransform() ||
+ hasUnsupportedBackgroundColor()) {
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags = HWC_SKIP_LAYER;
+ return;
+ }
+
+ hwc1Layer.flags = 0;
+ switch (mCompositionType) {
+ case Composition::Client:
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags |= HWC_SKIP_LAYER;
+ break;
+ case Composition::Device:
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ break;
+ case Composition::SolidColor:
+ // In theory the following line should work, but since the HWC1
+ // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1
+ // devices may not work correctly. To be on the safe side, we
+ // fall back to client composition.
+ //
+ // hwc1Layer.compositionType = HWC_BACKGROUND;
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags |= HWC_SKIP_LAYER;
+ break;
+ case Composition::Cursor:
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) {
+ hwc1Layer.hints |= HWC_IS_CURSOR_LAYER;
+ }
+ break;
+ case Composition::Sideband:
+ if (mDisplay.getDevice().getHwc1MinorVersion() < 4) {
+ hwc1Layer.compositionType = HWC_SIDEBAND;
+ } else {
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags |= HWC_SKIP_LAYER;
+ }
+ break;
+ default:
+ hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+ hwc1Layer.flags |= HWC_SKIP_LAYER;
+ break;
+ }
+ ALOGV("Layer %" PRIu64 " %s set to %d", mId,
+ to_string(mCompositionType).c_str(),
+ hwc1Layer.compositionType);
+ ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, " and skipping");
+}
+
+// Adapter helpers
+
+void HWC2On1Adapter::populateCapabilities() {
+ if (mHwc1MinorVersion >= 3U) {
+ int supportedTypes = 0;
+ auto result = mHwc1Device->query(mHwc1Device,
+ HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes);
+ if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) {
+ ALOGI("Found support for HWC virtual displays");
+ mHwc1SupportsVirtualDisplays = true;
+ }
+ }
+ if (mHwc1MinorVersion >= 4U) {
+ mCapabilities.insert(Capability::SidebandStream);
+ }
+
+ // Check for HWC background color layer support.
+ if (mHwc1MinorVersion >= 1U) {
+ int backgroundColorSupported = 0;
+ auto result = mHwc1Device->query(mHwc1Device,
+ HWC_BACKGROUND_LAYER_SUPPORTED,
+ &backgroundColorSupported);
+ if ((result == 0) && (backgroundColorSupported == 1)) {
+ ALOGV("Found support for HWC background color");
+ mHwc1SupportsBackgroundColor = true;
+ }
+ }
+
+ // Some devices might have HWC1 retire fences that accurately emulate
+ // HWC2 present fences when they are deferred, but it's not very reliable.
+ // To be safe, we indicate PresentFenceIsNotReliable for all HWC1 devices.
+ mCapabilities.insert(Capability::PresentFenceIsNotReliable);
+}
+
+HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id) {
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ auto display = mDisplays.find(id);
+ if (display == mDisplays.end()) {
+ return nullptr;
+ }
+
+ return display->second.get();
+}
+
+std::tuple<HWC2On1Adapter::Layer*, Error> HWC2On1Adapter::getLayer(
+ hwc2_display_t displayId, hwc2_layer_t layerId) {
+ auto display = getDisplay(displayId);
+ if (!display) {
+ return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay);
+ }
+
+ auto layerEntry = mLayers.find(layerId);
+ if (layerEntry == mLayers.end()) {
+ return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
+ }
+
+ auto layer = layerEntry->second;
+ if (layer->getDisplay().getId() != displayId) {
+ return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
+ }
+ return std::make_tuple(layer.get(), Error::None);
+}
+
+void HWC2On1Adapter::populatePrimary() {
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ auto display = std::make_shared<Display>(*this, HWC2::DisplayType::Physical);
+ mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId();
+ display->setHwc1Id(HWC_DISPLAY_PRIMARY);
+ display->populateConfigs();
+ mDisplays.emplace(display->getId(), std::move(display));
+}
+
+bool HWC2On1Adapter::prepareAllDisplays() {
+ ATRACE_CALL();
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ for (const auto& displayPair : mDisplays) {
+ auto& display = displayPair.second;
+ if (!display->prepare()) {
+ return false;
+ }
+ }
+
+ if (mHwc1DisplayMap.count(HWC_DISPLAY_PRIMARY) == 0) {
+ ALOGE("prepareAllDisplays: Unable to find primary HWC1 display");
+ return false;
+ }
+
+ // Build an array of hwc_display_contents_1 to call prepare() on HWC1.
+ mHwc1Contents.clear();
+
+ // Always push the primary display
+ auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY];
+ auto& primaryDisplay = mDisplays[primaryDisplayId];
+ mHwc1Contents.push_back(primaryDisplay->getDisplayContents());
+
+ // Push the external display, if present
+ if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) {
+ auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL];
+ auto& externalDisplay = mDisplays[externalDisplayId];
+ mHwc1Contents.push_back(externalDisplay->getDisplayContents());
+ } else {
+ // Even if an external display isn't present, we still need to send
+ // at least two displays down to HWC1
+ mHwc1Contents.push_back(nullptr);
+ }
+
+ // Push the hardware virtual display, if supported and present
+ if (mHwc1MinorVersion >= 3) {
+ if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) {
+ auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL];
+ auto& virtualDisplay = mDisplays[virtualDisplayId];
+ mHwc1Contents.push_back(virtualDisplay->getDisplayContents());
+ } else {
+ mHwc1Contents.push_back(nullptr);
+ }
+ }
+
+ for (auto& displayContents : mHwc1Contents) {
+ if (!displayContents) {
+ continue;
+ }
+
+ ALOGV("Display %zd layers:", mHwc1Contents.size() - 1);
+ for (size_t l = 0; l < displayContents->numHwLayers; ++l) {
+ auto& layer = displayContents->hwLayers[l];
+ ALOGV(" %zd: %d", l, layer.compositionType);
+ }
+ }
+
+ ALOGV("Calling HWC1 prepare");
+ {
+ ATRACE_NAME("HWC1 prepare");
+ mHwc1Device->prepare(mHwc1Device, mHwc1Contents.size(),
+ mHwc1Contents.data());
+ }
+
+ for (size_t c = 0; c < mHwc1Contents.size(); ++c) {
+ auto& contents = mHwc1Contents[c];
+ if (!contents) {
+ continue;
+ }
+ ALOGV("Display %zd layers:", c);
+ for (size_t l = 0; l < contents->numHwLayers; ++l) {
+ ALOGV(" %zd: %d", l, contents->hwLayers[l].compositionType);
+ }
+ }
+
+ // Return the received contents to their respective displays
+ for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+ if (mHwc1Contents[hwc1Id] == nullptr) {
+ continue;
+ }
+
+ auto displayId = mHwc1DisplayMap[hwc1Id];
+ auto& display = mDisplays[displayId];
+ display->generateChanges();
+ }
+
+ return true;
+}
+
+void dumpHWC1Message(hwc_composer_device_1* device, size_t numDisplays,
+ hwc_display_contents_1_t** displays) {
+ ALOGV("*****************************");
+ size_t displayId = 0;
+ while (displayId < numDisplays) {
+ hwc_display_contents_1_t* display = displays[displayId];
+
+ ALOGV("hwc_display_contents_1_t[%zu] @0x%p", displayId, display);
+ if (display == nullptr) {
+ displayId++;
+ continue;
+ }
+ ALOGV(" retirefd:0x%08x", display->retireFenceFd);
+ ALOGV(" outbuf :0x%p", display->outbuf);
+ ALOGV(" outbuffd:0x%08x", display->outbufAcquireFenceFd);
+ ALOGV(" flags :0x%08x", display->flags);
+ for(size_t layerId=0 ; layerId < display->numHwLayers ; layerId++) {
+ hwc_layer_1_t& layer = display->hwLayers[layerId];
+ ALOGV(" Layer[%zu]:", layerId);
+ ALOGV(" composition : 0x%08x", layer.compositionType);
+ ALOGV(" hints : 0x%08x", layer.hints);
+ ALOGV(" flags : 0x%08x", layer.flags);
+ ALOGV(" handle : 0x%p", layer.handle);
+ ALOGV(" transform : 0x%08x", layer.transform);
+ ALOGV(" blending : 0x%08x", layer.blending);
+ ALOGV(" sourceCropf : %f, %f, %f, %f",
+ layer.sourceCropf.left,
+ layer.sourceCropf.top,
+ layer.sourceCropf.right,
+ layer.sourceCropf.bottom);
+ ALOGV(" displayFrame : %d, %d, %d, %d",
+ layer.displayFrame.left,
+ layer.displayFrame.left,
+ layer.displayFrame.left,
+ layer.displayFrame.left);
+ hwc_region_t& visReg = layer.visibleRegionScreen;
+ ALOGV(" visibleRegionScreen: #0x%08zx[@0x%p]",
+ visReg.numRects,
+ visReg.rects);
+ for (size_t visRegId=0; visRegId < visReg.numRects ; visRegId++) {
+ if (layer.visibleRegionScreen.rects == nullptr) {
+ ALOGV(" null");
+ } else {
+ ALOGV(" visibleRegionScreen[%zu] %d, %d, %d, %d",
+ visRegId,
+ visReg.rects[visRegId].left,
+ visReg.rects[visRegId].top,
+ visReg.rects[visRegId].right,
+ visReg.rects[visRegId].bottom);
+ }
+ }
+ ALOGV(" acquireFenceFd : 0x%08x", layer.acquireFenceFd);
+ ALOGV(" releaseFenceFd : 0x%08x", layer.releaseFenceFd);
+ ALOGV(" planeAlpha : 0x%08x", layer.planeAlpha);
+ if (getMinorVersion(device) < 5)
+ continue;
+ ALOGV(" surfaceDamage : #0x%08zx[@0x%p]",
+ layer.surfaceDamage.numRects,
+ layer.surfaceDamage.rects);
+ for (size_t sdId=0; sdId < layer.surfaceDamage.numRects ; sdId++) {
+ if (layer.surfaceDamage.rects == nullptr) {
+ ALOGV(" null");
+ } else {
+ ALOGV(" surfaceDamage[%zu] %d, %d, %d, %d",
+ sdId,
+ layer.surfaceDamage.rects[sdId].left,
+ layer.surfaceDamage.rects[sdId].top,
+ layer.surfaceDamage.rects[sdId].right,
+ layer.surfaceDamage.rects[sdId].bottom);
+ }
+ }
+ }
+ displayId++;
+ }
+ ALOGV("-----------------------------");
+}
+
+Error HWC2On1Adapter::setAllDisplays() {
+ ATRACE_CALL();
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ // Make sure we're ready to validate
+ for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+ if (mHwc1Contents[hwc1Id] == nullptr) {
+ continue;
+ }
+
+ auto displayId = mHwc1DisplayMap[hwc1Id];
+ auto& display = mDisplays[displayId];
+ Error error = display->set(*mHwc1Contents[hwc1Id]);
+ if (error != Error::None) {
+ ALOGE("setAllDisplays: Failed to set display %zd: %s", hwc1Id,
+ to_string(error).c_str());
+ return error;
+ }
+ }
+
+ ALOGV("Calling HWC1 set");
+ {
+ ATRACE_NAME("HWC1 set");
+ //dumpHWC1Message(mHwc1Device, mHwc1Contents.size(), mHwc1Contents.data());
+ mHwc1Device->set(mHwc1Device, mHwc1Contents.size(),
+ mHwc1Contents.data());
+ }
+
+ // Add retire and release fences
+ for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+ if (mHwc1Contents[hwc1Id] == nullptr) {
+ continue;
+ }
+
+ auto displayId = mHwc1DisplayMap[hwc1Id];
+ auto& display = mDisplays[displayId];
+ auto retireFenceFd = mHwc1Contents[hwc1Id]->retireFenceFd;
+ ALOGV("setAllDisplays: Adding retire fence %d to display %zd",
+ retireFenceFd, hwc1Id);
+ display->addRetireFence(mHwc1Contents[hwc1Id]->retireFenceFd);
+ display->addReleaseFences(*mHwc1Contents[hwc1Id]);
+ }
+
+ return Error::None;
+}
+
+void HWC2On1Adapter::hwc1Invalidate() {
+ ALOGV("Received hwc1Invalidate");
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ // If the HWC2-side callback hasn't been registered yet, buffer this until
+ // it is registered.
+ if (mCallbacks.count(Callback::Refresh) == 0) {
+ mHasPendingInvalidate = true;
+ return;
+ }
+
+ const auto& callbackInfo = mCallbacks[Callback::Refresh];
+ std::vector<hwc2_display_t> displays;
+ for (const auto& displayPair : mDisplays) {
+ displays.emplace_back(displayPair.first);
+ }
+
+ // Call back without the state lock held.
+ lock.unlock();
+
+ auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(callbackInfo.pointer);
+ for (auto display : displays) {
+ refresh(callbackInfo.data, display);
+ }
+}
+
+void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp) {
+ ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp);
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ // If the HWC2-side callback hasn't been registered yet, buffer this until
+ // it is registered.
+ if (mCallbacks.count(Callback::Vsync) == 0) {
+ mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp);
+ return;
+ }
+
+ if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+ ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId);
+ return;
+ }
+
+ const auto& callbackInfo = mCallbacks[Callback::Vsync];
+ auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+
+ // Call back without the state lock held.
+ lock.unlock();
+
+ auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer);
+ vsync(callbackInfo.data, displayId, timestamp);
+}
+
+void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) {
+ ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected);
+
+ if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) {
+ ALOGE("hwc1Hotplug: Received hotplug for non-external display");
+ return;
+ }
+
+ std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
+
+ // If the HWC2-side callback hasn't been registered yet, buffer this until
+ // it is registered
+ if (mCallbacks.count(Callback::Hotplug) == 0) {
+ mPendingHotplugs.emplace_back(hwc1DisplayId, connected);
+ return;
+ }
+
+ hwc2_display_t displayId = UINT64_MAX;
+ if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+ if (connected == 0) {
+ ALOGW("hwc1Hotplug: Received disconnect for unconnected display");
+ return;
+ }
+
+ // Create a new display on connect
+ auto display = std::make_shared<HWC2On1Adapter::Display>(*this,
+ HWC2::DisplayType::Physical);
+ display->setHwc1Id(HWC_DISPLAY_EXTERNAL);
+ display->populateConfigs();
+ displayId = display->getId();
+ mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId;
+ mDisplays.emplace(displayId, std::move(display));
+ } else {
+ if (connected != 0) {
+ ALOGW("hwc1Hotplug: Received connect for previously connected "
+ "display");
+ return;
+ }
+
+ // Disconnect an existing display
+ displayId = mHwc1DisplayMap[hwc1DisplayId];
+ mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL);
+ mDisplays.erase(displayId);
+ }
+
+ const auto& callbackInfo = mCallbacks[Callback::Hotplug];
+
+ // Call back without the state lock held
+ lock.unlock();
+
+ auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer);
+ auto hwc2Connected = (connected == 0) ?
+ HWC2::Connection::Disconnected : HWC2::Connection::Connected;
+ hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected));
+}
+} // namespace android
diff --git a/libs/hwc2on1adapter/MiniFence.cpp b/libs/hwc2on1adapter/MiniFence.cpp
new file mode 100644
index 0000000000..dfbe4d63cc
--- /dev/null
+++ b/libs/hwc2on1adapter/MiniFence.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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 "hwc2on1adapter/MiniFence.h"
+
+#include <unistd.h>
+
+namespace android {
+
+const sp<MiniFence> MiniFence::NO_FENCE = sp<MiniFence>(new MiniFence);
+
+MiniFence::MiniFence() :
+ mFenceFd(-1) {
+}
+
+MiniFence::MiniFence(int fenceFd) :
+ mFenceFd(fenceFd) {
+}
+
+MiniFence::~MiniFence() {
+ if (mFenceFd != -1) {
+ close(mFenceFd);
+ }
+}
+
+int MiniFence::dup() const {
+ return ::dup(mFenceFd);
+}
+}
diff --git a/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h b/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h
new file mode 100644
index 0000000000..3badfce078
--- /dev/null
+++ b/libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h
@@ -0,0 +1,738 @@
+/*
+ * Copyright 2015 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 ANDROID_SF_HWC2_ON_1_ADAPTER_H
+#define ANDROID_SF_HWC2_ON_1_ADAPTER_H
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include "MiniFence.h"
+
+#include <atomic>
+#include <map>
+#include <mutex>
+#include <queue>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+struct hwc_composer_device_1;
+struct hwc_display_contents_1;
+struct hwc_layer_1;
+
+namespace android {
+
+// For devices unable to provide an implementation of HWC2 (see hwcomposer2.h),
+// we provide an adapter able to talk to HWC1 (see hwcomposer.h). It translates
+// streamed function calls ala HWC2 model to batched array of structs calls ala
+// HWC1 model.
+class HWC2On1Adapter : public hwc2_device_t
+{
+public:
+ explicit HWC2On1Adapter(struct hwc_composer_device_1* hwc1Device);
+ ~HWC2On1Adapter();
+
+ struct hwc_composer_device_1* getHwc1Device() const { return mHwc1Device; }
+ uint8_t getHwc1MinorVersion() const { return mHwc1MinorVersion; }
+
+private:
+ static inline HWC2On1Adapter* getAdapter(hwc2_device_t* device) {
+ return static_cast<HWC2On1Adapter*>(device);
+ }
+
+ // getCapabilities
+
+ void doGetCapabilities(uint32_t* outCount,
+ int32_t* /*hwc2_capability_t*/ outCapabilities);
+ static void getCapabilitiesHook(hwc2_device_t* device, uint32_t* outCount,
+ int32_t* /*hwc2_capability_t*/ outCapabilities) {
+ getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
+ }
+
+ bool supportsBackgroundColor() {
+ return mHwc1SupportsBackgroundColor;
+ }
+
+ // getFunction
+
+ hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor);
+ static hwc2_function_pointer_t getFunctionHook(hwc2_device_t* device,
+ int32_t intDesc) {
+ auto descriptor = static_cast<HWC2::FunctionDescriptor>(intDesc);
+ return getAdapter(device)->doGetFunction(descriptor);
+ }
+
+ // Device functions
+
+ HWC2::Error createVirtualDisplay(uint32_t width, uint32_t height,
+ hwc2_display_t* outDisplay);
+ static int32_t createVirtualDisplayHook(hwc2_device_t* device,
+ uint32_t width, uint32_t height, int32_t* /*format*/,
+ hwc2_display_t* outDisplay) {
+ // HWC1 implementations cannot override the buffer format requested by
+ // the consumer
+ auto error = getAdapter(device)->createVirtualDisplay(width, height,
+ outDisplay);
+ return static_cast<int32_t>(error);
+ }
+
+ HWC2::Error destroyVirtualDisplay(hwc2_display_t display);
+ static int32_t destroyVirtualDisplayHook(hwc2_device_t* device,
+ hwc2_display_t display) {
+ auto error = getAdapter(device)->destroyVirtualDisplay(display);
+ return static_cast<int32_t>(error);
+ }
+
+ std::string mDumpString;
+ void dump(uint32_t* outSize, char* outBuffer);
+ static void dumpHook(hwc2_device_t* device, uint32_t* outSize,
+ char* outBuffer) {
+ getAdapter(device)->dump(outSize, outBuffer);
+ }
+
+ uint32_t getMaxVirtualDisplayCount();
+ static uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* device) {
+ return getAdapter(device)->getMaxVirtualDisplayCount();
+ }
+
+ HWC2::Error registerCallback(HWC2::Callback descriptor,
+ hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer);
+ static int32_t registerCallbackHook(hwc2_device_t* device,
+ int32_t intDesc, hwc2_callback_data_t callbackData,
+ hwc2_function_pointer_t pointer) {
+ auto descriptor = static_cast<HWC2::Callback>(intDesc);
+ auto error = getAdapter(device)->registerCallback(descriptor,
+ callbackData, pointer);
+ return static_cast<int32_t>(error);
+ }
+
+ // Display functions
+
+ class Layer;
+
+ class SortLayersByZ {
+ public:
+ bool operator()(const std::shared_ptr<Layer>& lhs,
+ const std::shared_ptr<Layer>& rhs);
+ };
+
+ // The semantics of the fences returned by the device differ between
+ // hwc1.set() and hwc2.present(). Read hwcomposer.h and hwcomposer2.h
+ // for more information.
+ //
+ // Release fences in hwc1 are obtained on set() for a frame n and signaled
+ // when the layer buffer is not needed for read operations anymore
+ // (typically on frame n+1). In HWC2, release fences are obtained with a
+ // special call after present() for frame n. These fences signal
+ // on frame n: More specifically, the fence for a given buffer provided in
+ // frame n will signal when the prior buffer is no longer required.
+ //
+ // A retire fence (HWC1) is signaled when a composition is replaced
+ // on the panel whereas a present fence (HWC2) is signaled when a
+ // composition starts to be displayed on a panel.
+ //
+ // The HWC2to1Adapter emulates the new fence semantics for a frame
+ // n by returning the fence from frame n-1. For frame 0, the adapter
+ // returns NO_FENCE.
+ class DeferredFence {
+ public:
+ DeferredFence()
+ : mFences({MiniFence::NO_FENCE, MiniFence::NO_FENCE}) {}
+
+ void add(int32_t fenceFd) {
+ mFences.emplace(new MiniFence(fenceFd));
+ mFences.pop();
+ }
+
+ const sp<MiniFence>& get() const {
+ return mFences.front();
+ }
+
+ private:
+ // There are always two fences in this queue.
+ std::queue<sp<MiniFence>> mFences;
+ };
+
+ class FencedBuffer {
+ public:
+ FencedBuffer() : mBuffer(nullptr), mFence(MiniFence::NO_FENCE) {}
+
+ void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; }
+ void setFence(int fenceFd) { mFence = new MiniFence(fenceFd); }
+
+ buffer_handle_t getBuffer() const { return mBuffer; }
+ int getFence() const { return mFence->dup(); }
+
+ private:
+ buffer_handle_t mBuffer;
+ sp<MiniFence> mFence;
+ };
+
+ class Display {
+ public:
+ Display(HWC2On1Adapter& device, HWC2::DisplayType type);
+
+ hwc2_display_t getId() const { return mId; }
+ HWC2On1Adapter& getDevice() const { return mDevice; }
+
+ // Does not require locking because it is set before adding the
+ // Displays to the Adapter's list of displays
+ void setHwc1Id(int32_t id) { mHwc1Id = id; }
+ int32_t getHwc1Id() const { return mHwc1Id; }
+
+ // HWC2 Display functions
+ HWC2::Error acceptChanges();
+ HWC2::Error createLayer(hwc2_layer_t* outLayerId);
+ HWC2::Error destroyLayer(hwc2_layer_t layerId);
+ HWC2::Error getActiveConfig(hwc2_config_t* outConfigId);
+ HWC2::Error getAttribute(hwc2_config_t configId,
+ HWC2::Attribute attribute, int32_t* outValue);
+ HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements,
+ hwc2_layer_t* outLayers, int32_t* outTypes);
+ HWC2::Error getColorModes(uint32_t* outNumModes, int32_t* outModes);
+ HWC2::Error getConfigs(uint32_t* outNumConfigs,
+ hwc2_config_t* outConfigIds);
+ HWC2::Error getDozeSupport(int32_t* outSupport);
+ HWC2::Error getHdrCapabilities(uint32_t* outNumTypes,
+ int32_t* outTypes, float* outMaxLuminance,
+ float* outMaxAverageLuminance, float* outMinLuminance);
+ HWC2::Error getName(uint32_t* outSize, char* outName);
+ HWC2::Error getReleaseFences(uint32_t* outNumElements,
+ hwc2_layer_t* outLayers, int32_t* outFences);
+ HWC2::Error getRequests(int32_t* outDisplayRequests,
+ uint32_t* outNumElements, hwc2_layer_t* outLayers,
+ int32_t* outLayerRequests);
+ HWC2::Error getType(int32_t* outType);
+
+ // Since HWC1 "presents" (called "set" in HWC1) all Displays
+ // at once, the first call to any Display::present will trigger
+ // present() on all Displays in the Device. Subsequent calls without
+ // first calling validate() are noop (except for duping/returning
+ // the retire fence).
+ HWC2::Error present(int32_t* outRetireFence);
+
+ HWC2::Error setActiveConfig(hwc2_config_t configId);
+ HWC2::Error setClientTarget(buffer_handle_t target,
+ int32_t acquireFence, int32_t dataspace,
+ hwc_region_t damage);
+ HWC2::Error setColorMode(android_color_mode_t mode);
+ HWC2::Error setColorTransform(android_color_transform_t hint);
+ HWC2::Error setOutputBuffer(buffer_handle_t buffer,
+ int32_t releaseFence);
+ HWC2::Error setPowerMode(HWC2::PowerMode mode);
+ HWC2::Error setVsyncEnabled(HWC2::Vsync enabled);
+
+ // Since HWC1 "validates" (called "prepare" in HWC1) all Displays
+ // at once, the first call to any Display::validate() will trigger
+ // validate() on all other Displays in the Device.
+ HWC2::Error validate(uint32_t* outNumTypes,
+ uint32_t* outNumRequests);
+
+ HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z);
+
+ HWC2::Error getClientTargetSupport(uint32_t width, uint32_t height,
+ int32_t format, int32_t dataspace);
+
+ // Read configs from HWC1 device
+ void populateConfigs();
+
+ // Set configs for a virtual display
+ void populateConfigs(uint32_t width, uint32_t height);
+
+ bool prepare();
+
+ // Called after hwc.prepare() with responses from the device.
+ void generateChanges();
+
+ bool hasChanges() const;
+ HWC2::Error set(hwc_display_contents_1& hwcContents);
+ void addRetireFence(int fenceFd);
+ void addReleaseFences(const hwc_display_contents_1& hwcContents);
+
+ bool hasColorTransform() const;
+
+ std::string dump() const;
+
+ // Return a rect from the pool allocated during validate()
+ hwc_rect_t* GetRects(size_t numRects);
+
+ hwc_display_contents_1* getDisplayContents();
+
+ void markGeometryChanged() { mGeometryChanged = true; }
+ void resetGeometryMarker() { mGeometryChanged = false;}
+ private:
+ class Config {
+ public:
+ Config(Display& display)
+ : mDisplay(display),
+ mId(0),
+ mAttributes() {}
+
+ bool isOnDisplay(const Display& display) const {
+ return display.getId() == mDisplay.getId();
+ }
+
+ void setAttribute(HWC2::Attribute attribute, int32_t value);
+ int32_t getAttribute(HWC2::Attribute attribute) const;
+
+ void setHwc1Id(uint32_t id);
+ bool hasHwc1Id(uint32_t id) const;
+ HWC2::Error getColorModeForHwc1Id(uint32_t id,
+ android_color_mode_t *outMode) const;
+ HWC2::Error getHwc1IdForColorMode(android_color_mode_t mode,
+ uint32_t* outId) const;
+
+ void setId(hwc2_config_t id) { mId = id; }
+ hwc2_config_t getId() const { return mId; }
+
+ // Attempts to merge two configs that differ only in color
+ // mode. Returns whether the merge was successful
+ bool merge(const Config& other);
+
+ std::set<android_color_mode_t> getColorModes() const;
+
+ // splitLine divides the output into two lines suitable for
+ // dumpsys SurfaceFlinger
+ std::string toString(bool splitLine = false) const;
+
+ private:
+ Display& mDisplay;
+ hwc2_config_t mId;
+ std::unordered_map<HWC2::Attribute, int32_t> mAttributes;
+
+ // Maps from color transform to HWC1 config ID
+ std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids;
+ };
+
+ // Stores changes requested from the device upon calling prepare().
+ // Handles change request to:
+ // - Layer composition type.
+ // - Layer hints.
+ class Changes {
+ public:
+ uint32_t getNumTypes() const {
+ return static_cast<uint32_t>(mTypeChanges.size());
+ }
+
+ uint32_t getNumLayerRequests() const {
+ return static_cast<uint32_t>(mLayerRequests.size());
+ }
+
+ const std::unordered_map<hwc2_layer_t, HWC2::Composition>&
+ getTypeChanges() const {
+ return mTypeChanges;
+ }
+
+ const std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>&
+ getLayerRequests() const {
+ return mLayerRequests;
+ }
+
+ void addTypeChange(hwc2_layer_t layerId,
+ HWC2::Composition type) {
+ mTypeChanges.insert({layerId, type});
+ }
+
+ void clearTypeChanges() { mTypeChanges.clear(); }
+
+ void addLayerRequest(hwc2_layer_t layerId,
+ HWC2::LayerRequest request) {
+ mLayerRequests.insert({layerId, request});
+ }
+
+ private:
+ std::unordered_map<hwc2_layer_t, HWC2::Composition>
+ mTypeChanges;
+ std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
+ mLayerRequests;
+ };
+
+ std::shared_ptr<const Config>
+ getConfig(hwc2_config_t configId) const;
+
+ void populateColorModes();
+ void initializeActiveConfig();
+
+ // Creates a bi-directional mapping between index in HWC1
+ // prepare/set array and Layer object. Stores mapping in
+ // mHwc1LayerMap and also updates Layer's attribute mHwc1Id.
+ void assignHwc1LayerIds();
+
+ // Called after a response to prepare() has been received:
+ // Ingest composition type changes requested by the device.
+ void updateTypeChanges(const struct hwc_layer_1& hwc1Layer,
+ const Layer& layer);
+
+ // Called after a response to prepare() has been received:
+ // Ingest layer hint changes requested by the device.
+ void updateLayerRequests(const struct hwc_layer_1& hwc1Layer,
+ const Layer& layer);
+
+ // Set all fields in HWC1 comm array for layer containing the
+ // HWC_FRAMEBUFFER_TARGET (always the last layer).
+ void prepareFramebufferTarget();
+
+ // Display ID generator.
+ static std::atomic<hwc2_display_t> sNextId;
+ const hwc2_display_t mId;
+
+
+ HWC2On1Adapter& mDevice;
+
+ // The state of this display should only be modified from
+ // SurfaceFlinger's main loop, with the exception of when dump is
+ // called. To prevent a bad state from crashing us during a dump
+ // call, all public calls into Display must acquire this mutex.
+ //
+ // It is recursive because we don't want to deadlock in validate
+ // (or present) when we call HWC2On1Adapter::prepareAllDisplays
+ // (or setAllDisplays), which calls back into Display functions
+ // which require locking.
+ mutable std::recursive_mutex mStateMutex;
+
+ // Allocate RAM able to store all layers and rects used for
+ // communication with HWC1. Place allocated RAM in variable
+ // mHwc1RequestedContents.
+ void allocateRequestedContents();
+
+ // Array of structs exchanged between client and hwc1 device.
+ // Sent to device upon calling prepare().
+ std::unique_ptr<hwc_display_contents_1> mHwc1RequestedContents;
+ private:
+ DeferredFence mRetireFence;
+
+ // Will only be non-null after the Display has been validated and
+ // before it has been presented
+ std::unique_ptr<Changes> mChanges;
+
+ int32_t mHwc1Id;
+
+ std::vector<std::shared_ptr<Config>> mConfigs;
+ std::shared_ptr<const Config> mActiveConfig;
+ std::set<android_color_mode_t> mColorModes;
+ android_color_mode_t mActiveColorMode;
+ std::string mName;
+ HWC2::DisplayType mType;
+ HWC2::PowerMode mPowerMode;
+ HWC2::Vsync mVsyncEnabled;
+
+ // Used to populate HWC1 HWC_FRAMEBUFFER_TARGET layer
+ FencedBuffer mClientTarget;
+
+
+ FencedBuffer mOutputBuffer;
+
+ bool mHasColorTransform;
+
+ // All layers this Display is aware of.
+ std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers;
+
+ // Mapping between layer index in array of hwc_display_contents_1*
+ // passed to HWC1 during validate/set and Layer object.
+ std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap;
+
+ // All communication with HWC1 via prepare/set is done with one
+ // alloc. This pointer is pointing to a pool of hwc_rect_t.
+ size_t mNumAvailableRects;
+ hwc_rect_t* mNextAvailableRect;
+
+ // True if any of the Layers contained in this Display have been
+ // updated with anything other than a buffer since last call to
+ // Display::set()
+ bool mGeometryChanged;
+ };
+
+ // Utility template calling a Display object method directly based on the
+ // hwc2_display_t displayId parameter.
+ template <typename ...Args>
+ static int32_t callDisplayFunction(hwc2_device_t* device,
+ hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...),
+ Args... args) {
+ auto display = getAdapter(device)->getDisplay(displayId);
+ if (!display) {
+ return static_cast<int32_t>(HWC2::Error::BadDisplay);
+ }
+ auto error = ((*display).*member)(std::forward<Args>(args)...);
+ return static_cast<int32_t>(error);
+ }
+
+ template <typename MF, MF memFunc, typename ...Args>
+ static int32_t displayHook(hwc2_device_t* device, hwc2_display_t displayId,
+ Args... args) {
+ return HWC2On1Adapter::callDisplayFunction(device, displayId, memFunc,
+ std::forward<Args>(args)...);
+ }
+
+ static int32_t getDisplayAttributeHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_config_t config,
+ int32_t intAttribute, int32_t* outValue) {
+ auto attribute = static_cast<HWC2::Attribute>(intAttribute);
+ return callDisplayFunction(device, display, &Display::getAttribute,
+ config, attribute, outValue);
+ }
+
+ static int32_t setColorTransformHook(hwc2_device_t* device,
+ hwc2_display_t display, const float* /*matrix*/,
+ int32_t /*android_color_transform_t*/ intHint) {
+ // We intentionally throw away the matrix, because if the hint is
+ // anything other than IDENTITY, we have to fall back to client
+ // composition anyway
+ auto hint = static_cast<android_color_transform_t>(intHint);
+ return callDisplayFunction(device, display, &Display::setColorTransform,
+ hint);
+ }
+
+ static int32_t setColorModeHook(hwc2_device_t* device,
+ hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) {
+ auto mode = static_cast<android_color_mode_t>(intMode);
+ return callDisplayFunction(device, display, &Display::setColorMode,
+ mode);
+ }
+
+ static int32_t setPowerModeHook(hwc2_device_t* device,
+ hwc2_display_t display, int32_t intMode) {
+ auto mode = static_cast<HWC2::PowerMode>(intMode);
+ return callDisplayFunction(device, display, &Display::setPowerMode,
+ mode);
+ }
+
+ static int32_t setVsyncEnabledHook(hwc2_device_t* device,
+ hwc2_display_t display, int32_t intEnabled) {
+ auto enabled = static_cast<HWC2::Vsync>(intEnabled);
+ return callDisplayFunction(device, display, &Display::setVsyncEnabled,
+ enabled);
+ }
+
+ class Layer {
+ public:
+ explicit Layer(Display& display);
+
+ bool operator==(const Layer& other) { return mId == other.mId; }
+ bool operator!=(const Layer& other) { return !(*this == other); }
+
+ hwc2_layer_t getId() const { return mId; }
+ Display& getDisplay() const { return mDisplay; }
+
+ // HWC2 Layer functions
+ HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence);
+ HWC2::Error setCursorPosition(int32_t x, int32_t y);
+ HWC2::Error setSurfaceDamage(hwc_region_t damage);
+
+ // HWC2 Layer state functions
+ HWC2::Error setBlendMode(HWC2::BlendMode mode);
+ HWC2::Error setColor(hwc_color_t color);
+ HWC2::Error setCompositionType(HWC2::Composition type);
+ HWC2::Error setDataspace(android_dataspace_t dataspace);
+ HWC2::Error setDisplayFrame(hwc_rect_t frame);
+ HWC2::Error setPlaneAlpha(float alpha);
+ HWC2::Error setSidebandStream(const native_handle_t* stream);
+ HWC2::Error setSourceCrop(hwc_frect_t crop);
+ HWC2::Error setTransform(HWC2::Transform transform);
+ HWC2::Error setVisibleRegion(hwc_region_t visible);
+ HWC2::Error setZ(uint32_t z);
+
+ HWC2::Composition getCompositionType() const {
+ return mCompositionType;
+ }
+ uint32_t getZ() const { return mZ; }
+
+ void addReleaseFence(int fenceFd);
+ const sp<MiniFence>& getReleaseFence() const;
+
+ void setHwc1Id(size_t id) { mHwc1Id = id; }
+ size_t getHwc1Id() const { return mHwc1Id; }
+
+ // Write state to HWC1 communication struct.
+ void applyState(struct hwc_layer_1& hwc1Layer);
+
+ std::string dump() const;
+
+ std::size_t getNumVisibleRegions() { return mVisibleRegion.size(); }
+
+ std::size_t getNumSurfaceDamages() { return mSurfaceDamage.size(); }
+
+ // True if a layer cannot be properly rendered by the device due
+ // to usage of SolidColor (a.k.a BackgroundColor in HWC1).
+ bool hasUnsupportedBackgroundColor() {
+ return (mCompositionType == HWC2::Composition::SolidColor &&
+ !mDisplay.getDevice().supportsBackgroundColor());
+ }
+ private:
+ void applyCommonState(struct hwc_layer_1& hwc1Layer);
+ void applySolidColorState(struct hwc_layer_1& hwc1Layer);
+ void applySidebandState(struct hwc_layer_1& hwc1Layer);
+ void applyBufferState(struct hwc_layer_1& hwc1Layer);
+ void applyCompositionType(struct hwc_layer_1& hwc1Layer);
+
+ static std::atomic<hwc2_layer_t> sNextId;
+ const hwc2_layer_t mId;
+ Display& mDisplay;
+
+ FencedBuffer mBuffer;
+ std::vector<hwc_rect_t> mSurfaceDamage;
+
+ HWC2::BlendMode mBlendMode;
+ hwc_color_t mColor;
+ HWC2::Composition mCompositionType;
+ hwc_rect_t mDisplayFrame;
+ float mPlaneAlpha;
+ const native_handle_t* mSidebandStream;
+ hwc_frect_t mSourceCrop;
+ HWC2::Transform mTransform;
+ std::vector<hwc_rect_t> mVisibleRegion;
+
+ uint32_t mZ;
+
+ DeferredFence mReleaseFence;
+
+ size_t mHwc1Id;
+ bool mHasUnsupportedPlaneAlpha;
+ };
+
+ // Utility tempate calling a Layer object method based on ID parameters:
+ // hwc2_display_t displayId
+ // and
+ // hwc2_layer_t layerId
+ template <typename ...Args>
+ static int32_t callLayerFunction(hwc2_device_t* device,
+ hwc2_display_t displayId, hwc2_layer_t layerId,
+ HWC2::Error (Layer::*member)(Args...), Args... args) {
+ auto result = getAdapter(device)->getLayer(displayId, layerId);
+ auto error = std::get<HWC2::Error>(result);
+ if (error == HWC2::Error::None) {
+ auto layer = std::get<Layer*>(result);
+ error = ((*layer).*member)(std::forward<Args>(args)...);
+ }
+ return static_cast<int32_t>(error);
+ }
+
+ template <typename MF, MF memFunc, typename ...Args>
+ static int32_t layerHook(hwc2_device_t* device, hwc2_display_t displayId,
+ hwc2_layer_t layerId, Args... args) {
+ return HWC2On1Adapter::callLayerFunction(device, displayId, layerId,
+ memFunc, std::forward<Args>(args)...);
+ }
+
+ // Layer state functions
+
+ static int32_t setLayerBlendModeHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, int32_t intMode) {
+ auto mode = static_cast<HWC2::BlendMode>(intMode);
+ return callLayerFunction(device, display, layer,
+ &Layer::setBlendMode, mode);
+ }
+
+ static int32_t setLayerCompositionTypeHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, int32_t intType) {
+ auto type = static_cast<HWC2::Composition>(intType);
+ return callLayerFunction(device, display, layer,
+ &Layer::setCompositionType, type);
+ }
+
+ static int32_t setLayerDataspaceHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, int32_t intDataspace) {
+ auto dataspace = static_cast<android_dataspace_t>(intDataspace);
+ return callLayerFunction(device, display, layer, &Layer::setDataspace,
+ dataspace);
+ }
+
+ static int32_t setLayerTransformHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) {
+ auto transform = static_cast<HWC2::Transform>(intTransform);
+ return callLayerFunction(device, display, layer, &Layer::setTransform,
+ transform);
+ }
+
+ static int32_t setLayerZOrderHook(hwc2_device_t* device,
+ hwc2_display_t display, hwc2_layer_t layer, uint32_t z) {
+ return callDisplayFunction(device, display, &Display::updateLayerZ,
+ layer, z);
+ }
+
+ // Adapter internals
+
+ void populateCapabilities();
+ Display* getDisplay(hwc2_display_t id);
+ std::tuple<Layer*, HWC2::Error> getLayer(hwc2_display_t displayId,
+ hwc2_layer_t layerId);
+ void populatePrimary();
+
+ bool prepareAllDisplays();
+ std::vector<struct hwc_display_contents_1*> mHwc1Contents;
+ HWC2::Error setAllDisplays();
+
+ // Callbacks
+ void hwc1Invalidate();
+ void hwc1Vsync(int hwc1DisplayId, int64_t timestamp);
+ void hwc1Hotplug(int hwc1DisplayId, int connected);
+
+ // These are set in the constructor and before any asynchronous events are
+ // possible
+
+ struct hwc_composer_device_1* const mHwc1Device;
+ const uint8_t mHwc1MinorVersion;
+ bool mHwc1SupportsVirtualDisplays;
+ bool mHwc1SupportsBackgroundColor;
+
+ class Callbacks;
+ const std::unique_ptr<Callbacks> mHwc1Callbacks;
+
+ std::unordered_set<HWC2::Capability> mCapabilities;
+
+ // These are only accessed from the main SurfaceFlinger thread (not from
+ // callbacks or dump
+
+ std::map<hwc2_layer_t, std::shared_ptr<Layer>> mLayers;
+
+ // A HWC1 supports only one virtual display.
+ std::shared_ptr<Display> mHwc1VirtualDisplay;
+
+ // These are potentially accessed from multiple threads, and are protected
+ // by this mutex. This needs to be recursive, since the HWC1 implementation
+ // can call back into the invalidate callback on the same thread that is
+ // calling prepare.
+ std::recursive_timed_mutex mStateMutex;
+
+ struct CallbackInfo {
+ hwc2_callback_data_t data;
+ hwc2_function_pointer_t pointer;
+ };
+ std::unordered_map<HWC2::Callback, CallbackInfo> mCallbacks;
+ bool mHasPendingInvalidate;
+
+ // There is a small gap between the time the HWC1 module is started and
+ // when the callbacks for vsync and hotplugs are registered by the
+ // HWC2on1Adapter. To prevent losing events they are stored in these arrays
+ // and fed to the callback as soon as possible.
+ std::vector<std::pair<int, int64_t>> mPendingVsyncs;
+ std::vector<std::pair<int, int>> mPendingHotplugs;
+
+ // Mapping between HWC1 display id and Display objects.
+ std::map<hwc2_display_t, std::shared_ptr<Display>> mDisplays;
+
+ // Map HWC1 display type (HWC_DISPLAY_PRIMARY, HWC_DISPLAY_EXTERNAL,
+ // HWC_DISPLAY_VIRTUAL) to Display IDs generated by HWC2on1Adapter objects.
+ std::unordered_map<int, hwc2_display_t> mHwc1DisplayMap;
+};
+
+} // namespace android
+
+#endif
diff --git a/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h b/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h
new file mode 100644
index 0000000000..75de764d2c
--- /dev/null
+++ b/libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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 MINIFENCE_H
+#define MINIFENCE_H
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+/* MiniFence is a minimal re-implementation of Fence from libui. It exists to
+ * avoid linking the HWC2on1Adapter to libui and satisfy Treble requirements.
+ */
+class MiniFence : public LightRefBase<MiniFence> {
+public:
+ static const sp<MiniFence> NO_FENCE;
+
+ // Construct a new MiniFence object with an invalid file descriptor.
+ MiniFence();
+
+ // Construct a new MiniFence object to manage a given fence file descriptor.
+ // When the new MiniFence object is destructed the file descriptor will be
+ // closed.
+ explicit MiniFence(int fenceFd);
+
+ // Not copyable or movable.
+ MiniFence(const MiniFence& rhs) = delete;
+ MiniFence& operator=(const MiniFence& rhs) = delete;
+ MiniFence(MiniFence&& rhs) = delete;
+ MiniFence& operator=(MiniFence&& rhs) = delete;
+
+ // Return a duplicate of the fence file descriptor. The caller is
+ // responsible for closing the returned file descriptor. On error, -1 will
+ // be returned and errno will indicate the problem.
+ int dup() const;
+
+private:
+ // Only allow instantiation using ref counting.
+ friend class LightRefBase<MiniFence>;
+ ~MiniFence();
+
+ int mFenceFd;
+
+};
+}
+#endif //MINIFENCE_H
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 9485d5b0e6..92944191c7 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -17,7 +17,11 @@
cc_library {
name: "libinput",
host_supported: true,
-
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
srcs: [
"Input.cpp",
"InputDevice.cpp",
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index d755ed30ac..4287abeb7d 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,6 +21,7 @@
#include <ctype.h>
#include <input/InputDevice.h>
+#include <input/InputEventLabels.h>
namespace android {
@@ -87,17 +88,23 @@ String8 getInputDeviceConfigurationFilePathByName(
const String8& name, InputDeviceConfigurationFileType type) {
// Search system repository.
String8 path;
- path.setTo(getenv("ANDROID_ROOT"));
- path.append("/usr/");
- appendInputDeviceConfigurationFileRelativePath(path, name, type);
+
+ // Treblized input device config files will be located /odm/usr or /vendor/usr.
+ const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
+ for (size_t i = 0; i < size(rootsForPartition); i++) {
+ path.setTo(rootsForPartition[i]);
+ path.append("/usr/");
+ appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBE
- ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
+ ALOGD("Probing for system provided input device configuration file: path='%s'",
+ path.string());
#endif
- if (!access(path.string(), R_OK)) {
+ if (!access(path.string(), R_OK)) {
#if DEBUG_PROBE
- ALOGD("Found");
+ ALOGD("Found");
#endif
- return path;
+ return path;
+ }
}
// Search user repository.
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 9a01395da1..07f2289785 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -208,7 +208,6 @@ static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaSta
}
int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
- int32_t mask;
switch (keyCode) {
case AKEYCODE_ALT_LEFT:
return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
new file mode 100644
index 0000000000..3ef8b4aa05
--- /dev/null
+++ b/libs/math/Android.bp
@@ -0,0 +1,21 @@
+// Copyright (C) 2017 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_library_static {
+ name: "libmath",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+subdirs = ["tests"]
diff --git a/libs/math/MODULE_LICENSE_APACHE2 b/libs/math/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/libs/math/MODULE_LICENSE_APACHE2
diff --git a/libs/math/NOTICE b/libs/math/NOTICE
new file mode 100644
index 0000000000..c5b1efa7aa
--- /dev/null
+++ b/libs/math/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libs/math/include/math/TMatHelpers.h b/libs/math/include/math/TMatHelpers.h
new file mode 100644
index 0000000000..5cb725d10a
--- /dev/null
+++ b/libs/math/include/math/TMatHelpers.h
@@ -0,0 +1,644 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <exception>
+#include <iomanip>
+#include <stdexcept>
+
+#include <math/quat.h>
+#include <math/TVecHelpers.h>
+
+#include <utils/String8.h>
+
+#ifndef LIKELY
+#define LIKELY_DEFINED_LOCAL
+#ifdef __cplusplus
+# define LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
+#endif
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/mat*.h
+ */
+
+
+/*
+ * Matrix utilities
+ */
+
+namespace matrix {
+
+inline constexpr int transpose(int v) { return v; }
+inline constexpr float transpose(float v) { return v; }
+inline constexpr double transpose(double v) { return v; }
+
+inline constexpr int trace(int v) { return v; }
+inline constexpr float trace(float v) { return v; }
+inline constexpr double trace(double v) { return v; }
+
+/*
+ * Matrix inversion
+ */
+template<typename MATRIX>
+MATRIX PURE gaussJordanInverse(const MATRIX& src) {
+ typedef typename MATRIX::value_type T;
+ static constexpr unsigned int N = MATRIX::NUM_ROWS;
+ MATRIX tmp(src);
+ MATRIX inverted(1);
+
+ for (size_t i = 0; i < N; ++i) {
+ // look for largest element in i'th column
+ size_t swap = i;
+ T t = std::abs(tmp[i][i]);
+ for (size_t j = i + 1; j < N; ++j) {
+ const T t2 = std::abs(tmp[j][i]);
+ if (t2 > t) {
+ swap = j;
+ t = t2;
+ }
+ }
+
+ if (swap != i) {
+ // swap columns.
+ std::swap(tmp[i], tmp[swap]);
+ std::swap(inverted[i], inverted[swap]);
+ }
+
+ const T denom(tmp[i][i]);
+ for (size_t k = 0; k < N; ++k) {
+ tmp[i][k] /= denom;
+ inverted[i][k] /= denom;
+ }
+
+ // Factor out the lower triangle
+ for (size_t j = 0; j < N; ++j) {
+ if (j != i) {
+ const T d = tmp[j][i];
+ for (size_t k = 0; k < N; ++k) {
+ tmp[j][k] -= tmp[i][k] * d;
+ inverted[j][k] -= inverted[i][k] * d;
+ }
+ }
+ }
+ }
+
+ return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// 2x2 matrix inverse is easy.
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) {
+ typedef typename MATRIX::value_type T;
+
+ // Assuming the input matrix is:
+ // | a b |
+ // | c d |
+ //
+ // The analytic inverse is
+ // | d -b |
+ // | -c a | / (a d - b c)
+ //
+ // Importantly, our matrices are column-major!
+
+ MATRIX inverted(MATRIX::NO_INIT);
+
+ const T a = x[0][0];
+ const T c = x[0][1];
+ const T b = x[1][0];
+ const T d = x[1][1];
+
+ const T det((a * d) - (b * c));
+ inverted[0][0] = d / det;
+ inverted[0][1] = -c / det;
+ inverted[1][0] = -b / det;
+ inverted[1][1] = a / det;
+ return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// From the Wikipedia article on matrix inversion's section on fast 3x3
+// matrix inversion:
+// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) {
+ typedef typename MATRIX::value_type T;
+
+ // Assuming the input matrix is:
+ // | a b c |
+ // | d e f |
+ // | g h i |
+ //
+ // The analytic inverse is
+ // | A B C |^T
+ // | D E F |
+ // | G H I | / determinant
+ //
+ // Which is
+ // | A D G |
+ // | B E H |
+ // | C F I | / determinant
+ //
+ // Where:
+ // A = (ei - fh), B = (fg - di), C = (dh - eg)
+ // D = (ch - bi), E = (ai - cg), F = (bg - ah)
+ // G = (bf - ce), H = (cd - af), I = (ae - bd)
+ //
+ // and the determinant is a*A + b*B + c*C (The rule of Sarrus)
+ //
+ // Importantly, our matrices are column-major!
+
+ MATRIX inverted(MATRIX::NO_INIT);
+
+ const T a = x[0][0];
+ const T b = x[1][0];
+ const T c = x[2][0];
+ const T d = x[0][1];
+ const T e = x[1][1];
+ const T f = x[2][1];
+ const T g = x[0][2];
+ const T h = x[1][2];
+ const T i = x[2][2];
+
+ // Do the full analytic inverse
+ const T A = e * i - f * h;
+ const T B = f * g - d * i;
+ const T C = d * h - e * g;
+ inverted[0][0] = A; // A
+ inverted[0][1] = B; // B
+ inverted[0][2] = C; // C
+ inverted[1][0] = c * h - b * i; // D
+ inverted[1][1] = a * i - c * g; // E
+ inverted[1][2] = b * g - a * h; // F
+ inverted[2][0] = b * f - c * e; // G
+ inverted[2][1] = c * d - a * f; // H
+ inverted[2][2] = a * e - b * d; // I
+
+ const T det(a * A + b * B + c * C);
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ inverted[col][row] /= det;
+ }
+ }
+
+ return inverted;
+}
+
+/**
+ * Inversion function which switches on the matrix size.
+ * @warning This function assumes the matrix is invertible. The result is
+ * undefined if it is not. It is the responsibility of the caller to
+ * make sure the matrix is not singular.
+ */
+template <typename MATRIX>
+inline constexpr MATRIX PURE inverse(const MATRIX& matrix) {
+ static_assert(MATRIX::NUM_ROWS == MATRIX::NUM_COLS, "only square matrices can be inverted");
+ return (MATRIX::NUM_ROWS == 2) ? fastInverse2<MATRIX>(matrix) :
+ ((MATRIX::NUM_ROWS == 3) ? fastInverse3<MATRIX>(matrix) :
+ gaussJordanInverse<MATRIX>(matrix));
+}
+
+template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
+CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
+ // pre-requisite:
+ // lhs : D columns, R rows
+ // rhs : C columns, D rows
+ // res : C columns, R rows
+
+ static_assert(MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS,
+ "matrices can't be multiplied. invalid dimensions.");
+ static_assert(MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS,
+ "invalid dimension of matrix multiply result.");
+ static_assert(MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS,
+ "invalid dimension of matrix multiply result.");
+
+ MATRIX_R res(MATRIX_R::NO_INIT);
+ for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) {
+ res[col] = lhs * rhs[col];
+ }
+ return res;
+}
+
+// transpose. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE transpose(const MATRIX& m) {
+ // for now we only handle square matrix transpose
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices");
+ MATRIX result(MATRIX::NO_INIT);
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) {
+ result[col][row] = transpose(m[row][col]);
+ }
+ }
+ return result;
+}
+
+// trace. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) {
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices");
+ typename MATRIX::value_type result(0);
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ result += trace(m[col][col]);
+ }
+ return result;
+}
+
+// diag. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) {
+ static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices");
+ typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
+ for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+ result[col] = m[col][col];
+ }
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// This is taken from the Imath MatrixAlgo code, and is identical to Eigen.
+template <typename MATRIX>
+TQuaternion<typename MATRIX::value_type> extractQuat(const MATRIX& mat) {
+ typedef typename MATRIX::value_type T;
+
+ TQuaternion<T> quat(TQuaternion<T>::NO_INIT);
+
+ // Compute the trace to see if it is positive or not.
+ const T trace = mat[0][0] + mat[1][1] + mat[2][2];
+
+ // check the sign of the trace
+ if (LIKELY(trace > 0)) {
+ // trace is positive
+ T s = std::sqrt(trace + 1);
+ quat.w = T(0.5) * s;
+ s = T(0.5) / s;
+ quat.x = (mat[1][2] - mat[2][1]) * s;
+ quat.y = (mat[2][0] - mat[0][2]) * s;
+ quat.z = (mat[0][1] - mat[1][0]) * s;
+ } else {
+ // trace is negative
+
+ // Find the index of the greatest diagonal
+ size_t i = 0;
+ if (mat[1][1] > mat[0][0]) { i = 1; }
+ if (mat[2][2] > mat[i][i]) { i = 2; }
+
+ // Get the next indices: (n+1)%3
+ static constexpr size_t next_ijk[3] = { 1, 2, 0 };
+ size_t j = next_ijk[i];
+ size_t k = next_ijk[j];
+ T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1);
+ quat[i] = T(0.5) * s;
+ if (s != 0) {
+ s = T(0.5) / s;
+ }
+ quat.w = (mat[j][k] - mat[k][j]) * s;
+ quat[j] = (mat[i][j] + mat[j][i]) * s;
+ quat[k] = (mat[i][k] + mat[k][i]) * s;
+ }
+ return quat;
+}
+
+template <typename MATRIX>
+String8 asString(const MATRIX& m) {
+ String8 s;
+ for (size_t c = 0; c < MATRIX::col_size(); c++) {
+ s.append("| ");
+ for (size_t r = 0; r < MATRIX::row_size(); r++) {
+ s.appendFormat("%7.2f ", m[r][c]);
+ }
+ s.append("|\n");
+ }
+ return s;
+}
+
+} // namespace matrix
+
+// -------------------------------------------------------------------------------------
+
+/*
+ * TMatProductOperators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class BASE, typename T>
+class TMatProductOperators {
+public:
+ // multiply by a scalar
+ BASE<T>& operator *= (T v) {
+ BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ lhs[col] *= v;
+ }
+ return lhs;
+ }
+
+ // matrix *= matrix
+ template<typename U>
+ const BASE<T>& operator *= (const BASE<U>& rhs) {
+ BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+ lhs = matrix::multiply<BASE<T> >(lhs, rhs);
+ return lhs;
+ }
+
+ // divide by a scalar
+ BASE<T>& operator /= (T v) {
+ BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ lhs[col] /= v;
+ }
+ return lhs;
+ }
+
+ // matrix * matrix, result is a matrix of the same type than the lhs matrix
+ template<typename U>
+ friend CONSTEXPR BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
+ return matrix::multiply<BASE<T> >(lhs, rhs);
+ }
+};
+
+/*
+ * TMatSquareFunctions implements functions on a matrix of type BASE<T>.
+ *
+ * BASE only needs to implement:
+ * - operator[]
+ * - col_type
+ * - row_type
+ * - COL_SIZE
+ * - ROW_SIZE
+ *
+ * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template<template<typename U> class BASE, typename T>
+class TMatSquareFunctions {
+public:
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ friend inline CONSTEXPR BASE<T> PURE inverse(const BASE<T>& matrix) {
+ return matrix::inverse(matrix);
+ }
+ friend inline constexpr BASE<T> PURE transpose(const BASE<T>& m) {
+ return matrix::transpose(m);
+ }
+ friend inline constexpr T PURE trace(const BASE<T>& m) {
+ return matrix::trace(m);
+ }
+};
+
+template<template<typename U> class BASE, typename T>
+class TMatHelpers {
+public:
+ constexpr inline size_t getColumnSize() const { return BASE<T>::COL_SIZE; }
+ constexpr inline size_t getRowSize() const { return BASE<T>::ROW_SIZE; }
+ constexpr inline size_t getColumnCount() const { return BASE<T>::NUM_COLS; }
+ constexpr inline size_t getRowCount() const { return BASE<T>::NUM_ROWS; }
+ constexpr inline size_t size() const { return BASE<T>::ROW_SIZE; } // for TVec*<>
+
+ // array access
+ constexpr T const* asArray() const {
+ return &static_cast<BASE<T> const &>(*this)[0][0];
+ }
+
+ // element access
+ inline constexpr T const& operator()(size_t row, size_t col) const {
+ return static_cast<BASE<T> const &>(*this)[col][row];
+ }
+
+ inline T& operator()(size_t row, size_t col) {
+ return static_cast<BASE<T>&>(*this)[col][row];
+ }
+
+ template <typename VEC>
+ static CONSTEXPR BASE<T> translate(const VEC& t) {
+ BASE<T> r;
+ r[BASE<T>::NUM_COLS-1] = t;
+ return r;
+ }
+
+ template <typename VEC>
+ static constexpr BASE<T> scale(const VEC& s) {
+ return BASE<T>(s);
+ }
+
+ friend inline CONSTEXPR BASE<T> PURE abs(BASE<T> m) {
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ m[col] = abs(m[col]);
+ }
+ return m;
+ }
+};
+
+// functions for 3x3 and 4x4 matrices
+template<template<typename U> class BASE, typename T>
+class TMatTransform {
+public:
+ inline constexpr TMatTransform() {
+ static_assert(BASE<T>::NUM_ROWS == 3 || BASE<T>::NUM_ROWS == 4, "3x3 or 4x4 matrices only");
+ }
+
+ template <typename A, typename VEC>
+ static CONSTEXPR BASE<T> rotate(A radian, const VEC& about) {
+ BASE<T> r;
+ T c = std::cos(radian);
+ T s = std::sin(radian);
+ if (about.x == 1 && about.y == 0 && about.z == 0) {
+ r[1][1] = c; r[2][2] = c;
+ r[1][2] = s; r[2][1] = -s;
+ } else if (about.x == 0 && about.y == 1 && about.z == 0) {
+ r[0][0] = c; r[2][2] = c;
+ r[2][0] = s; r[0][2] = -s;
+ } else if (about.x == 0 && about.y == 0 && about.z == 1) {
+ r[0][0] = c; r[1][1] = c;
+ r[0][1] = s; r[1][0] = -s;
+ } else {
+ VEC nabout = normalize(about);
+ typename VEC::value_type x = nabout.x;
+ typename VEC::value_type y = nabout.y;
+ typename VEC::value_type z = nabout.z;
+ T nc = 1 - c;
+ T xy = x * y;
+ T yz = y * z;
+ T zx = z * x;
+ T xs = x * s;
+ T ys = y * s;
+ T zs = z * s;
+ r[0][0] = x*x*nc + c; r[1][0] = xy*nc - zs; r[2][0] = zx*nc + ys;
+ r[0][1] = xy*nc + zs; r[1][1] = y*y*nc + c; r[2][1] = yz*nc - xs;
+ r[0][2] = zx*nc - ys; r[1][2] = yz*nc + xs; r[2][2] = z*z*nc + c;
+
+ // Clamp results to -1, 1.
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+ }
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Create a matrix from euler angles using YPR around YXZ respectively
+ * @param yaw about Y axis
+ * @param pitch about X axis
+ * @param roll about Z axis
+ */
+ template <
+ typename Y, typename P, typename R,
+ typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+ >
+ static CONSTEXPR BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
+ return eulerZYX(roll, pitch, yaw);
+ }
+
+ /**
+ * Create a matrix from euler angles using YPR around ZYX respectively
+ * @param roll about X axis
+ * @param pitch about Y axis
+ * @param yaw about Z axis
+ *
+ * The euler angles are applied in ZYX order. i.e: a vector is first rotated
+ * about X (roll) then Y (pitch) and then Z (yaw).
+ */
+ template <
+ typename Y, typename P, typename R,
+ typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+ typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+ >
+ static CONSTEXPR BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
+ BASE<T> r;
+ T cy = std::cos(yaw);
+ T sy = std::sin(yaw);
+ T cp = std::cos(pitch);
+ T sp = std::sin(pitch);
+ T cr = std::cos(roll);
+ T sr = std::sin(roll);
+ T cc = cr * cy;
+ T cs = cr * sy;
+ T sc = sr * cy;
+ T ss = sr * sy;
+ r[0][0] = cp * cy;
+ r[0][1] = cp * sy;
+ r[0][2] = -sp;
+ r[1][0] = sp * sc - cs;
+ r[1][1] = sp * ss + cc;
+ r[1][2] = cp * sr;
+ r[2][0] = sp * cc + ss;
+ r[2][1] = sp * cs - sc;
+ r[2][2] = cp * cr;
+
+ // Clamp results to -1, 1.
+ for (size_t col = 0; col < 3; ++col) {
+ for (size_t row = 0; row < 3; ++row) {
+ r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+ }
+ }
+ return r;
+ }
+
+ TQuaternion<T> toQuaternion() const {
+ return matrix::extractQuat(static_cast<const BASE<T>&>(*this));
+ }
+};
+
+
+template <template<typename T> class BASE, typename T>
+class TMatDebug {
+public:
+ friend std::ostream& operator<<(std::ostream& stream, const BASE<T>& m) {
+ for (size_t row = 0; row < BASE<T>::NUM_ROWS; ++row) {
+ if (row != 0) {
+ stream << std::endl;
+ }
+ if (row == 0) {
+ stream << "/ ";
+ } else if (row == BASE<T>::NUM_ROWS-1) {
+ stream << "\\ ";
+ } else {
+ stream << "| ";
+ }
+ for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+ stream << std::setw(10) << std::to_string(m[col][row]);
+ }
+ if (row == 0) {
+ stream << " \\";
+ } else if (row == BASE<T>::NUM_ROWS-1) {
+ stream << " /";
+ } else {
+ stream << " |";
+ }
+ }
+ return stream;
+ }
+
+ String8 asString() const {
+ return matrix::asString(static_cast<const BASE<T>&>(*this));
+ }
+};
+
+// -------------------------------------------------------------------------------------
+} // namespace details
+} // namespace android
+
+#ifdef LIKELY_DEFINED_LOCAL
+#undef LIKELY_DEFINED_LOCAL
+#undef LIKELY
+#undef UNLIKELY
+#endif //LIKELY_DEFINED_LOCAL
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/TQuatHelpers.h b/libs/math/include/math/TQuatHelpers.h
new file mode 100644
index 0000000000..f0a71aebcd
--- /dev/null
+++ b/libs/math/include/math/TQuatHelpers.h
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <iostream>
+
+#include <math/vec3.h>
+
+#define PURE __attribute__((pure))
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/quat.h
+ */
+
+
+/*
+ * TQuatProductOperators implements basic arithmetic and basic compound assignment
+ * operators on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class QUATERNION, typename T>
+class TQuatProductOperators {
+public:
+ /* compound assignment from a another quaternion of the same size but different
+ * element type.
+ */
+ template <typename OTHER>
+ QUATERNION<T>& operator *= (const QUATERNION<OTHER>& r) {
+ QUATERNION<T>& q = static_cast<QUATERNION<T>&>(*this);
+ q = q * r;
+ return q;
+ }
+
+ /* compound assignment products by a scalar
+ */
+ QUATERNION<T>& operator *= (T v) {
+ QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+ for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+ lhs[i] *= v;
+ }
+ return lhs;
+ }
+ QUATERNION<T>& operator /= (T v) {
+ QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+ for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+ lhs[i] /= v;
+ }
+ return lhs;
+ }
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ /* The operators below handle operation between quaternion of the same size
+ * but of a different element type.
+ */
+ template<typename RT>
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(const QUATERNION<T>& q, const QUATERNION<RT>& r) {
+ // could be written as:
+ // return QUATERNION<T>(
+ // q.w*r.w - dot(q.xyz, r.xyz),
+ // q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz));
+
+ return QUATERNION<T>(
+ q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+ q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+ q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+ q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+ }
+
+ template<typename RT>
+ friend inline
+ constexpr TVec3<T> PURE operator *(const QUATERNION<T>& q, const TVec3<RT>& v) {
+ // note: if q is known to be a unit quaternion, then this simplifies to:
+ // TVec3<T> t = 2 * cross(q.xyz, v)
+ // return v + (q.w * t) + cross(q.xyz, t)
+ return imaginary(q * QUATERNION<T>(v, 0) * inverse(q));
+ }
+
+
+ /* For quaternions, we use explicit "by a scalar" products because it's much faster
+ * than going (implicitly) through the quaternion multiplication.
+ * For reference: we could use the code below instead, but it would be a lot slower.
+ * friend inline
+ * constexpr BASE<T> PURE operator *(const BASE<T>& q, const BASE<T>& r) {
+ * return BASE<T>(
+ * q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+ * q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+ * q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+ * q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+ *
+ */
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(QUATERNION<T> q, T scalar) {
+ // don't pass q by reference because we need a copy anyways
+ return q *= scalar;
+ }
+ friend inline
+ constexpr QUATERNION<T> PURE operator *(T scalar, QUATERNION<T> q) {
+ // don't pass q by reference because we need a copy anyways
+ return q *= scalar;
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE operator /(QUATERNION<T> q, T scalar) {
+ // don't pass q by reference because we need a copy anyways
+ return q /= scalar;
+ }
+};
+
+
+/*
+ * TQuatFunctions implements functions on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatFunctions {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ template<typename RT>
+ friend inline
+ constexpr T PURE dot(const QUATERNION<T>& p, const QUATERNION<RT>& q) {
+ return p.x * q.x +
+ p.y * q.y +
+ p.z * q.z +
+ p.w * q.w;
+ }
+
+ friend inline
+ constexpr T PURE norm(const QUATERNION<T>& q) {
+ return std::sqrt( dot(q, q) );
+ }
+
+ friend inline
+ constexpr T PURE length(const QUATERNION<T>& q) {
+ return norm(q);
+ }
+
+ friend inline
+ constexpr T PURE length2(const QUATERNION<T>& q) {
+ return dot(q, q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE normalize(const QUATERNION<T>& q) {
+ return length(q) ? q / length(q) : QUATERNION<T>(1);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE conj(const QUATERNION<T>& q) {
+ return QUATERNION<T>(q.w, -q.x, -q.y, -q.z);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE inverse(const QUATERNION<T>& q) {
+ return conj(q) * (1 / dot(q, q));
+ }
+
+ friend inline
+ constexpr T PURE real(const QUATERNION<T>& q) {
+ return q.w;
+ }
+
+ friend inline
+ constexpr TVec3<T> PURE imaginary(const QUATERNION<T>& q) {
+ return q.xyz;
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE unreal(const QUATERNION<T>& q) {
+ return QUATERNION<T>(q.xyz, 0);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE cross(const QUATERNION<T>& p, const QUATERNION<T>& q) {
+ return unreal(p*q);
+ }
+
+ friend inline
+ QUATERNION<T> PURE exp(const QUATERNION<T>& q) {
+ const T nq(norm(q.xyz));
+ return std::exp(q.w)*QUATERNION<T>((sin(nq)/nq)*q.xyz, cos(nq));
+ }
+
+ friend inline
+ QUATERNION<T> PURE log(const QUATERNION<T>& q) {
+ const T nq(norm(q));
+ return QUATERNION<T>((std::acos(q.w/nq)/norm(q.xyz))*q.xyz, log(nq));
+ }
+
+ friend inline
+ QUATERNION<T> PURE pow(const QUATERNION<T>& q, T a) {
+ // could also be computed as: exp(a*log(q));
+ const T nq(norm(q));
+ const T theta(a*std::acos(q.w / nq));
+ return std::pow(nq, a) * QUATERNION<T>(normalize(q.xyz) * std::sin(theta), std::cos(theta));
+ }
+
+ friend inline
+ QUATERNION<T> PURE slerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ // could also be computed as: pow(q * inverse(p), t) * p;
+ const T d = dot(p, q);
+ const T npq = sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q||
+ const T a = std::acos(std::abs(d) / npq);
+ const T a0 = a * (1 - t);
+ const T a1 = a * t;
+ const T isina = 1 / sin(a);
+ const T s0 = std::sin(a0) * isina;
+ const T s1 = std::sin(a1) * isina;
+ // ensure we're taking the "short" side
+ return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE lerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ return ((1 - t) * p) + (t * q);
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE nlerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+ return normalize(lerp(p, q, t));
+ }
+
+ friend inline
+ constexpr QUATERNION<T> PURE positive(const QUATERNION<T>& q) {
+ return q.w < 0 ? -q : q;
+ }
+};
+
+/*
+ * TQuatDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatDebug {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ friend std::ostream& operator<< (std::ostream& stream, const QUATERNION<T>& q) {
+ return stream << "< " << q.w << " + " << q.x << "i + " << q.y << "j + " << q.z << "k >";
+ }
+};
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+} // namespace details
+} // namespace android
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
new file mode 100644
index 0000000000..20f852fd9f
--- /dev/null
+++ b/libs/math/include/math/TVecHelpers.h
@@ -0,0 +1,608 @@
+/*
+ * 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.
+ */
+
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <limits>
+#include <iostream>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/vec{2|3|4}.h
+ */
+
+/*
+ * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class VECTOR, typename T>
+class TVecAddOperators {
+public:
+ /* compound assignment from a another vector of the same size but different
+ * element type.
+ */
+ template<typename OTHER>
+ VECTOR<T>& operator +=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] += v[i];
+ }
+ return lhs;
+ }
+ template<typename OTHER>
+ VECTOR<T>& operator -=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] -= v[i];
+ }
+ return lhs;
+ }
+
+ /* compound assignment from a another vector of the same type.
+ * These operators can be used for implicit conversion and handle operations
+ * like "vector *= scalar" by letting the compiler implicitly convert a scalar
+ * to a vector (assuming the BASE<T> allows it).
+ */
+ VECTOR<T>& operator +=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] += v[i];
+ }
+ return lhs;
+ }
+ VECTOR<T>& operator -=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] -= v[i];
+ }
+ return lhs;
+ }
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ /* The operators below handle operation between vectors of the same size
+ * but of a different element type.
+ */
+ template<typename RT>
+ friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv += rv;
+ }
+ template<typename RT>
+ friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv -= rv;
+ }
+
+ /* The operators below (which are not templates once this class is instanced,
+ * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
+ * These handle operations like "vector + scalar" and "scalar + vector" by
+ * letting the compiler implicitly convert a scalar to a vector (assuming
+ * the BASE<T> allows it).
+ */
+ friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv += rv;
+ }
+ friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv -= rv;
+ }
+};
+
+template<template<typename T> class VECTOR, typename T>
+class TVecProductOperators {
+public:
+ /* compound assignment from a another vector of the same size but different
+ * element type.
+ */
+ template<typename OTHER>
+ VECTOR<T>& operator *=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] *= v[i];
+ }
+ return lhs;
+ }
+ template<typename OTHER>
+ VECTOR<T>& operator /=(const VECTOR<OTHER>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] /= v[i];
+ }
+ return lhs;
+ }
+
+ /* compound assignment from a another vector of the same type.
+ * These operators can be used for implicit conversion and handle operations
+ * like "vector *= scalar" by letting the compiler implicitly convert a scalar
+ * to a vector (assuming the BASE<T> allows it).
+ */
+ VECTOR<T>& operator *=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] *= v[i];
+ }
+ return lhs;
+ }
+ VECTOR<T>& operator /=(const VECTOR<T>& v) {
+ VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < lhs.size(); i++) {
+ lhs[i] /= v[i];
+ }
+ return lhs;
+ }
+
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+
+ /* The operators below handle operation between vectors of the same size
+ * but of a different element type.
+ */
+ template<typename RT>
+ friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv *= rv;
+ }
+ template<typename RT>
+ friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<RT>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv /= rv;
+ }
+
+ /* The operators below (which are not templates once this class is instanced,
+ * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
+ * These handle operations like "vector * scalar" and "scalar * vector" by
+ * letting the compiler implicitly convert a scalar to a vector (assuming
+ * the BASE<T> allows it).
+ */
+ friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv *= rv;
+ }
+ friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<T>& rv) {
+ // don't pass lv by reference because we need a copy anyways
+ return lv /= rv;
+ }
+};
+
+/*
+ * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ *
+ * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecUnaryOperators {
+public:
+ VECTOR<T>& operator ++() {
+ VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < rhs.size(); i++) {
+ ++rhs[i];
+ }
+ return rhs;
+ }
+
+ VECTOR<T>& operator --() {
+ VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+ for (size_t i = 0; i < rhs.size(); i++) {
+ --rhs[i];
+ }
+ return rhs;
+ }
+
+ CONSTEXPR VECTOR<T> operator -() const {
+ VECTOR<T> r(VECTOR<T>::NO_INIT);
+ VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+ for (size_t i = 0; i < r.size(); i++) {
+ r[i] = -rv[i];
+ }
+ return r;
+ }
+};
+
+/*
+ * TVecComparisonOperators implements relational/comparison operators
+ * on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecComparisonOperators {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ template<typename RT>
+ friend inline
+ bool PURE operator ==(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++)
+ if (lv[i] != rv[i])
+ return false;
+ return true;
+ }
+
+ template<typename RT>
+ friend inline
+ bool PURE operator !=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return !operator ==(lv, rv);
+ }
+
+ template<typename RT>
+ friend inline
+ bool PURE operator >(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++) {
+ if (lv[i] == rv[i]) {
+ continue;
+ }
+ return lv[i] > rv[i];
+ }
+ return false;
+ }
+
+ template<typename RT>
+ friend inline
+ constexpr bool PURE operator <=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return !(lv > rv);
+ }
+
+ template<typename RT>
+ friend inline
+ bool PURE operator <(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ for (size_t i = 0; i < lv.size(); i++) {
+ if (lv[i] == rv[i]) {
+ continue;
+ }
+ return lv[i] < rv[i];
+ }
+ return false;
+ }
+
+ template<typename RT>
+ friend inline
+ constexpr bool PURE operator >=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return !(lv < rv);
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] == rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] != rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] < rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] <= rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] > rv[i];
+ }
+ return r;
+ }
+
+ template<typename RT>
+ friend inline
+ CONSTEXPR VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ VECTOR<bool> r;
+ for (size_t i = 0; i < lv.size(); i++) {
+ r[i] = lv[i] >= rv[i];
+ }
+ return r;
+ }
+};
+
+/*
+ * TVecFunctions implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecFunctions {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ template<typename RT>
+ friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ T r(0);
+ for (size_t i = 0; i < lv.size(); i++) {
+ //r = std::fma(lv[i], rv[i], r);
+ r += lv[i] * rv[i];
+ }
+ return r;
+ }
+
+ friend inline constexpr T PURE norm(const VECTOR<T>& lv) {
+ return std::sqrt(dot(lv, lv));
+ }
+
+ friend inline constexpr T PURE length(const VECTOR<T>& lv) {
+ return norm(lv);
+ }
+
+ friend inline constexpr T PURE norm2(const VECTOR<T>& lv) {
+ return dot(lv, lv);
+ }
+
+ friend inline constexpr T PURE length2(const VECTOR<T>& lv) {
+ return norm2(lv);
+ }
+
+ template<typename RT>
+ friend inline constexpr T PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return length(rv - lv);
+ }
+
+ template<typename RT>
+ friend inline constexpr T PURE distance2(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+ return length2(rv - lv);
+ }
+
+ friend inline constexpr VECTOR<T> PURE normalize(const VECTOR<T>& lv) {
+ return lv * (T(1) / length(lv));
+ }
+
+ friend inline constexpr VECTOR<T> PURE rcp(VECTOR<T> v) {
+ return T(1) / v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE abs(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::abs(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE floor(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::floor(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE ceil(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::ceil(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE round(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::round(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE inversesqrt(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = T(1) / std::sqrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE sqrt(VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::sqrt(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE pow(VECTOR<T> v, T p) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::pow(v[i], p);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE saturate(const VECTOR<T>& lv) {
+ return clamp(lv, T(0), T(1));
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) {
+ for (size_t i = 0; i< v.size(); i++) {
+ v[i] = std::min(max, std::max(min, v[i]));
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) {
+ for (size_t i = 0; i<lv.size(); i++) {
+ //a[i] = std::fma(lv[i], rv[i], a[i]);
+ a[i] += (lv[i] * rv[i]);
+ }
+ return a;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::min(u[i], v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = std::max(u[i], v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR T PURE max(const VECTOR<T>& v) {
+ T r(std::numeric_limits<T>::lowest());
+ for (size_t i = 0; i < v.size(); i++) {
+ r = std::max(r, v[i]);
+ }
+ return r;
+ }
+
+ friend inline CONSTEXPR T PURE min(const VECTOR<T>& v) {
+ T r(std::numeric_limits<T>::max());
+ for (size_t i = 0; i < v.size(); i++) {
+ r = std::min(r, v[i]);
+ }
+ return r;
+ }
+
+ friend inline CONSTEXPR VECTOR<T> PURE apply(VECTOR<T> v, const std::function<T(T)>& f) {
+ for (size_t i = 0; i < v.size(); i++) {
+ v[i] = f(v[i]);
+ }
+ return v;
+ }
+
+ friend inline CONSTEXPR bool PURE any(const VECTOR<T>& v) {
+ for (size_t i = 0; i < v.size(); i++) {
+ if (v[i] != T(0)) return true;
+ }
+ return false;
+ }
+
+ friend inline CONSTEXPR bool PURE all(const VECTOR<T>& v) {
+ bool result = true;
+ for (size_t i = 0; i < v.size(); i++) {
+ result &= (v[i] != T(0));
+ }
+ return result;
+ }
+
+ template<typename R>
+ friend inline CONSTEXPR VECTOR<R> PURE map(VECTOR<T> v, const std::function<R(T)>& f) {
+ VECTOR<R> result;
+ for (size_t i = 0; i < v.size(); i++) {
+ result[i] = f(v[i]);
+ }
+ return result;
+ }
+};
+
+/*
+ * TVecDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecDebug {
+public:
+ /*
+ * NOTE: the functions below ARE NOT member methods. They are friend functions
+ * with they definition inlined with their declaration. This makes these
+ * template functions available to the compiler when (and only when) this class
+ * is instantiated, at which point they're only templated on the 2nd parameter
+ * (the first one, BASE<T> being known).
+ */
+ friend std::ostream& operator<<(std::ostream& stream, const VECTOR<T>& v) {
+ stream << "< ";
+ for (size_t i = 0; i < v.size() - 1; i++) {
+ stream << T(v[i]) << ", ";
+ }
+ stream << T(v[v.size() - 1]) << " >";
+ return stream;
+ }
+};
+
+#undef CONSTEXPR
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+} // namespace details
+} // namespace android
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
new file mode 100644
index 0000000000..76829734a4
--- /dev/null
+++ b/libs/math/include/math/half.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+#include <iosfwd>
+#include <limits>
+#include <type_traits>
+
+#ifndef LIKELY
+#define LIKELY_DEFINED_LOCAL
+#ifdef __cplusplus
+# define LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
+#endif
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+
+/*
+ * half-float
+ *
+ * 1 5 10
+ * +-+------+------------+
+ * |s|eee.ee|mm.mmmm.mmmm|
+ * +-+------+------------+
+ *
+ * minimum (denormal) value: 2^-24 = 5.96e-8
+ * minimum (normal) value: 2^-14 = 6.10e-5
+ * maximum value: 2-2^-10 = 65504
+ *
+ * Integers between 0 and 2048 can be represented exactly
+ */
+class half {
+ struct fp16 {
+ uint16_t bits;
+ explicit constexpr fp16() noexcept : bits(0) { }
+ explicit constexpr fp16(uint16_t b) noexcept : bits(b) { }
+ void setS(unsigned int s) noexcept { bits = uint16_t((bits & 0x7FFF) | (s<<15)); }
+ void setE(unsigned int s) noexcept { bits = uint16_t((bits & 0xE3FF) | (s<<10)); }
+ void setM(unsigned int s) noexcept { bits = uint16_t((bits & 0xFC00) | (s<< 0)); }
+ constexpr unsigned int getS() const noexcept { return bits >> 15u; }
+ constexpr unsigned int getE() const noexcept { return (bits >> 10u) & 0x1Fu; }
+ constexpr unsigned int getM() const noexcept { return bits & 0x3FFu; }
+ };
+ struct fp32 {
+ union {
+ uint32_t bits;
+ float fp;
+ };
+ explicit constexpr fp32() noexcept : bits(0) { }
+ explicit constexpr fp32(float f) noexcept : fp(f) { }
+ void setS(unsigned int s) noexcept { bits = uint32_t((bits & 0x7FFFFFFF) | (s<<31)); }
+ void setE(unsigned int s) noexcept { bits = uint32_t((bits & 0x807FFFFF) | (s<<23)); }
+ void setM(unsigned int s) noexcept { bits = uint32_t((bits & 0xFF800000) | (s<< 0)); }
+ constexpr unsigned int getS() const noexcept { return bits >> 31u; }
+ constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; }
+ constexpr unsigned int getM() const noexcept { return bits & 0x7FFFFFu; }
+ };
+
+public:
+ CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
+ CONSTEXPR operator float() const noexcept { return htof(mBits); }
+
+ uint16_t getBits() const noexcept { return mBits.bits; }
+ unsigned int getExponent() const noexcept { return mBits.getE(); }
+ unsigned int getMantissa() const noexcept { return mBits.getM(); }
+
+private:
+ friend class std::numeric_limits<half>;
+ friend CONSTEXPR half operator"" _hf(long double v);
+
+ enum Binary { binary };
+ explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { }
+ static CONSTEXPR fp16 ftoh(float v) noexcept;
+ static CONSTEXPR float htof(fp16 v) noexcept;
+ fp16 mBits;
+};
+
+inline CONSTEXPR half::fp16 half::ftoh(float v) noexcept {
+ fp16 out;
+ fp32 in(v);
+ if (UNLIKELY(in.getE() == 0xFF)) { // inf or nan
+ out.setE(0x1F);
+ out.setM(in.getM() ? 0x200 : 0);
+ } else {
+ int e = static_cast<int>(in.getE()) - 127 + 15;
+ if (e >= 0x1F) {
+ // overflow
+ out.setE(0x31); // +/- inf
+ } else if (e <= 0) {
+ // underflow
+ // flush to +/- 0
+ } else {
+ unsigned int m = in.getM();
+ out.setE(uint16_t(e));
+ out.setM(m >> 13);
+ if (m & 0x1000) {
+ // rounding
+ out.bits++;
+ }
+ }
+ }
+ out.setS(in.getS());
+ return out;
+}
+
+inline CONSTEXPR float half::htof(half::fp16 in) noexcept {
+ fp32 out;
+ if (UNLIKELY(in.getE() == 0x1F)) { // inf or nan
+ out.setE(0xFF);
+ out.setM(in.getM() ? 0x400000 : 0);
+ } else {
+ if (in.getE() == 0) {
+ if (in.getM()) {
+ // TODO: denormal half float, treat as zero for now
+ // (it's stupid because they can be represented as regular float)
+ }
+ } else {
+ int e = static_cast<int>(in.getE()) - 15 + 127;
+ unsigned int m = in.getM();
+ out.setE(uint32_t(e));
+ out.setM(m << 13);
+ }
+ }
+ out.setS(in.getS());
+ return out.fp;
+}
+
+inline CONSTEXPR android::half operator"" _hf(long double v) {
+ return android::half(android::half::binary, android::half::ftoh(static_cast<float>(v)).bits);
+}
+
+} // namespace android
+
+namespace std {
+
+template<> struct is_floating_point<android::half> : public std::true_type {};
+
+template<>
+class numeric_limits<android::half> {
+public:
+ typedef android::half type;
+
+ static constexpr const bool is_specialized = true;
+ static constexpr const bool is_signed = true;
+ static constexpr const bool is_integer = false;
+ static constexpr const bool is_exact = false;
+ static constexpr const bool has_infinity = true;
+ static constexpr const bool has_quiet_NaN = true;
+ static constexpr const bool has_signaling_NaN = false;
+ static constexpr const float_denorm_style has_denorm = denorm_absent;
+ static constexpr const bool has_denorm_loss = true;
+ static constexpr const bool is_iec559 = false;
+ static constexpr const bool is_bounded = true;
+ static constexpr const bool is_modulo = false;
+ static constexpr const bool traps = false;
+ static constexpr const bool tinyness_before = false;
+ static constexpr const float_round_style round_style = round_indeterminate;
+
+ static constexpr const int digits = 11;
+ static constexpr const int digits10 = 3;
+ static constexpr const int max_digits10 = 5;
+ static constexpr const int radix = 2;
+ static constexpr const int min_exponent = -13;
+ static constexpr const int min_exponent10 = -4;
+ static constexpr const int max_exponent = 16;
+ static constexpr const int max_exponent10 = 4;
+
+ inline static constexpr type round_error() noexcept { return android::half(android::half::binary, 0x3800); }
+ inline static constexpr type min() noexcept { return android::half(android::half::binary, 0x0400); }
+ inline static constexpr type max() noexcept { return android::half(android::half::binary, 0x7bff); }
+ inline static constexpr type lowest() noexcept { return android::half(android::half::binary, 0xfbff); }
+ inline static constexpr type epsilon() noexcept { return android::half(android::half::binary, 0x1400); }
+ inline static constexpr type infinity() noexcept { return android::half(android::half::binary, 0x7c00); }
+ inline static constexpr type quiet_NaN() noexcept { return android::half(android::half::binary, 0x7fff); }
+ inline static constexpr type denorm_min() noexcept { return android::half(android::half::binary, 0x0001); }
+ inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); }
+};
+
+} // namespace std
+
+#ifdef LIKELY_DEFINED_LOCAL
+#undef LIKELY_DEFINED_LOCAL
+#undef LIKELY
+#undef UNLIKELY
+#endif // LIKELY_DEFINED_LOCAL
+
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat2.h b/libs/math/include/math/mat2.h
new file mode 100644
index 0000000000..3e6cd4c794
--- /dev/null
+++ b/libs/math/include/math/mat2.h
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/TMatHelpers.h>
+#include <math/vec2.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+/**
+ * A 2x2 column-major matrix class.
+ *
+ * Conceptually a 2x2 matrix is a an array of 2 column vec2:
+ *
+ * mat2 m =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0] & m[1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0][0] & m[1][0] \\
+ * m[0][1] & m[1][1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m(0,0) & m(0,1) \\
+ * m(1,0) & m(1,1) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2.
+ *
+ */
+template <typename T>
+class TMat22 : public TVecUnaryOperators<TMat22, T>,
+ public TVecComparisonOperators<TMat22, T>,
+ public TVecAddOperators<TMat22, T>,
+ public TMatProductOperators<TMat22, T>,
+ public TMatSquareFunctions<TMat22, T>,
+ public TMatHelpers<TMat22, T>,
+ public TMatDebug<TMat22, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ typedef TVec2<T> col_type;
+ typedef TVec2<T> row_type;
+
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+ /*
+ * <-- N columns -->
+ *
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
+ *
+ * COL_SIZE = M
+ * ROW_SIZE = N
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+ */
+
+ col_type m_value[NUM_COLS];
+
+public:
+ // array access
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
+
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TMat22(const TMat22&) = default;
+ ~TMat22() = default;
+ TMat22& operator = (const TMat22&) = default;
+
+ /**
+ * constructors
+ */
+
+ /**
+ * leaves object uninitialized. use with caution.
+ */
+ explicit constexpr TMat22(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
+
+
+ /**
+ * initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * 1 & 0 \\
+ * 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat22();
+
+ /**
+ * initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v & 0 \\
+ * 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template<typename U>
+ explicit CONSTEXPR TMat22(U v);
+
+ /**
+ * sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v[0] & 0 \\
+ * 0 & v[1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(const TVec2<U>& v);
+
+ /**
+ * construct from another matrix of the same size
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(const TMat22<U>& rhs);
+
+ /**
+ * construct from 2 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * v0 & v1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename A, typename B>
+ CONSTEXPR TMat22(const TVec2<A>& v0, const TVec2<B>& v1);
+
+ /** construct from 4 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cc}
+ * m[0][0] & m[1][0] \\
+ * m[0][1] & m[1][1] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <
+ typename A, typename B,
+ typename C, typename D>
+ CONSTEXPR TMat22(A m00, B m01, C m10, D m11);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat22(U const* rawArray);
+
+ /**
+ * Rotate by radians in the 2D plane
+ */
+ static CONSTEXPR TMat22<T> rotate(T radian) {
+ TMat22<T> r(TMat22<T>::NO_INIT);
+ T c = std::cos(radian);
+ T s = std::sin(radian);
+ r[0][0] = c; r[1][1] = c;
+ r[0][1] = s; r[1][0] = -s;
+ return r;
+ }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat22<T>::TMat22() {
+ m_value[0] = col_type(1, 0);
+ m_value[1] = col_type(0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U v) {
+ m_value[0] = col_type(v, 0);
+ m_value[1] = col_type(0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<U>& v) {
+ m_value[0] = col_type(v.x, 0);
+ m_value[1] = col_type(0, v.y);
+}
+
+// construct from 4 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+ typename A, typename B,
+ typename C, typename D>
+CONSTEXPR TMat22<T>::TMat22( A m00, B m01, C m10, D m11) {
+ m_value[0] = col_type(m00, m01);
+ m_value[1] = col_type(m10, m11);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(const TMat22<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
+}
+
+// Construct from 2 column vectors.
+template <typename T>
+template <typename A, typename B>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<A>& v0, const TVec2<B>& v1) {
+ m_value[0] = v0;
+ m_value[1] = v1;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::col_type PURE operator *(const TMat22<T>& lhs, const TVec2<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat22<U>::col_type result;
+ for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
+ return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::row_type PURE operator *(const TVec2<U>& lhs, const TMat22<T>& rhs) {
+ typename TMat22<U>::row_type result(TMat22<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
+ return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(TMat22<T> lhs, U rhs) {
+ return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(U lhs, const TMat22<T>& rhs) {
+ return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat22<T>::col_type PURE diag(const TMat22<T>& m) {
+ return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat22<double> mat2d;
+typedef details::TMat22<float> mat2;
+typedef details::TMat22<float> mat2f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h
new file mode 100644
index 0000000000..5c8a9b2573
--- /dev/null
+++ b/libs/math/include/math/mat3.h
@@ -0,0 +1,440 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/quat.h>
+#include <math/TMatHelpers.h>
+#include <math/vec3.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 3x3 column-major matrix class.
+ *
+ * Conceptually a 3x3 matrix is a an array of 3 column vec3:
+ *
+ * mat3 m =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0] & m[1] & m[2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0][0] & m[1][0] & m[2][0] \\
+ * m[0][1] & m[1][1] & m[2][1] \\
+ * m[0][2] & m[1][2] & m[2][2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m(0,0) & m(0,1) & m(0,2) \\
+ * m(1,0) & m(1,1) & m(1,2) \\
+ * m(2,0) & m(2,1) & m(2,2) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3.
+ *
+ */
+template <typename T>
+class TMat33 : public TVecUnaryOperators<TMat33, T>,
+ public TVecComparisonOperators<TMat33, T>,
+ public TVecAddOperators<TMat33, T>,
+ public TMatProductOperators<TMat33, T>,
+ public TMatSquareFunctions<TMat33, T>,
+ public TMatTransform<TMat33, T>,
+ public TMatHelpers<TMat33, T>,
+ public TMatDebug<TMat33, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ typedef TVec3<T> col_type;
+ typedef TVec3<T> row_type;
+
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+ /*
+ * <-- N columns -->
+ *
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
+ *
+ * COL_SIZE = M
+ * ROW_SIZE = N
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+ */
+
+ col_type m_value[NUM_COLS];
+
+public:
+ // array access
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
+
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TMat33(const TMat33&) = default;
+ ~TMat33() = default;
+ TMat33& operator = (const TMat33&) = default;
+
+ /**
+ * constructors
+ */
+
+ /**
+ * leaves object uninitialized. use with caution.
+ */
+ explicit constexpr TMat33(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
+
+
+ /**
+ * initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * 1 & 0 & 0 \\
+ * 0 & 1 & 0 \\
+ * 0 & 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat33();
+
+ /**
+ * initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v & 0 & 0 \\
+ * 0 & v & 0 \\
+ * 0 & 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template<typename U>
+ explicit CONSTEXPR TMat33(U v);
+
+ /**
+ * sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v[0] & 0 & 0 \\
+ * 0 & v[1] & 0 \\
+ * 0 & 0 & v[2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TVec3<U>& v);
+
+ /**
+ * construct from another matrix of the same size
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TMat33<U>& rhs);
+
+ /**
+ * construct from 3 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * v0 & v1 & v2 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename A, typename B, typename C>
+ CONSTEXPR TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2);
+
+ /** construct from 9 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{ccc}
+ * m[0][0] & m[1][0] & m[2][0] \\
+ * m[0][1] & m[1][1] & m[2][1] \\
+ * m[0][2] & m[1][2] & m[2][2] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <
+ typename A, typename B, typename C,
+ typename D, typename E, typename F,
+ typename G, typename H, typename I>
+ CONSTEXPR TMat33(
+ A m00, B m01, C m02,
+ D m10, E m11, F m12,
+ G m20, H m21, I m22);
+
+ /**
+ * construct from a quaternion
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(const TQuaternion<U>& q);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat33(U const* rawArray);
+
+ /**
+ * orthogonalize only works on matrices of size 3x3
+ */
+ friend inline
+ CONSTEXPR TMat33 orthogonalize(const TMat33& m) {
+ TMat33 ret(TMat33::NO_INIT);
+ ret[0] = normalize(m[0]);
+ ret[2] = normalize(cross(ret[0], m[1]));
+ ret[1] = normalize(cross(ret[2], ret[0]));
+ return ret;
+ }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat33<T>::TMat33() {
+ m_value[0] = col_type(1, 0, 0);
+ m_value[1] = col_type(0, 1, 0);
+ m_value[2] = col_type(0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U v) {
+ m_value[0] = col_type(v, 0, 0);
+ m_value[1] = col_type(0, v, 0);
+ m_value[2] = col_type(0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<U>& v) {
+ m_value[0] = col_type(v.x, 0, 0);
+ m_value[1] = col_type(0, v.y, 0);
+ m_value[2] = col_type(0, 0, v.z);
+}
+
+// construct from 9 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+ typename A, typename B, typename C,
+ typename D, typename E, typename F,
+ typename G, typename H, typename I>
+CONSTEXPR TMat33<T>::TMat33(
+ A m00, B m01, C m02,
+ D m10, E m11, F m12,
+ G m20, H m21, I m22) {
+ m_value[0] = col_type(m00, m01, m02);
+ m_value[1] = col_type(m10, m11, m12);
+ m_value[2] = col_type(m20, m21, m22);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TMat33<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
+}
+
+// Construct from 3 column vectors.
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2) {
+ m_value[0] = v0;
+ m_value[1] = v1;
+ m_value[2] = v2;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TQuaternion<U>& q) {
+ const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+ const U s = n > 0 ? 2/n : 0;
+ const U x = s*q.x;
+ const U y = s*q.y;
+ const U z = s*q.z;
+ const U xx = x*q.x;
+ const U xy = x*q.y;
+ const U xz = x*q.z;
+ const U xw = x*q.w;
+ const U yy = y*q.y;
+ const U yz = y*q.z;
+ const U yw = y*q.w;
+ const U zz = z*q.z;
+ const U zw = z*q.w;
+ m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw); // NOLINT
+ m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw); // NOLINT
+ m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy); // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::col_type PURE operator *(const TMat33<T>& lhs, const TVec3<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat33<U>::col_type result;
+ for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
+ return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::row_type PURE operator *(const TVec3<U>& lhs, const TMat33<T>& rhs) {
+ typename TMat33<U>::row_type result(TMat33<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
+ return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(TMat33<T> lhs, U rhs) {
+ return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(U lhs, const TMat33<T>& rhs) {
+ return rhs * lhs;
+}
+
+//------------------------------------------------------------------------------
+template <typename T>
+CONSTEXPR TMat33<T> orthogonalize(const TMat33<T>& m) {
+ TMat33<T> ret(TMat33<T>::NO_INIT);
+ ret[0] = normalize(m[0]);
+ ret[2] = normalize(cross(ret[0], m[1]));
+ ret[1] = normalize(cross(ret[2], ret[0]));
+ return ret;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat33<T>::col_type PURE diag(const TMat33<T>& m) {
+ return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat33<double> mat3d;
+typedef details::TMat33<float> mat3;
+typedef details::TMat33<float> mat3f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h
new file mode 100644
index 0000000000..6119ba7f68
--- /dev/null
+++ b/libs/math/include/math/mat4.h
@@ -0,0 +1,586 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/mat3.h>
+#include <math/quat.h>
+#include <math/TMatHelpers.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 4x4 column-major matrix class.
+ *
+ * Conceptually a 4x4 matrix is a an array of 4 column double4:
+ *
+ * mat4 m =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0] & m[1] & m[2] & m[3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+ * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+ * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+ * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ * =
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m(0,0) & m(0,1) & m(0,2) & m(0,3) \\
+ * m(1,0) & m(1,1) & m(1,2) & m(1,3) \\
+ * m(2,0) & m(2,1) & m(2,2) & m(2,3) \\
+ * m(3,0) & m(3,1) & m(3,2) & m(3,3) \\
+ * \end{array}
+ * \right)
+ * \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4.
+ *
+ */
+template <typename T>
+class TMat44 : public TVecUnaryOperators<TMat44, T>,
+ public TVecComparisonOperators<TMat44, T>,
+ public TVecAddOperators<TMat44, T>,
+ public TMatProductOperators<TMat44, T>,
+ public TMatSquareFunctions<TMat44, T>,
+ public TMatTransform<TMat44, T>,
+ public TMatHelpers<TMat44, T>,
+ public TMatDebug<TMat44, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+ typedef TVec4<T> col_type;
+ typedef TVec4<T> row_type;
+
+ static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows)
+ static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns)
+ static constexpr size_t NUM_ROWS = COL_SIZE;
+ static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+ /*
+ * <-- N columns -->
+ *
+ * a[0][0] a[1][0] a[2][0] ... a[N][0] ^
+ * a[0][1] a[1][1] a[2][1] ... a[N][1] |
+ * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows
+ * ... |
+ * a[0][M] a[1][M] a[2][M] ... a[N][M] v
+ *
+ * COL_SIZE = M
+ * ROW_SIZE = N
+ * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+ */
+
+ col_type m_value[NUM_COLS];
+
+public:
+ // array access
+ inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(column < NUM_COLS);
+#endif
+ return m_value[column];
+ }
+
+ inline col_type& operator[](size_t column) {
+ assert(column < NUM_COLS);
+ return m_value[column];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TMat44(const TMat44&) = default;
+ ~TMat44() = default;
+ TMat44& operator = (const TMat44&) = default;
+
+ /*
+ * constructors
+ */
+
+ // leaves object uninitialized. use with caution.
+ explicit constexpr TMat44(no_init)
+ : m_value{ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT),
+ col_type(col_type::NO_INIT) } {}
+
+ /** initialize to identity.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * 1 & 0 & 0 & 0 \\
+ * 0 & 1 & 0 & 0 \\
+ * 0 & 0 & 1 & 0 \\
+ * 0 & 0 & 0 & 1 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ CONSTEXPR TMat44();
+
+ /** initialize to Identity*scalar.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v & 0 & 0 & 0 \\
+ * 0 & v & 0 & 0 \\
+ * 0 & 0 & v & 0 \\
+ * 0 & 0 & 0 & v \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template<typename U>
+ explicit CONSTEXPR TMat44(U v);
+
+ /** sets the diagonal to a vector.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v[0] & 0 & 0 & 0 \\
+ * 0 & v[1] & 0 & 0 \\
+ * 0 & 0 & v[2] & 0 \\
+ * 0 & 0 & 0 & v[3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TVec4<U>& v);
+
+ // construct from another matrix of the same size
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TMat44<U>& rhs);
+
+ /** construct from 4 column vectors.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * v0 & v1 & v2 & v3 \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <typename A, typename B, typename C, typename D>
+ CONSTEXPR TMat44(const TVec4<A>& v0, const TVec4<B>& v1, const TVec4<C>& v2, const TVec4<D>& v3);
+
+ /** construct from 16 elements in column-major form.
+ *
+ * \f$
+ * \left(
+ * \begin{array}{cccc}
+ * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+ * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+ * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+ * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+ * \end{array}
+ * \right)
+ * \f$
+ */
+ template <
+ typename A, typename B, typename C, typename D,
+ typename E, typename F, typename G, typename H,
+ typename I, typename J, typename K, typename L,
+ typename M, typename N, typename O, typename P>
+ CONSTEXPR TMat44(
+ A m00, B m01, C m02, D m03,
+ E m10, F m11, G m12, H m13,
+ I m20, J m21, K m22, L m23,
+ M m30, N m31, O m32, P m33);
+
+ /**
+ * construct from a quaternion
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TQuaternion<U>& q);
+
+ /**
+ * construct from a C array in column major form.
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(U const* rawArray);
+
+ /**
+ * construct from a 3x3 matrix
+ */
+ template <typename U>
+ explicit CONSTEXPR TMat44(const TMat33<U>& matrix);
+
+ /**
+ * construct from a 3x3 matrix and 3d translation
+ */
+ template <typename U, typename V>
+ CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec3<V>& translation);
+
+ /**
+ * construct from a 3x3 matrix and 4d last column.
+ */
+ template <typename U, typename V>
+ CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec4<V>& column3);
+
+ /*
+ * helpers
+ */
+
+ static CONSTEXPR TMat44 ortho(T left, T right, T bottom, T top, T near, T far);
+
+ static CONSTEXPR TMat44 frustum(T left, T right, T bottom, T top, T near, T far);
+
+ enum class Fov {
+ HORIZONTAL,
+ VERTICAL
+ };
+ static CONSTEXPR TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL);
+
+ template <typename A, typename B, typename C>
+ static CONSTEXPR TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up);
+
+ template <typename A>
+ static CONSTEXPR TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) {
+ TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 };
+ return r.xyz / r.w;
+ }
+
+ template <typename A>
+ static CONSTEXPR TVec4<A> project(const TMat44& projectionMatrix, TVec4<A> vertice) {
+ vertice = projectionMatrix * vertice;
+ return { vertice.xyz / vertice.w, 1 };
+ }
+
+ /**
+ * Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix
+ */
+ inline constexpr TMat33<T> upperLeft() const {
+ return TMat33<T>(m_value[0].xyz, m_value[1].xyz, m_value[2].xyz);
+ }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat44<T>::TMat44() {
+ m_value[0] = col_type(1, 0, 0, 0);
+ m_value[1] = col_type(0, 1, 0, 0);
+ m_value[2] = col_type(0, 0, 1, 0);
+ m_value[3] = col_type(0, 0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U v) {
+ m_value[0] = col_type(v, 0, 0, 0);
+ m_value[1] = col_type(0, v, 0, 0);
+ m_value[2] = col_type(0, 0, v, 0);
+ m_value[3] = col_type(0, 0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat44<T>::TMat44(const TVec4<U>& v) {
+ m_value[0] = col_type(v.x, 0, 0, 0);
+ m_value[1] = col_type(0, v.y, 0, 0);
+ m_value[2] = col_type(0, 0, v.z, 0);
+ m_value[3] = col_type(0, 0, 0, v.w);
+}
+
+// construct from 16 scalars
+template<typename T>
+template <
+ typename A, typename B, typename C, typename D,
+ typename E, typename F, typename G, typename H,
+ typename I, typename J, typename K, typename L,
+ typename M, typename N, typename O, typename P>
+CONSTEXPR TMat44<T>::TMat44(
+ A m00, B m01, C m02, D m03,
+ E m10, F m11, G m12, H m13,
+ I m20, J m21, K m22, L m23,
+ M m30, N m31, O m32, P m33) {
+ m_value[0] = col_type(m00, m01, m02, m03);
+ m_value[1] = col_type(m10, m11, m12, m13);
+ m_value[2] = col_type(m20, m21, m22, m23);
+ m_value[3] = col_type(m30, m31, m32, m33);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat44<U>& rhs) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ m_value[col] = col_type(rhs[col]);
+ }
+}
+
+// Construct from 4 column vectors.
+template <typename T>
+template <typename A, typename B, typename C, typename D>
+CONSTEXPR TMat44<T>::TMat44(
+ const TVec4<A>& v0, const TVec4<B>& v1,
+ const TVec4<C>& v2, const TVec4<D>& v3) {
+ m_value[0] = col_type(v0);
+ m_value[1] = col_type(v1);
+ m_value[2] = col_type(v2);
+ m_value[3] = col_type(v3);
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U const* rawArray) {
+ for (size_t col = 0; col < NUM_COLS; ++col) {
+ for (size_t row = 0; row < NUM_ROWS; ++row) {
+ m_value[col][row] = *rawArray++;
+ }
+ }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TQuaternion<U>& q) {
+ const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+ const U s = n > 0 ? 2/n : 0;
+ const U x = s*q.x;
+ const U y = s*q.y;
+ const U z = s*q.z;
+ const U xx = x*q.x;
+ const U xy = x*q.y;
+ const U xz = x*q.z;
+ const U xw = x*q.w;
+ const U yy = y*q.y;
+ const U yz = y*q.z;
+ const U yw = y*q.w;
+ const U zz = z*q.z;
+ const U zw = z*q.w;
+ m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw, 0);
+ m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw, 0); // NOLINT
+ m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy, 0); // NOLINT
+ m_value[3] = col_type( 0, 0, 0, 1); // NOLINT
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+ m_value[3] = col_type( 0, 0, 0, 1); // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec3<V>& v) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+ m_value[3] = col_type( v[0], v[1], v[2], 1); // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec4<V>& v) {
+ m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); // NOLINT
+ m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); // NOLINT
+ m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); // NOLINT
+ m_value[3] = col_type( v[0], v[1], v[2], v[3]); // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------------------
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
+ TMat44<T> m;
+ m[0][0] = 2 / (right - left);
+ m[1][1] = 2 / (top - bottom);
+ m[2][2] = -2 / (far - near);
+ m[3][0] = -(right + left) / (right - left);
+ m[3][1] = -(top + bottom) / (top - bottom);
+ m[3][2] = -(far + near) / (far - near);
+ return m;
+}
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
+ TMat44<T> m;
+ m[0][0] = (2 * near) / (right - left);
+ m[1][1] = (2 * near) / (top - bottom);
+ m[2][0] = (right + left) / (right - left);
+ m[2][1] = (top + bottom) / (top - bottom);
+ m[2][2] = -(far + near) / (far - near);
+ m[2][3] = -1;
+ m[3][2] = -(2 * far * near) / (far - near);
+ m[3][3] = 0;
+ return m;
+}
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) {
+ T h;
+ T w;
+
+ if (direction == TMat44::Fov::VERTICAL) {
+ h = std::tan(fov * M_PI / 360.0f) * near;
+ w = h * aspect;
+ } else {
+ w = std::tan(fov * M_PI / 360.0f) * near;
+ h = w / aspect;
+ }
+ return frustum(-w, w, -h, h, near, far);
+}
+
+/*
+ * Returns a matrix representing the pose of a virtual camera looking towards -Z in its
+ * local Y-up coordinate system. "eye" is where the camera is located, "center" is the points its
+ * looking at and "up" defines where the Y axis of the camera's local coordinate system is.
+ */
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) {
+ TVec3<T> z_axis(normalize(center - eye));
+ TVec3<T> norm_up(normalize(up));
+ if (std::abs(dot(z_axis, norm_up)) > 0.999) {
+ // Fix up vector if we're degenerate (looking straight up, basically)
+ norm_up = { norm_up.z, norm_up.x, norm_up.y };
+ }
+ TVec3<T> x_axis(normalize(cross(z_axis, norm_up)));
+ TVec3<T> y_axis(cross(x_axis, z_axis));
+ return TMat44<T>(
+ TVec4<T>(x_axis, 0),
+ TVec4<T>(y_axis, 0),
+ TVec4<T>(-z_axis, 0),
+ TVec4<T>(eye, 1));
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec4<U>& rhs) {
+ // Result is initialized to zero.
+ typename TMat44<T>::col_type result;
+ for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+ result += lhs[col] * rhs[col];
+ }
+ return result;
+}
+
+// mat44 * vec3, result is vec3( mat44 * {vec3, 1} )
+template <typename T, typename U>
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec3<U>& rhs) {
+ return lhs * TVec4<U>{ rhs, 1 };
+}
+
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<U>::row_type PURE operator *(const TVec4<U>& lhs, const TMat44<T>& rhs) {
+ typename TMat44<U>::row_type result(TMat44<U>::row_type::NO_INIT);
+ for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+ result[col] = dot(lhs, rhs[col]);
+ }
+ return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template <typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(TMat44<T> lhs, U rhs) {
+ return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template <typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(U lhs, const TMat44<T>& rhs) {
+ return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+typename TMat44<T>::col_type PURE diag(const TMat44<T>& m) {
+ return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat44<double> mat4d;
+typedef details::TMat44<float> mat4;
+typedef details::TMat44<float> mat4f;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h
new file mode 100644
index 0000000000..1936a2baec
--- /dev/null
+++ b/libs/math/include/math/quat.h
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/half.h>
+#include <math/TQuatHelpers.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifndef PURE
+#define PURE __attribute__((pure))
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TQuaternion : public TVecAddOperators<TQuaternion, T>,
+ public TVecUnaryOperators<TQuaternion, T>,
+ public TVecComparisonOperators<TQuaternion, T>,
+ public TQuatProductOperators<TQuaternion, T>,
+ public TQuatFunctions<TQuaternion, T>,
+ public TQuatDebug<TQuaternion, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ /*
+ * quaternion internals stored as:
+ *
+ * q = w + xi + yj + zk
+ *
+ * q[0] = x;
+ * q[1] = y;
+ * q[2] = z;
+ * q[3] = w;
+ *
+ */
+ union {
+ struct { T x, y, z, w; };
+ TVec4<T> xyzw;
+ TVec3<T> xyz;
+ TVec2<T> xy;
+ };
+
+ enum { SIZE = 4 };
+ inline constexpr static size_type size() { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TQuaternion(const TQuaternion&) = default;
+ ~TQuaternion() = default;
+ TQuaternion& operator = (const TQuaternion&) = default;
+
+ // constructors
+
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TQuaternion(no_init) : xyzw(TVec4<T>::NO_INIT) {}
+
+ // default constructor. sets all values to zero.
+ constexpr TQuaternion() : x(0), y(0), z(0), w(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A>
+ constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {
+ static_assert(std::is_arithmetic<A>::value, "requires arithmetic type");
+ }
+
+ // initialize from 4 values to w + xi + yj + zk
+ template<typename A, typename B, typename C, typename D>
+ constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) { }
+
+ // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w
+ template<typename A, typename B>
+ constexpr TQuaternion(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+ // initialize from a double4
+ template<typename A>
+ constexpr explicit TQuaternion(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+ // initialize from a quaternion of a different type
+ template<typename A>
+ constexpr explicit TQuaternion(const TQuaternion<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+ // conjugate operator
+ constexpr TQuaternion operator~() const {
+ return conj(*this);
+ }
+
+ // constructs a quaternion from an axis and angle
+ template <typename A, typename B>
+ constexpr static TQuaternion PURE fromAxisAngle(const TVec3<A>& axis, B angle) {
+ return TQuaternion(std::sin(angle*0.5) * normalize(axis), std::cos(angle*0.5));
+ }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TQuaternion<double> quatd;
+typedef details::TQuaternion<float> quat;
+typedef details::TQuaternion<float> quatf;
+typedef details::TQuaternion<half> quath;
+
+constexpr inline quat operator"" _i(long double v) {
+ return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(long double v) {
+ return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(long double v) {
+ return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quat operator"" _i(unsigned long long v) { // NOLINT
+ return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(unsigned long long v) { // NOLINT
+ return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(unsigned long long v) { // NOLINT
+ return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quatd operator"" _id(long double v) {
+ return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(long double v) {
+ return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(long double v) {
+ return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+constexpr inline quatd operator"" _id(unsigned long long v) { // NOLINT
+ return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(unsigned long long v) { // NOLINT
+ return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(unsigned long long v) { // NOLINT
+ return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
+
+#undef PURE
diff --git a/libs/math/include/math/scalar.h b/libs/math/include/math/scalar.h
new file mode 100644
index 0000000000..2eced92e76
--- /dev/null
+++ b/libs/math/include/math/scalar.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 <algorithm>
+#include <cmath>
+
+namespace android {
+
+template<typename T>
+static constexpr T saturate(T v) noexcept {
+ return T(std::min(T(1), std::max(T(0), v)));
+}
+
+template<typename T>
+static constexpr T clamp(T v, T min, T max) noexcept {
+ return T(std::min(max, std::max(min, v)));
+}
+
+template<typename T>
+static constexpr T mix(T x, T y, T a) noexcept {
+ return x * (T(1) - a) + y * a;
+}
+
+template<typename T>
+static constexpr T lerp(T x, T y, T a) noexcept {
+ return mix(x, y, a);
+}
+
+} // namespace std
diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h
new file mode 100644
index 0000000000..a34763347c
--- /dev/null
+++ b/libs/math/include/math/vec2.h
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/TVecHelpers.h>
+#include <math/half.h>
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <type_traits>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec2 : public TVecProductOperators<TVec2, T>,
+ public TVecAddOperators<TVec2, T>,
+ public TVecUnaryOperators<TVec2, T>,
+ public TVecComparisonOperators<TVec2, T>,
+ public TVecFunctions<TVec2, T>,
+ public TVecDebug<TVec2, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ union {
+ struct { T x, y; };
+ struct { T s, t; };
+ struct { T r, g; };
+ };
+
+ static constexpr size_t SIZE = 2;
+ inline constexpr size_type size() const { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TVec2(const TVec2&) = default;
+ ~TVec2() = default;
+ TVec2& operator = (const TVec2&) = default;
+
+ // constructors
+
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TVec2(no_init) { }
+
+ // default constructor
+ constexpr TVec2() : x(0), y(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec2(A v) : x(v), y(v) { }
+
+ template<typename A, typename B>
+ constexpr TVec2(A x, B y) : x(x), y(y) { }
+
+ template<typename A>
+ explicit
+ constexpr TVec2(const TVec2<A>& v) : x(v.x), y(v.y) { }
+
+ // cross product works only on vectors of size 2 or 3
+ template<typename RT>
+ friend inline
+ constexpr value_type cross(const TVec2& u, const TVec2<RT>& v) {
+ return value_type(u.x*v.y - u.y*v.x);
+ }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec2<double> double2;
+typedef details::TVec2<float> float2;
+typedef details::TVec2<float> vec2;
+typedef details::TVec2<half> half2;
+typedef details::TVec2<int32_t> int2;
+typedef details::TVec2<uint32_t> uint2;
+typedef details::TVec2<int16_t> short2;
+typedef details::TVec2<uint16_t> ushort2;
+typedef details::TVec2<int8_t> byte2;
+typedef details::TVec2<uint8_t> ubyte2;
+typedef details::TVec2<bool> bool2;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h
new file mode 100644
index 0000000000..009fd84e3b
--- /dev/null
+++ b/libs/math/include/math/vec3.h
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/vec2.h>
+#include <math/half.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec3 : public TVecProductOperators<TVec3, T>,
+ public TVecAddOperators<TVec3, T>,
+ public TVecUnaryOperators<TVec3, T>,
+ public TVecComparisonOperators<TVec3, T>,
+ public TVecFunctions<TVec3, T>,
+ public TVecDebug<TVec3, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ union {
+ struct { T x, y, z; };
+ struct { T s, t, p; };
+ struct { T r, g, b; };
+ TVec2<T> xy;
+ TVec2<T> st;
+ TVec2<T> rg;
+ };
+
+ static constexpr size_t SIZE = 3;
+ inline constexpr size_type size() const { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TVec3(const TVec3&) = default;
+ ~TVec3() = default;
+ TVec3& operator = (const TVec3&) = default;
+
+ // constructors
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TVec3(no_init) { }
+
+ // default constructor
+ constexpr TVec3() : x(0), y(0), z(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec3(A v) : x(v), y(v), z(v) { }
+
+ template<typename A, typename B, typename C>
+ constexpr TVec3(A x, B y, C z) : x(x), y(y), z(z) { }
+
+ template<typename A, typename B>
+ constexpr TVec3(const TVec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
+
+ template<typename A>
+ explicit
+ constexpr TVec3(const TVec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
+
+ // cross product works only on vectors of size 3
+ template <typename RT>
+ friend inline
+ constexpr TVec3 cross(const TVec3& u, const TVec3<RT>& v) {
+ return TVec3(
+ u.y*v.z - u.z*v.y,
+ u.z*v.x - u.x*v.z,
+ u.x*v.y - u.y*v.x);
+ }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec3<double> double3;
+typedef details::TVec3<float> float3;
+typedef details::TVec3<float> vec3;
+typedef details::TVec3<half> half3;
+typedef details::TVec3<int32_t> int3;
+typedef details::TVec3<uint32_t> uint3;
+typedef details::TVec3<int16_t> short3;
+typedef details::TVec3<uint16_t> ushort3;
+typedef details::TVec3<int8_t> byte3;
+typedef details::TVec3<uint8_t> ubyte3;
+typedef details::TVec3<bool> bool3;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec4.h b/libs/math/include/math/vec4.h
new file mode 100644
index 0000000000..1e279fe3f2
--- /dev/null
+++ b/libs/math/include/math/vec4.h
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <math/vec3.h>
+#include <math/half.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec4 : public TVecProductOperators<TVec4, T>,
+ public TVecAddOperators<TVec4, T>,
+ public TVecUnaryOperators<TVec4, T>,
+ public TVecComparisonOperators<TVec4, T>,
+ public TVecFunctions<TVec4, T>,
+ public TVecDebug<TVec4, T> {
+public:
+ enum no_init { NO_INIT };
+ typedef T value_type;
+ typedef T& reference;
+ typedef T const& const_reference;
+ typedef size_t size_type;
+
+ union {
+ struct { T x, y, z, w; };
+ struct { T s, t, p, q; };
+ struct { T r, g, b, a; };
+ TVec2<T> xy;
+ TVec2<T> st;
+ TVec2<T> rg;
+ TVec3<T> xyz;
+ TVec3<T> stp;
+ TVec3<T> rgb;
+ };
+
+ static constexpr size_t SIZE = 4;
+ inline constexpr size_type size() const { return SIZE; }
+
+ // array access
+ inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+ // only possible in C++0x14 with constexpr
+ assert(i < SIZE);
+#endif
+ return (&x)[i];
+ }
+
+ inline T& operator[](size_t i) {
+ assert(i < SIZE);
+ return (&x)[i];
+ }
+
+ // -----------------------------------------------------------------------
+ // we want the compiler generated versions for these...
+ TVec4(const TVec4&) = default;
+ ~TVec4() = default;
+ TVec4& operator = (const TVec4&) = default;
+
+ // constructors
+
+ // leaves object uninitialized. use with caution.
+ explicit
+ constexpr TVec4(no_init) { }
+
+ // default constructor
+ constexpr TVec4() : x(0), y(0), z(0), w(0) { }
+
+ // handles implicit conversion to a tvec4. must not be explicit.
+ template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+ constexpr TVec4(A v) : x(v), y(v), z(v), w(v) { }
+
+ template<typename A, typename B, typename C, typename D>
+ constexpr TVec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
+
+ template<typename A, typename B, typename C>
+ constexpr TVec4(const TVec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
+
+ template<typename A, typename B>
+ constexpr TVec4(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+ template<typename A>
+ explicit
+ constexpr TVec4(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+};
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec4<double> double4;
+typedef details::TVec4<float> float4;
+typedef details::TVec4<float> vec4;
+typedef details::TVec4<half> half4;
+typedef details::TVec4<int32_t> int4;
+typedef details::TVec4<uint32_t> uint4;
+typedef details::TVec4<int16_t> short4;
+typedef details::TVec4<uint16_t> ushort4;
+typedef details::TVec4<int8_t> byte4;
+typedef details::TVec4<uint8_t> ubyte4;
+typedef details::TVec4<bool> bool4;
+
+// ----------------------------------------------------------------------------------------
+} // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
new file mode 100644
index 0000000000..0ed24a2a1e
--- /dev/null
+++ b/libs/math/tests/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2014 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_test {
+ name: "vec_test",
+ srcs: ["vec_test.cpp"],
+ static_libs: ["libmath"],
+}
+
+cc_test {
+ name: "mat_test",
+ srcs: ["mat_test.cpp"],
+ static_libs: ["libmath"],
+}
+
+cc_test {
+ name: "half_test",
+ srcs: ["half_test.cpp"],
+ static_libs: ["libmath"],
+}
+
+cc_test {
+ name: "quat_test",
+ srcs: ["quat_test.cpp"],
+ static_libs: ["libmath"],
+}
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
new file mode 100644
index 0000000000..496a7ef56d
--- /dev/null
+++ b/libs/math/tests/half_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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_TAG "HalfTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/half.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HalfTest : public testing::Test {
+protected:
+};
+
+TEST_F(HalfTest, Basics) {
+
+ EXPECT_EQ(2UL, sizeof(half));
+
+ // test +/- zero
+ EXPECT_EQ(0x0000, half( 0.0f).getBits());
+ EXPECT_EQ(0x8000, half(-0.0f).getBits());
+
+ // test nan
+ EXPECT_EQ(0x7e00, half(NAN).getBits());
+
+ // test +/- infinity
+ EXPECT_EQ(0x7C00, half( std::numeric_limits<float>::infinity()).getBits());
+ EXPECT_EQ(0xFC00, half(-std::numeric_limits<float>::infinity()).getBits());
+
+ // test a few known values
+ EXPECT_EQ(0x3C01, half(1.0009765625).getBits());
+ EXPECT_EQ(0xC000, half(-2).getBits());
+ EXPECT_EQ(0x0400, half(6.10352e-5).getBits());
+ EXPECT_EQ(0x7BFF, half(65504).getBits());
+ EXPECT_EQ(0x3555, half(1.0f/3).getBits());
+
+ // numeric limits
+ EXPECT_EQ(0x7C00, std::numeric_limits<half>::infinity().getBits());
+ EXPECT_EQ(0x0400, std::numeric_limits<half>::min().getBits());
+ EXPECT_EQ(0x7BFF, std::numeric_limits<half>::max().getBits());
+ EXPECT_EQ(0xFBFF, std::numeric_limits<half>::lowest().getBits());
+
+ // denormals (flushed to zero)
+ EXPECT_EQ(0x0000, half( 6.09756e-5).getBits()); // if handled, should be: 0x03FF
+ EXPECT_EQ(0x0000, half( 5.96046e-8).getBits()); // if handled, should be: 0x0001
+ EXPECT_EQ(0x8000, half(-6.09756e-5).getBits()); // if handled, should be: 0x83FF
+ EXPECT_EQ(0x8000, half(-5.96046e-8).getBits()); // if handled, should be: 0x8001
+
+ // test all exactly representable integers
+ for (int i=-2048 ; i<= 2048 ; ++i) {
+ half h = i;
+ EXPECT_EQ(i, float(h));
+ }
+}
+
+TEST_F(HalfTest, Literals) {
+ half one = 1.0_hf;
+ half pi = 3.1415926_hf;
+ half minusTwo = -2.0_hf;
+
+ EXPECT_EQ(half(1.0f), one);
+ EXPECT_EQ(half(3.1415926), pi);
+ EXPECT_EQ(half(-2.0f), minusTwo);
+}
+
+
+TEST_F(HalfTest, Vec) {
+ float4 f4(1,2,3,4);
+ half4 h4(f4);
+ half3 h3(f4.xyz);
+ half2 h2(f4.xy);
+
+ EXPECT_EQ(f4, h4);
+ EXPECT_EQ(f4.xyz, h3);
+ EXPECT_EQ(f4.xy, h2);
+}
+
+}; // namespace android
diff --git a/libs/math/tests/mat_test.cpp b/libs/math/tests/mat_test.cpp
new file mode 100644
index 0000000000..c365366384
--- /dev/null
+++ b/libs/math/tests/mat_test.cpp
@@ -0,0 +1,692 @@
+/*
+ * 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_TAG "MatTest"
+
+#include <stdlib.h>
+
+#include <limits>
+#include <random>
+#include <functional>
+
+#include <gtest/gtest.h>
+
+#include <math/mat2.h>
+#include <math/mat4.h>
+
+namespace android {
+
+class MatTest : public testing::Test {
+protected:
+};
+
+TEST_F(MatTest, Basics) {
+ mat4 m0;
+ EXPECT_EQ(sizeof(mat4), sizeof(float)*16);
+}
+
+TEST_F(MatTest, ComparisonOps) {
+ mat4 m0;
+ mat4 m1(2);
+
+ EXPECT_TRUE(m0 == m0);
+ EXPECT_TRUE(m0 != m1);
+ EXPECT_FALSE(m0 != m0);
+ EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(MatTest, Constructors) {
+ mat4 m0;
+ ASSERT_EQ(m0[0].x, 1);
+ ASSERT_EQ(m0[0].y, 0);
+ ASSERT_EQ(m0[0].z, 0);
+ ASSERT_EQ(m0[0].w, 0);
+ ASSERT_EQ(m0[1].x, 0);
+ ASSERT_EQ(m0[1].y, 1);
+ ASSERT_EQ(m0[1].z, 0);
+ ASSERT_EQ(m0[1].w, 0);
+ ASSERT_EQ(m0[2].x, 0);
+ ASSERT_EQ(m0[2].y, 0);
+ ASSERT_EQ(m0[2].z, 1);
+ ASSERT_EQ(m0[2].w, 0);
+ ASSERT_EQ(m0[3].x, 0);
+ ASSERT_EQ(m0[3].y, 0);
+ ASSERT_EQ(m0[3].z, 0);
+ ASSERT_EQ(m0[3].w, 1);
+
+ mat4 m1(2);
+ mat4 m2(vec4(2));
+ mat4 m3(m2);
+
+ EXPECT_EQ(m1, m2);
+ EXPECT_EQ(m2, m3);
+ EXPECT_EQ(m3, m1);
+
+ mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4));
+}
+
+TEST_F(MatTest, ArithmeticOps) {
+ mat4 m0;
+ mat4 m1(2);
+ mat4 m2(vec4(2));
+
+ m1 += m2;
+ EXPECT_EQ(mat4(4), m1);
+
+ m2 -= m1;
+ EXPECT_EQ(mat4(-2), m2);
+
+ m1 *= 2;
+ EXPECT_EQ(mat4(8), m1);
+
+ m1 /= 2;
+ EXPECT_EQ(mat4(4), m1);
+
+ m0 = -m0;
+ EXPECT_EQ(mat4(-1), m0);
+}
+
+TEST_F(MatTest, UnaryOps) {
+ const mat4 identity;
+ mat4 m0;
+
+ m0 = -m0;
+ EXPECT_EQ(mat4(vec4(-1, 0, 0, 0),
+ vec4(0, -1, 0, 0),
+ vec4(0, 0, -1, 0),
+ vec4(0, 0, 0, -1)), m0);
+
+ m0 = -m0;
+ EXPECT_EQ(identity, m0);
+}
+
+TEST_F(MatTest, MiscOps) {
+ const mat4 identity;
+ mat4 m0;
+ EXPECT_EQ(4, trace(m0));
+
+ mat4 m1(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
+ mat4 m2(vec4(1, 5, 9, 13), vec4(2, 6, 10, 14), vec4(3, 7, 11, 15), vec4(4, 8, 12, 16));
+ EXPECT_EQ(m1, transpose(m2));
+ EXPECT_EQ(m2, transpose(m1));
+ EXPECT_EQ(vec4(1, 6, 11, 16), diag(m1));
+
+ EXPECT_EQ(identity, inverse(identity));
+
+ mat4 m3(vec4(4, 3, 0, 0), vec4(3, 2, 0, 0), vec4(0, 0, 1, 0), vec4(0, 0, 0, 1));
+ mat4 m3i(inverse(m3));
+ EXPECT_FLOAT_EQ(-2, m3i[0][0]);
+ EXPECT_FLOAT_EQ(3, m3i[0][1]);
+ EXPECT_FLOAT_EQ(3, m3i[1][0]);
+ EXPECT_FLOAT_EQ(-4, m3i[1][1]);
+
+ mat4 m3ii(inverse(m3i));
+ EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
+ EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
+ EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
+ EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
+
+ EXPECT_EQ(m1, m1*identity);
+
+
+ for (size_t c=0 ; c<4 ; c++) {
+ for (size_t r=0 ; r<4 ; r++) {
+ EXPECT_FLOAT_EQ(m1[c][r], m1(r, c));
+ }
+ }
+}
+
+TEST_F(MatTest, ElementAccess) {
+ mat4 m(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
+ for (size_t c=0 ; c<4 ; c++) {
+ for (size_t r=0 ; r<4 ; r++) {
+ EXPECT_FLOAT_EQ(m[c][r], m(r, c));
+ }
+ }
+
+ m(3,2) = 100;
+ EXPECT_FLOAT_EQ(m[2][3], 100);
+ EXPECT_FLOAT_EQ(m(3, 2), 100);
+}
+
+//------------------------------------------------------------------------------
+// MAT 3
+//------------------------------------------------------------------------------
+
+class Mat3Test : public testing::Test {
+protected:
+};
+
+TEST_F(Mat3Test, Basics) {
+ mat3 m0;
+ EXPECT_EQ(sizeof(mat3), sizeof(float)*9);
+}
+
+TEST_F(Mat3Test, ComparisonOps) {
+ mat3 m0;
+ mat3 m1(2);
+
+ EXPECT_TRUE(m0 == m0);
+ EXPECT_TRUE(m0 != m1);
+ EXPECT_FALSE(m0 != m0);
+ EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(Mat3Test, Constructors) {
+ mat3 m0;
+ ASSERT_EQ(m0[0].x, 1);
+ ASSERT_EQ(m0[0].y, 0);
+ ASSERT_EQ(m0[0].z, 0);
+ ASSERT_EQ(m0[1].x, 0);
+ ASSERT_EQ(m0[1].y, 1);
+ ASSERT_EQ(m0[1].z, 0);
+ ASSERT_EQ(m0[2].x, 0);
+ ASSERT_EQ(m0[2].y, 0);
+ ASSERT_EQ(m0[2].z, 1);
+
+ mat3 m1(2);
+ mat3 m2(vec3(2));
+ mat3 m3(m2);
+
+ EXPECT_EQ(m1, m2);
+ EXPECT_EQ(m2, m3);
+ EXPECT_EQ(m3, m1);
+}
+
+TEST_F(Mat3Test, ArithmeticOps) {
+ mat3 m0;
+ mat3 m1(2);
+ mat3 m2(vec3(2));
+
+ m1 += m2;
+ EXPECT_EQ(mat3(4), m1);
+
+ m2 -= m1;
+ EXPECT_EQ(mat3(-2), m2);
+
+ m1 *= 2;
+ EXPECT_EQ(mat3(8), m1);
+
+ m1 /= 2;
+ EXPECT_EQ(mat3(4), m1);
+
+ m0 = -m0;
+ EXPECT_EQ(mat3(-1), m0);
+}
+
+TEST_F(Mat3Test, UnaryOps) {
+ const mat3 identity;
+ mat3 m0;
+
+ m0 = -m0;
+ EXPECT_EQ(mat3(vec3(-1, 0, 0),
+ vec3(0, -1, 0),
+ vec3(0, 0, -1)), m0);
+
+ m0 = -m0;
+ EXPECT_EQ(identity, m0);
+}
+
+TEST_F(Mat3Test, MiscOps) {
+ const mat3 identity;
+ mat3 m0;
+ EXPECT_EQ(3, trace(m0));
+
+ mat3 m1(vec3(1, 2, 3), vec3(4, 5, 6), vec3(7, 8, 9));
+ mat3 m2(vec3(1, 4, 7), vec3(2, 5, 8), vec3(3, 6, 9));
+ EXPECT_EQ(m1, transpose(m2));
+ EXPECT_EQ(m2, transpose(m1));
+ EXPECT_EQ(vec3(1, 5, 9), diag(m1));
+
+ EXPECT_EQ(identity, inverse(identity));
+
+ mat3 m3(vec3(4, 3, 0), vec3(3, 2, 0), vec3(0, 0, 1));
+ mat3 m3i(inverse(m3));
+ EXPECT_FLOAT_EQ(-2, m3i[0][0]);
+ EXPECT_FLOAT_EQ(3, m3i[0][1]);
+ EXPECT_FLOAT_EQ(3, m3i[1][0]);
+ EXPECT_FLOAT_EQ(-4, m3i[1][1]);
+
+ mat3 m3ii(inverse(m3i));
+ EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
+ EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
+ EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
+ EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
+
+ EXPECT_EQ(m1, m1*identity);
+}
+
+//------------------------------------------------------------------------------
+// MAT 2
+//------------------------------------------------------------------------------
+
+class Mat2Test : public testing::Test {
+protected:
+};
+
+TEST_F(Mat2Test, Basics) {
+ mat2 m0;
+ EXPECT_EQ(sizeof(mat2), sizeof(float)*4);
+}
+
+TEST_F(Mat2Test, ComparisonOps) {
+ mat2 m0;
+ mat2 m1(2);
+
+ EXPECT_TRUE(m0 == m0);
+ EXPECT_TRUE(m0 != m1);
+ EXPECT_FALSE(m0 != m0);
+ EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(Mat2Test, Constructors) {
+ mat2 m0;
+ ASSERT_EQ(m0[0].x, 1);
+ ASSERT_EQ(m0[0].y, 0);
+ ASSERT_EQ(m0[1].x, 0);
+ ASSERT_EQ(m0[1].y, 1);
+
+ mat2 m1(2);
+ mat2 m2(vec2(2));
+ mat2 m3(m2);
+
+ EXPECT_EQ(m1, m2);
+ EXPECT_EQ(m2, m3);
+ EXPECT_EQ(m3, m1);
+}
+
+TEST_F(Mat2Test, ArithmeticOps) {
+ mat2 m0;
+ mat2 m1(2);
+ mat2 m2(vec2(2));
+
+ m1 += m2;
+ EXPECT_EQ(mat2(4), m1);
+
+ m2 -= m1;
+ EXPECT_EQ(mat2(-2), m2);
+
+ m1 *= 2;
+ EXPECT_EQ(mat2(8), m1);
+
+ m1 /= 2;
+ EXPECT_EQ(mat2(4), m1);
+
+ m0 = -m0;
+ EXPECT_EQ(mat2(-1), m0);
+}
+
+TEST_F(Mat2Test, UnaryOps) {
+ const mat2 identity;
+ mat2 m0;
+
+ m0 = -m0;
+ EXPECT_EQ(mat2(vec2(-1, 0),
+ vec2(0, -1)), m0);
+
+ m0 = -m0;
+ EXPECT_EQ(identity, m0);
+}
+
+TEST_F(Mat2Test, MiscOps) {
+ const mat2 identity;
+ mat2 m0;
+ EXPECT_EQ(2, trace(m0));
+
+ mat2 m1(vec2(1, 2), vec2(3, 4));
+ mat2 m2(vec2(1, 3), vec2(2, 4));
+ EXPECT_EQ(m1, transpose(m2));
+ EXPECT_EQ(m2, transpose(m1));
+ EXPECT_EQ(vec2(1, 4), diag(m1));
+
+ EXPECT_EQ(identity, inverse(identity));
+
+ EXPECT_EQ(m1, m1*identity);
+}
+
+//------------------------------------------------------------------------------
+// MORE MATRIX TESTS
+//------------------------------------------------------------------------------
+
+template <typename T>
+class MatTestT : public ::testing::Test {
+public:
+};
+
+typedef ::testing::Types<float,float> TestMatrixValueTypes;
+
+TYPED_TEST_CASE(MatTestT, TestMatrixValueTypes);
+
+#define TEST_MATRIX_INVERSE(MATRIX, EPSILON) \
+{ \
+ typedef decltype(MATRIX) MatrixType; \
+ MatrixType inv1 = inverse(MATRIX); \
+ MatrixType ident1 = MATRIX * inv1; \
+ static const MatrixType IDENTITY; \
+ for (size_t row = 0; row < MatrixType::ROW_SIZE; ++row) { \
+ for (size_t col = 0; col < MatrixType::COL_SIZE; ++col) { \
+ EXPECT_NEAR(ident1[row][col], IDENTITY[row][col], EPSILON); \
+ } \
+ } \
+}
+
+TYPED_TEST(MatTestT, Inverse4) {
+ typedef ::android::details::TMat44<TypeParam> M44T;
+
+ M44T m1(1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ M44T m2(0, -1, 0, 0,
+ 1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ M44T m3(1, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 0, 1,
+ 0, 0, -1, 0);
+
+ M44T m4(
+ 4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00,
+ -8.749647e-01, 1.456563e-01, -4.617587e-01, 3.044795e+00,
+ 1.229049e-01, 9.892561e-01, 7.916244e-02, -6.737138e+00,
+ 0.000000e+00, 0.000000e+00, 0.000000e+00, 1.000000e+00);
+
+ M44T m5(
+ 4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00,
+ -8.749647e-01, 1.456563e-01, -4.617587e-01, 3.044795e+00,
+ 1.229049e-01, 9.892561e-01, 7.916244e-02, -6.737138e+00,
+ 1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00);
+
+ TEST_MATRIX_INVERSE(m1, 0);
+ TEST_MATRIX_INVERSE(m2, 0);
+ TEST_MATRIX_INVERSE(m3, 0);
+ TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+ TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+TYPED_TEST(MatTestT, Inverse3) {
+ typedef ::android::details::TMat33<TypeParam> M33T;
+
+ M33T m1(1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1);
+
+ M33T m2(0, -1, 0,
+ 1, 0, 0,
+ 0, 0, 1);
+
+ M33T m3(2, 0, 0,
+ 0, 0, 1,
+ 0, -1, 0);
+
+ M33T m4(
+ 4.683281e-01, 1.251189e-02, 0.000000e+00,
+ -8.749647e-01, 1.456563e-01, 0.000000e+00,
+ 0.000000e+00, 0.000000e+00, 1.000000e+00);
+
+ M33T m5(
+ 4.683281e-01, 1.251189e-02, -8.834660e-01,
+ -8.749647e-01, 1.456563e-01, -4.617587e-01,
+ 1.229049e-01, 9.892561e-01, 7.916244e-02);
+
+ TEST_MATRIX_INVERSE(m1, 0);
+ TEST_MATRIX_INVERSE(m2, 0);
+ TEST_MATRIX_INVERSE(m3, 0);
+ TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+ TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+TYPED_TEST(MatTestT, Inverse2) {
+ typedef ::android::details::TMat22<TypeParam> M22T;
+
+ M22T m1(1, 0,
+ 0, 1);
+
+ M22T m2(0, -1,
+ 1, 0);
+
+ M22T m3(
+ 4.683281e-01, 1.251189e-02,
+ -8.749647e-01, 1.456563e-01);
+
+ M22T m4(
+ 4.683281e-01, 1.251189e-02,
+ -8.749647e-01, 1.456563e-01);
+
+ TEST_MATRIX_INVERSE(m1, 0);
+ TEST_MATRIX_INVERSE(m2, 0);
+ TEST_MATRIX_INVERSE(m3, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+ TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+// A macro to help with vector comparisons within floating point range.
+#define EXPECT_VEC_EQ(VEC1, VEC2) \
+do { \
+ const decltype(VEC1) v1 = VEC1; \
+ const decltype(VEC2) v2 = VEC2; \
+ if (std::is_same<TypeParam,float>::value) { \
+ for (size_t i = 0; i < v1.size(); ++i) { \
+ EXPECT_FLOAT_EQ(v1[i], v2[i]); \
+ } \
+ } else if (std::is_same<TypeParam,float>::value) { \
+ for (size_t i = 0; i < v1.size(); ++i) { \
+ EXPECT_DOUBLE_EQ(v1[i], v2[i]); \
+ } \
+ } else { \
+ for (size_t i = 0; i < v1.size(); ++i) { \
+ EXPECT_EQ(v1[i], v2[i]); \
+ } \
+ } \
+} while(0)
+
+//------------------------------------------------------------------------------
+// A macro to help with type comparisons within floating point range.
+#define ASSERT_TYPE_EQ(T1, T2) \
+do { \
+ const decltype(T1) t1 = T1; \
+ const decltype(T2) t2 = T2; \
+ if (std::is_same<TypeParam,float>::value) { \
+ ASSERT_FLOAT_EQ(t1, t2); \
+ } else if (std::is_same<TypeParam,float>::value) { \
+ ASSERT_DOUBLE_EQ(t1, t2); \
+ } else { \
+ ASSERT_EQ(t1, t2); \
+ } \
+} while(0)
+
+//------------------------------------------------------------------------------
+// Test some translation stuff.
+TYPED_TEST(MatTestT, Translation4) {
+ typedef ::android::details::TMat44<TypeParam> M44T;
+ typedef ::android::details::TVec4<TypeParam> V4T;
+
+ V4T translateBy(-7.3, 1.1, 14.4, 0.0);
+ V4T translation(translateBy[0], translateBy[1], translateBy[2], 1.0);
+ M44T translation_matrix = M44T::translate(translation);
+
+ V4T p1(9.9, 3.1, 41.1, 1.0);
+ V4T p2(-18.0, 0.0, 1.77, 1.0);
+ V4T p3(0, 0, 0, 1);
+ V4T p4(-1000, -1000, 1000, 1.0);
+
+ EXPECT_VEC_EQ(translation_matrix * p1, translateBy + p1);
+ EXPECT_VEC_EQ(translation_matrix * p2, translateBy + p2);
+ EXPECT_VEC_EQ(translation_matrix * p3, translateBy + p3);
+ EXPECT_VEC_EQ(translation_matrix * p4, translateBy + p4);
+}
+
+//------------------------------------------------------------------------------
+template <typename MATRIX>
+static void verifyOrthonormal(const MATRIX& A) {
+ typedef typename MATRIX::value_type T;
+
+ static constexpr T value_eps = T(100) * std::numeric_limits<T>::epsilon();
+
+ const MATRIX prod = A * transpose(A);
+ for (size_t i = 0; i < MATRIX::NUM_COLS; ++i) {
+ for (size_t j = 0; j < MATRIX::NUM_ROWS; ++j) {
+ if (i == j) {
+ ASSERT_NEAR(prod[i][j], T(1), value_eps);
+ } else {
+ ASSERT_NEAR(prod[i][j], T(0), value_eps);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Test euler code.
+TYPED_TEST(MatTestT, EulerZYX_44) {
+ typedef ::android::details::TMat44<TypeParam> M44T;
+
+ std::default_random_engine generator(82828);
+ std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M44T m = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ verifyOrthonormal(m);
+ }
+
+ M44T m = M44T::eulerZYX(1, 2, 3);
+ verifyOrthonormal(m);
+}
+
+//------------------------------------------------------------------------------
+// Test euler code.
+TYPED_TEST(MatTestT, EulerZYX_33) {
+
+ typedef ::android::details::TMat33<TypeParam> M33T;
+
+ std::default_random_engine generator(112233);
+ std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M33T m = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ verifyOrthonormal(m);
+ }
+
+ M33T m = M33T::eulerZYX(1, 2, 3);
+ verifyOrthonormal(m);
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPostTranslation) {
+
+ typedef ::android::details::TMat44<TypeParam> M44T;
+ typedef ::android::details::TVec4<TypeParam> V4T;
+ typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+ std::default_random_engine generator(112233);
+ std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ M44T t = M44T::translate(V4T(rand_gen(), rand_gen(), rand_gen(), 1));
+ QuatT qr = r.toQuaternion();
+ M44T tr = t * r;
+ QuatT qtr = tr.toQuaternion();
+
+ ASSERT_TYPE_EQ(qr.x, qtr.x);
+ ASSERT_TYPE_EQ(qr.y, qtr.y);
+ ASSERT_TYPE_EQ(qr.z, qtr.z);
+ ASSERT_TYPE_EQ(qr.w, qtr.w);
+ }
+
+ M44T r = M44T::eulerZYX(1, 2, 3);
+ M44T t = M44T::translate(V4T(20, -15, 2, 1));
+ QuatT qr = r.toQuaternion();
+ M44T tr = t * r;
+ QuatT qtr = tr.toQuaternion();
+
+ ASSERT_TYPE_EQ(qr.x, qtr.x);
+ ASSERT_TYPE_EQ(qr.y, qtr.y);
+ ASSERT_TYPE_EQ(qr.z, qtr.z);
+ ASSERT_TYPE_EQ(qr.w, qtr.w);
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPointTransformation33) {
+ static constexpr TypeParam value_eps =
+ TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon();
+
+ typedef ::android::details::TMat33<TypeParam> M33T;
+ typedef ::android::details::TVec3<TypeParam> V3T;
+ typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+ std::default_random_engine generator(112233);
+ std::uniform_real_distribution<float> distribution(-100.0, 100.0);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M33T r = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ QuatT qr = r.toQuaternion();
+ V3T p(rand_gen(), rand_gen(), rand_gen());
+
+ V3T pr = r * p;
+ V3T pq = qr * p;
+
+ ASSERT_NEAR(pr.x, pq.x, value_eps);
+ ASSERT_NEAR(pr.y, pq.y, value_eps);
+ ASSERT_NEAR(pr.z, pq.z, value_eps);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPointTransformation44) {
+ static constexpr TypeParam value_eps =
+ TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon();
+
+ typedef ::android::details::TMat44<TypeParam> M44T;
+ typedef ::android::details::TVec4<TypeParam> V4T;
+ typedef ::android::details::TVec3<TypeParam> V3T;
+ typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+ std::default_random_engine generator(992626);
+ std::uniform_real_distribution<float> distribution(-100.0, 100.0);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < 100; ++i) {
+ M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+ QuatT qr = r.toQuaternion();
+ V3T p(rand_gen(), rand_gen(), rand_gen());
+
+ V4T pr = r * V4T(p.x, p.y, p.z, 1);
+ pr.x /= pr.w;
+ pr.y /= pr.w;
+ pr.z /= pr.w;
+ V3T pq = qr * p;
+
+ ASSERT_NEAR(pr.x, pq.x, value_eps);
+ ASSERT_NEAR(pr.y, pq.y, value_eps);
+ ASSERT_NEAR(pr.z, pq.z, value_eps);
+ }
+}
+
+#undef TEST_MATRIX_INVERSE
+
+}; // namespace android
diff --git a/libs/math/tests/quat_test.cpp b/libs/math/tests/quat_test.cpp
new file mode 100644
index 0000000000..c20771eed2
--- /dev/null
+++ b/libs/math/tests/quat_test.cpp
@@ -0,0 +1,301 @@
+/*
+ * 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_TAG "QuatTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <random>
+#include <functional>
+
+#include <math/quat.h>
+#include <math/mat4.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class QuatTest : public testing::Test {
+protected:
+};
+
+TEST_F(QuatTest, Basics) {
+ quatd q;
+ double4& v(q.xyzw);
+
+ EXPECT_EQ(sizeof(quatd), sizeof(double)*4);
+ EXPECT_EQ(reinterpret_cast<void*>(&q), reinterpret_cast<void*>(&v));
+}
+
+TEST_F(QuatTest, Constructors) {
+ quatd q0;
+ EXPECT_EQ(q0.x, 0);
+ EXPECT_EQ(q0.y, 0);
+ EXPECT_EQ(q0.z, 0);
+ EXPECT_EQ(q0.w, 0);
+
+ quatd q1(1);
+ EXPECT_EQ(q1.x, 0);
+ EXPECT_EQ(q1.y, 0);
+ EXPECT_EQ(q1.z, 0);
+ EXPECT_EQ(q1.w, 1);
+
+ quatd q2(1, 2, 3, 4);
+ EXPECT_EQ(q2.x, 2);
+ EXPECT_EQ(q2.y, 3);
+ EXPECT_EQ(q2.z, 4);
+ EXPECT_EQ(q2.w, 1);
+
+ quatd q3(q2);
+ EXPECT_EQ(q3.x, 2);
+ EXPECT_EQ(q3.y, 3);
+ EXPECT_EQ(q3.z, 4);
+ EXPECT_EQ(q3.w, 1);
+
+ quatd q4(q3.xyz, 42);
+ EXPECT_EQ(q4.x, 2);
+ EXPECT_EQ(q4.y, 3);
+ EXPECT_EQ(q4.z, 4);
+ EXPECT_EQ(q4.w, 42);
+
+ quatd q5(double3(q2.xy, 42), 24);
+ EXPECT_EQ(q5.x, 2);
+ EXPECT_EQ(q5.y, 3);
+ EXPECT_EQ(q5.z, 42);
+ EXPECT_EQ(q5.w, 24);
+
+ quatd q6;
+ q6 = 12;
+ EXPECT_EQ(q6.x, 0);
+ EXPECT_EQ(q6.y, 0);
+ EXPECT_EQ(q6.z, 0);
+ EXPECT_EQ(q6.w, 12);
+
+ quatd q7 = 1 + 2_id + 3_jd + 4_kd;
+ EXPECT_EQ(q7.x, 2);
+ EXPECT_EQ(q7.y, 3);
+ EXPECT_EQ(q7.z, 4);
+ EXPECT_EQ(q7.w, 1);
+
+ quatf qf(2);
+ EXPECT_EQ(qf.x, 0);
+ EXPECT_EQ(qf.y, 0);
+ EXPECT_EQ(qf.z, 0);
+ EXPECT_EQ(qf.w, 2);
+}
+
+TEST_F(QuatTest, Access) {
+ quatd q0(1, 2, 3, 4);
+ q0.x = 10;
+ q0.y = 20;
+ q0.z = 30;
+ q0.w = 40;
+ EXPECT_EQ(q0.x, 10);
+ EXPECT_EQ(q0.y, 20);
+ EXPECT_EQ(q0.z, 30);
+ EXPECT_EQ(q0.w, 40);
+
+ q0[0] = 100;
+ q0[1] = 200;
+ q0[2] = 300;
+ q0[3] = 400;
+ EXPECT_EQ(q0.x, 100);
+ EXPECT_EQ(q0.y, 200);
+ EXPECT_EQ(q0.z, 300);
+ EXPECT_EQ(q0.w, 400);
+
+ q0.xyz = double3(1, 2, 3);
+ EXPECT_EQ(q0.x, 1);
+ EXPECT_EQ(q0.y, 2);
+ EXPECT_EQ(q0.z, 3);
+ EXPECT_EQ(q0.w, 400);
+}
+
+TEST_F(QuatTest, UnaryOps) {
+ quatd q0(1, 2, 3, 4);
+
+ q0 += 1;
+ EXPECT_EQ(q0.x, 2);
+ EXPECT_EQ(q0.y, 3);
+ EXPECT_EQ(q0.z, 4);
+ EXPECT_EQ(q0.w, 2);
+
+ q0 -= 1;
+ EXPECT_EQ(q0.x, 2);
+ EXPECT_EQ(q0.y, 3);
+ EXPECT_EQ(q0.z, 4);
+ EXPECT_EQ(q0.w, 1);
+
+ q0 *= 2;
+ EXPECT_EQ(q0.x, 4);
+ EXPECT_EQ(q0.y, 6);
+ EXPECT_EQ(q0.z, 8);
+ EXPECT_EQ(q0.w, 2);
+
+ q0 /= 2;
+ EXPECT_EQ(q0.x, 2);
+ EXPECT_EQ(q0.y, 3);
+ EXPECT_EQ(q0.z, 4);
+ EXPECT_EQ(q0.w, 1);
+
+ quatd q1(10, 20, 30, 40);
+
+ q0 += q1;
+ EXPECT_EQ(q0.x, 22);
+ EXPECT_EQ(q0.y, 33);
+ EXPECT_EQ(q0.z, 44);
+ EXPECT_EQ(q0.w, 11);
+
+ q0 -= q1;
+ EXPECT_EQ(q0.x, 2);
+ EXPECT_EQ(q0.y, 3);
+ EXPECT_EQ(q0.z, 4);
+ EXPECT_EQ(q0.w, 1);
+
+ q1 = -q1;
+ EXPECT_EQ(q1.x, -20);
+ EXPECT_EQ(q1.y, -30);
+ EXPECT_EQ(q1.z, -40);
+ EXPECT_EQ(q1.w, -10);
+
+ // TODO(mathias): multiplies
+}
+
+TEST_F(QuatTest, ComparisonOps) {
+ quatd q0(1, 2, 3, 4);
+ quatd q1(10, 20, 30, 40);
+
+ EXPECT_TRUE(q0 == q0);
+ EXPECT_TRUE(q0 != q1);
+ EXPECT_FALSE(q0 != q0);
+ EXPECT_FALSE(q0 == q1);
+}
+
+TEST_F(QuatTest, ArithmeticOps) {
+ quatd q0(1, 2, 3, 4);
+ quatd q1(10, 20, 30, 40);
+
+ quatd q2(q0 + q1);
+ EXPECT_EQ(q2.x, 22);
+ EXPECT_EQ(q2.y, 33);
+ EXPECT_EQ(q2.z, 44);
+ EXPECT_EQ(q2.w, 11);
+
+ q0 = q1 * 2;
+ EXPECT_EQ(q0.x, 40);
+ EXPECT_EQ(q0.y, 60);
+ EXPECT_EQ(q0.z, 80);
+ EXPECT_EQ(q0.w, 20);
+
+ q0 = 2 * q1;
+ EXPECT_EQ(q0.x, 40);
+ EXPECT_EQ(q0.y, 60);
+ EXPECT_EQ(q0.z, 80);
+ EXPECT_EQ(q0.w, 20);
+
+ quatf qf(2);
+ q0 = q1 * qf;
+ EXPECT_EQ(q0.x, 40);
+ EXPECT_EQ(q0.y, 60);
+ EXPECT_EQ(q0.z, 80);
+ EXPECT_EQ(q0.w, 20);
+
+ EXPECT_EQ(1_id * 1_id, quat(-1));
+ EXPECT_EQ(1_jd * 1_jd, quat(-1));
+ EXPECT_EQ(1_kd * 1_kd, quat(-1));
+ EXPECT_EQ(1_id * 1_jd * 1_kd, quat(-1));
+}
+
+TEST_F(QuatTest, ArithmeticFunc) {
+ quatd q(1, 2, 3, 4);
+ quatd qc(conj(q));
+ __attribute__((unused)) quatd qi(inverse(q));
+ quatd qn(normalize(q));
+
+ EXPECT_EQ(qc.x, -2);
+ EXPECT_EQ(qc.y, -3);
+ EXPECT_EQ(qc.z, -4);
+ EXPECT_EQ(qc.w, 1);
+
+ EXPECT_EQ(~q, qc);
+ EXPECT_EQ(length(q), length(qc));
+ EXPECT_EQ(sqrt(30), length(q));
+ EXPECT_FLOAT_EQ(1, length(qn));
+ EXPECT_FLOAT_EQ(1, dot(qn, qn));
+
+ quatd qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2);
+ EXPECT_EQ(mat4d(qr).toQuaternion(), qr);
+ EXPECT_EQ(1_id, mat4d(1_id).toQuaternion());
+ EXPECT_EQ(1_jd, mat4d(1_jd).toQuaternion());
+ EXPECT_EQ(1_kd, mat4d(1_kd).toQuaternion());
+
+
+ EXPECT_EQ(qr, log(exp(qr)));
+
+ quatd qq = qr * qr;
+ quatd q2 = pow(qr, 2);
+ EXPECT_NEAR(qq.x, q2.x, 1e-15);
+ EXPECT_NEAR(qq.y, q2.y, 1e-15);
+ EXPECT_NEAR(qq.z, q2.z, 1e-15);
+ EXPECT_NEAR(qq.w, q2.w, 1e-15);
+
+ quatd qa = quatd::fromAxisAngle(double3(0, 0, 1), 0);
+ quatd qb = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2);
+ quatd qs = slerp(qa, qb, 0.5);
+ qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 4);
+ EXPECT_FLOAT_EQ(qr.x, qs.x);
+ EXPECT_FLOAT_EQ(qr.y, qs.y);
+ EXPECT_FLOAT_EQ(qr.z, qs.z);
+ EXPECT_FLOAT_EQ(qr.w, qs.w);
+
+ qs = nlerp(qa, qb, 0.5);
+ EXPECT_FLOAT_EQ(qr.x, qs.x);
+ EXPECT_FLOAT_EQ(qr.y, qs.y);
+ EXPECT_FLOAT_EQ(qr.z, qs.z);
+ EXPECT_FLOAT_EQ(qr.w, qs.w);
+}
+
+TEST_F(QuatTest, MultiplicationExhaustive) {
+ static constexpr double value_eps = double(1000) * std::numeric_limits<double>::epsilon();
+
+ std::default_random_engine generator(171717);
+ std::uniform_real_distribution<double> distribution(-10.0, 10.0);
+ auto rand_gen = std::bind(distribution, generator);
+
+ for (size_t i = 0; i < (1024 * 1024); ++i) {
+ double3 axis_a = normalize(double3(rand_gen(), rand_gen(), rand_gen()));
+ double angle_a = rand_gen();
+ quatd a = quatd::fromAxisAngle(axis_a, angle_a);
+
+ double3 axis_b = normalize(double3(rand_gen(), rand_gen(), rand_gen()));
+ double angle_b = rand_gen();
+ quatd b = quatd::fromAxisAngle(axis_b, angle_b);
+
+ quatd ab = a * b;
+ quatd ab_other(a.w * b.xyz + b.w * a.xyz + cross(a.xyz, b.xyz),
+ (a.w * b.w) - dot(a.xyz, b.xyz));
+
+ ASSERT_NEAR(ab.x, ab_other.x, value_eps);
+ ASSERT_NEAR(ab.y, ab_other.y, value_eps);
+ ASSERT_NEAR(ab.z, ab_other.z, value_eps);
+ ASSERT_NEAR(ab.w, ab_other.w, value_eps);
+ }
+}
+
+}; // namespace android
diff --git a/libs/ui/tests/vec_test.cpp b/libs/math/tests/vec_test.cpp
index 454c999ec4..79ae2e46f9 100644
--- a/libs/ui/tests/vec_test.cpp
+++ b/libs/math/tests/vec_test.cpp
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-#define LOG_TAG "RegionTest"
+#define LOG_TAG "VecTest"
#include <math.h>
#include <stdlib.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
-#include <ui/vec4.h>
+#include <math/vec4.h>
#include <gtest/gtest.h>
@@ -37,7 +35,7 @@ TEST_F(VecTest, Basics) {
EXPECT_EQ(sizeof(vec4), sizeof(float)*4);
EXPECT_EQ(sizeof(vec3), sizeof(float)*3);
EXPECT_EQ(sizeof(vec2), sizeof(float)*2);
- EXPECT_EQ((void*)&v3, (void*)&v4);
+ EXPECT_EQ(reinterpret_cast<void*>(&v3), reinterpret_cast<void*>(&v4));
}
TEST_F(VecTest, Constructors) {
@@ -53,7 +51,7 @@ TEST_F(VecTest, Constructors) {
EXPECT_EQ(v1.z, 1);
EXPECT_EQ(v1.w, 1);
- vec4 v2(1,2,3,4);
+ vec4 v2(1, 2, 3, 4);
EXPECT_EQ(v2.x, 1);
EXPECT_EQ(v2.y, 2);
EXPECT_EQ(v2.z, 3);
@@ -77,15 +75,16 @@ TEST_F(VecTest, Constructors) {
EXPECT_EQ(v5.z, 42);
EXPECT_EQ(v5.w, 24);
- tvec4<double> vd(2);
- EXPECT_EQ(vd.x, 2);
- EXPECT_EQ(vd.y, 2);
- EXPECT_EQ(vd.z, 2);
- EXPECT_EQ(vd.w, 2);
+ float4 vf(2);
+ EXPECT_EQ(vf.x, 2);
+ EXPECT_EQ(vf.y, 2);
+ EXPECT_EQ(vf.z, 2);
+ EXPECT_EQ(vf.w, 2);
}
TEST_F(VecTest, Access) {
- vec4 v0(1,2,3,4);
+ vec4 v0(1, 2, 3, 4);
+
v0.x = 10;
v0.y = 20;
v0.z = 30;
@@ -104,7 +103,7 @@ TEST_F(VecTest, Access) {
EXPECT_EQ(v0.z, 300);
EXPECT_EQ(v0.w, 400);
- v0.xyz = vec3(1,2,3);
+ v0.xyz = vec3(1, 2, 3);
EXPECT_EQ(v0.x, 1);
EXPECT_EQ(v0.y, 2);
EXPECT_EQ(v0.z, 3);
@@ -112,7 +111,7 @@ TEST_F(VecTest, Access) {
}
TEST_F(VecTest, UnaryOps) {
- vec4 v0(1,2,3,4);
+ vec4 v0(1, 2, 3, 4);
v0 += 1;
EXPECT_EQ(v0.x, 2);
@@ -164,41 +163,23 @@ TEST_F(VecTest, UnaryOps) {
EXPECT_EQ(v0.z, 3);
EXPECT_EQ(v0.w, 4);
- ++v0;
- EXPECT_EQ(v0.x, 2);
- EXPECT_EQ(v0.y, 3);
- EXPECT_EQ(v0.z, 4);
- EXPECT_EQ(v0.w, 5);
-
- ++++v0;
- EXPECT_EQ(v0.x, 4);
- EXPECT_EQ(v0.y, 5);
- EXPECT_EQ(v0.z, 6);
- EXPECT_EQ(v0.w, 7);
-
- --v1;
- EXPECT_EQ(v1.x, 9);
- EXPECT_EQ(v1.y, 19);
- EXPECT_EQ(v1.z, 29);
- EXPECT_EQ(v1.w, 39);
-
v1 = -v1;
+ EXPECT_EQ(v1.x, -10);
+ EXPECT_EQ(v1.y, -20);
+ EXPECT_EQ(v1.z, -30);
+ EXPECT_EQ(v1.w, -40);
+
+ float4 fv(1, 2, 3, 4);
+ v1 += fv;
EXPECT_EQ(v1.x, -9);
- EXPECT_EQ(v1.y, -19);
- EXPECT_EQ(v1.z, -29);
- EXPECT_EQ(v1.w, -39);
-
- tvec4<double> dv(1,2,3,4);
- v1 += dv;
- EXPECT_EQ(v1.x, -8);
- EXPECT_EQ(v1.y, -17);
- EXPECT_EQ(v1.z, -26);
- EXPECT_EQ(v1.w, -35);
+ EXPECT_EQ(v1.y, -18);
+ EXPECT_EQ(v1.z, -27);
+ EXPECT_EQ(v1.w, -36);
}
TEST_F(VecTest, ComparisonOps) {
- vec4 v0(1,2,3,4);
- vec4 v1(10,20,30,40);
+ vec4 v0(1, 2, 3, 4);
+ vec4 v1(10, 20, 30, 40);
EXPECT_TRUE(v0 == v0);
EXPECT_TRUE(v0 != v1);
@@ -206,9 +187,26 @@ TEST_F(VecTest, ComparisonOps) {
EXPECT_FALSE(v0 == v1);
}
+TEST_F(VecTest, ComparisonFunctions) {
+ vec4 v0(1, 2, 3, 4);
+ vec4 v1(10, 20, 30, 40);
+
+ EXPECT_TRUE(all(equal(v0, v0)));
+ EXPECT_TRUE(all(notEqual(v0, v1)));
+ EXPECT_FALSE(any(notEqual(v0, v0)));
+ EXPECT_FALSE(any(equal(v0, v1)));
+
+ EXPECT_FALSE(all(lessThan(v0, v0)));
+ EXPECT_TRUE(all(lessThanEqual(v0, v0)));
+ EXPECT_FALSE(all(greaterThan(v0, v0)));
+ EXPECT_TRUE(all(greaterThanEqual(v0, v0)));
+ EXPECT_TRUE(all(lessThan(v0, v1)));
+ EXPECT_TRUE(all(greaterThan(v1, v0)));
+}
+
TEST_F(VecTest, ArithmeticOps) {
- vec4 v0(1,2,3,4);
- vec4 v1(10,20,30,40);
+ vec4 v0(1, 2, 3, 4);
+ vec4 v1(10, 20, 30, 40);
vec4 v2(v0 + v1);
EXPECT_EQ(v2.x, 11);
@@ -228,8 +226,8 @@ TEST_F(VecTest, ArithmeticOps) {
EXPECT_EQ(v0.z, 60);
EXPECT_EQ(v0.w, 80);
- tvec4<double> vd(2);
- v0 = v1 * vd;
+ float4 vf(2);
+ v0 = v1 * vf;
EXPECT_EQ(v0.x, 20);
EXPECT_EQ(v0.y, 40);
EXPECT_EQ(v0.z, 60);
@@ -239,19 +237,34 @@ TEST_F(VecTest, ArithmeticOps) {
TEST_F(VecTest, ArithmeticFunc) {
vec3 east(1, 0, 0);
vec3 north(0, 1, 0);
- vec3 up( cross(east, north) );
- EXPECT_EQ(up, vec3(0,0,1));
+ vec3 up(cross(east, north));
+ EXPECT_EQ(up, vec3(0, 0, 1));
EXPECT_EQ(dot(east, north), 0);
EXPECT_EQ(length(east), 1);
EXPECT_EQ(distance(east, north), sqrtf(2));
- vec3 v0(1,2,3);
+ vec3 v0(1, 2, 3);
vec3 vn(normalize(v0));
EXPECT_FLOAT_EQ(1, length(vn));
EXPECT_FLOAT_EQ(length(v0), dot(v0, vn));
- tvec3<double> vd(east);
- EXPECT_EQ(length(vd), 1);
+ float3 vf(east);
+ EXPECT_EQ(length(vf), 1);
+
+ EXPECT_TRUE(any(vec3(0, 0, 1)));
+ EXPECT_FALSE(any(vec3(0, 0, 0)));
+
+ EXPECT_TRUE(all(vec3(1, 1, 1)));
+ EXPECT_FALSE(all(vec3(0, 0, 1)));
+
+ EXPECT_TRUE(any(bool3(false, false, true)));
+ EXPECT_FALSE(any(bool3(false)));
+
+ EXPECT_TRUE(all(bool3(true)));
+ EXPECT_FALSE(all(bool3(false, false, true)));
+
+ std::function<bool(float)> p = [](auto v) -> bool { return v > 0.0f; };
+ EXPECT_TRUE(all(map(vec3(1, 2, 3), p)));
}
}; // namespace android
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
new file mode 100644
index 0000000000..ed292e7bae
--- /dev/null
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHardwareBuffer"
+
+#include <vndk/hardware_buffer.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <memory>
+
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#include <ui/GraphicBuffer.h>
+#include <system/graphics.h>
+
+#include <private/android/AHardwareBufferHelpers.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+
+
+static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+// Public functions
+// ----------------------------------------------------------------------------
+
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) {
+ if (!outBuffer || !desc)
+ return BAD_VALUE;
+
+ if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
+ ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format);
+ return BAD_VALUE;
+ }
+
+ int format = AHardwareBuffer_convertToPixelFormat(desc->format);
+ if (desc->rfu0 != 0 || desc->rfu1 != 0) {
+ ALOGE("AHardwareBuffer_Desc::rfu fields must be 0");
+ return BAD_VALUE;
+ }
+
+ if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
+ ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format");
+ return BAD_VALUE;
+ }
+
+ uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+ sp<GraphicBuffer> gbuffer(new GraphicBuffer(
+ desc->width, desc->height, format, desc->layers, usage,
+ std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
+
+ status_t err = gbuffer->initCheck();
+ if (err != 0 || gbuffer->handle == 0) {
+ if (err == NO_MEMORY) {
+ GraphicBuffer::dumpAllocationsToSystemLog();
+ }
+ ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p",
+ desc->width, desc->height, desc->layers, strerror(-err), gbuffer->handle);
+ return err;
+ }
+
+ *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get());
+
+ // Ensure the buffer doesn't get destroyed when the sp<> goes away.
+ AHardwareBuffer_acquire(*outBuffer);
+ return NO_ERROR;
+}
+
+void AHardwareBuffer_acquire(AHardwareBuffer* buffer) {
+ // incStrong/decStrong token must be the same, doesn't matter what it is
+ AHardwareBuffer_to_GraphicBuffer(buffer)->incStrong((void*)AHardwareBuffer_acquire);
+}
+
+void AHardwareBuffer_release(AHardwareBuffer* buffer) {
+ // incStrong/decStrong token must be the same, doesn't matter what it is
+ AHardwareBuffer_to_GraphicBuffer(buffer)->decStrong((void*)AHardwareBuffer_acquire);
+}
+
+void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
+ AHardwareBuffer_Desc* outDesc) {
+ if (!buffer || !outDesc) return;
+
+ const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+ outDesc->width = gbuffer->getWidth();
+ outDesc->height = gbuffer->getHeight();
+ outDesc->layers = gbuffer->getLayerCount();
+ outDesc->format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gbuffer->getPixelFormat()));
+ outDesc->usage = AHardwareBuffer_convertFromGrallocUsageBits(gbuffer->getUsage());
+ outDesc->stride = gbuffer->getStride();
+ outDesc->rfu0 = 0;
+ outDesc->rfu1 = 0;
+}
+
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, void** outVirtualAddress) {
+ if (!buffer) return BAD_VALUE;
+
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+ " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ return BAD_VALUE;
+ }
+
+ usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+ GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+ Rect bounds;
+ if (!rect) {
+ bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
+ } else {
+ bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
+ }
+ return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
+}
+
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
+ if (!buffer) return BAD_VALUE;
+
+ GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+ if (fence == nullptr)
+ return gBuffer->unlock();
+ else
+ return gBuffer->unlockAsync(fence);
+}
+
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) {
+ if (!buffer) return BAD_VALUE;
+ const GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+ size_t flattenedSize = gBuffer->getFlattenedSize();
+ size_t fdCount = gBuffer->getFdCount();
+
+ std::unique_ptr<uint8_t[]> data(new uint8_t[flattenedSize]);
+ std::unique_ptr<int[]> fds(new int[fdCount]);
+
+ // Make copies of needed items since flatten modifies them, and we don't
+ // want to send anything if there's an error during flatten.
+ size_t flattenedSizeCopy = flattenedSize;
+ size_t fdCountCopy = fdCount;
+ void* dataStart = data.get();
+ int* fdsStart = fds.get();
+ status_t err = gBuffer->flatten(dataStart, flattenedSizeCopy, fdsStart,
+ fdCountCopy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ struct iovec iov[1];
+ iov[0].iov_base = data.get();
+ iov[0].iov_len = flattenedSize;
+
+ char buf[CMSG_SPACE(kFdBufferSize)];
+ struct msghdr msg = {
+ .msg_control = buf,
+ .msg_controllen = sizeof(buf),
+ .msg_iov = &iov[0],
+ .msg_iovlen = 1,
+ };
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount);
+ int* fdData = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ memcpy(fdData, fds.get(), sizeof(int) * fdCount);
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ int result;
+ do {
+ result = sendmsg(socketFd, &msg, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == -1) {
+ result = errno;
+ ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)",
+ result, strerror(result));
+ return -result;
+ }
+
+ return NO_ERROR;
+}
+
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) {
+ if (!outBuffer) return BAD_VALUE;
+
+ static constexpr int kMessageBufferSize = 4096 * sizeof(int);
+
+ std::unique_ptr<char[]> dataBuf(new char[kMessageBufferSize]);
+ char fdBuf[CMSG_SPACE(kFdBufferSize)];
+ struct iovec iov[1];
+ iov[0].iov_base = dataBuf.get();
+ iov[0].iov_len = kMessageBufferSize;
+
+ struct msghdr msg = {
+ .msg_control = fdBuf,
+ .msg_controllen = sizeof(fdBuf),
+ .msg_iov = &iov[0],
+ .msg_iovlen = 1,
+ };
+
+ int result;
+ do {
+ result = recvmsg(socketFd, &msg, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == -1) {
+ result = errno;
+ ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)",
+ result, strerror(result));
+ return -result;
+ }
+
+ if (msg.msg_iovlen != 1) {
+ ALOGE("Error reading AHardwareBuffer from socket: bad data length");
+ return INVALID_OPERATION;
+ }
+
+ if (msg.msg_controllen % sizeof(int) != 0) {
+ ALOGE("Error reading AHardwareBuffer from socket: bad fd length");
+ return INVALID_OPERATION;
+ }
+
+ size_t dataLen = msg.msg_iov[0].iov_len;
+ const void* data = static_cast<const void*>(msg.msg_iov[0].iov_base);
+ if (!data) {
+ ALOGE("Error reading AHardwareBuffer from socket: no buffer data");
+ return INVALID_OPERATION;
+ }
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg) {
+ ALOGE("Error reading AHardwareBuffer from socket: no fd header");
+ return INVALID_OPERATION;
+ }
+
+ size_t fdCount = msg.msg_controllen >> 2;
+ const int* fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+ if (!fdData) {
+ ALOGE("Error reading AHardwareBuffer from socket: no fd data");
+ return INVALID_OPERATION;
+ }
+
+ GraphicBuffer* gBuffer = new GraphicBuffer();
+ status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer);
+ // Ensure the buffer has a positive ref-count.
+ AHardwareBuffer_acquire(*outBuffer);
+
+ return NO_ERROR;
+}
+
+
+// ----------------------------------------------------------------------------
+// VNDK functions
+// ----------------------------------------------------------------------------
+
+const native_handle_t* AHardwareBuffer_getNativeHandle(
+ const AHardwareBuffer* buffer) {
+ if (!buffer) return nullptr;
+ const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+ return gbuffer->handle;
+}
+
+
+// ----------------------------------------------------------------------------
+// Helpers implementation
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks.
+struct UsageMaskMapping {
+ uint64_t hardwareBufferMask;
+ uint64_t grallocMask;
+};
+
+static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
+ return (mask & bitsToCheck) == bitsToCheck && bitsToCheck;
+}
+
+bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
+ static_assert(HAL_PIXEL_FORMAT_RGBA_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RGBX_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RGB_565 == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RGB_888 == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RGBA_FP16 == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RGBA_1010102 == AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_BLOB == AHARDWAREBUFFER_FORMAT_BLOB,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_BGRA_8888 == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_YV12 == AHARDWAREBUFFER_FORMAT_YV12,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_Y8 == AHARDWAREBUFFER_FORMAT_Y8,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_Y16 == AHARDWAREBUFFER_FORMAT_Y16,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RAW16 == AHARDWAREBUFFER_FORMAT_RAW16,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RAW10 == AHARDWAREBUFFER_FORMAT_RAW10,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RAW12 == AHARDWAREBUFFER_FORMAT_RAW12,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_RAW_OPAQUE == AHARDWAREBUFFER_FORMAT_RAW_OPAQUE,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED == AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_YCBCR_420_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_YCBCR_422_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_YCBCR_444_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_FLEX_RGB_888 == AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_FLEX_RGBA_8888 == AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_YCBCR_422_SP == AHARDWAREBUFFER_FORMAT_YCbCr_422_SP,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_YCRCB_420_SP == AHARDWAREBUFFER_FORMAT_YCrCb_420_SP,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
+ "HAL and AHardwareBuffer pixel format don't match");
+
+ switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+ case AHARDWAREBUFFER_FORMAT_BLOB:
+ // VNDK formats only -- unfortunately we can't differentiate from where we're called
+ case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_YV12:
+ case AHARDWAREBUFFER_FORMAT_Y8:
+ case AHARDWAREBUFFER_FORMAT_Y16:
+ case AHARDWAREBUFFER_FORMAT_RAW16:
+ case AHARDWAREBUFFER_FORMAT_RAW10:
+ case AHARDWAREBUFFER_FORMAT_RAW12:
+ case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE:
+ case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED:
+ case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
+ case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422:
+ case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444:
+ case AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8:
+ case AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8:
+ case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP:
+ case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP:
+ case AHARDWAREBUFFER_FORMAT_YCbCr_422_I:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) {
+ return hal_format;
+}
+
+uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) {
+ return ahardwarebuffer_format;
+}
+
+uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
+ using android::hardware::graphics::common::V1_0::BufferUsage;
+ static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_RARELY == (uint64_t)BufferUsage::CPU_READ_RARELY,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN == (uint64_t)BufferUsage::CPU_READ_OFTEN,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER == (uint64_t)BufferUsage::CPU_WRITE_NEVER,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY == (uint64_t)BufferUsage::CPU_WRITE_RARELY,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN == (uint64_t)BufferUsage::CPU_WRITE_OFTEN,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_VIDEO_ENCODE == (uint64_t)BufferUsage::VIDEO_ENCODER,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER == (uint64_t)BufferUsage::GPU_DATA_BUFFER,
+ "gralloc and AHardwareBuffer flags don't match");
+ static_assert(AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA == (uint64_t)BufferUsage::SENSOR_DIRECT_DATA,
+ "gralloc and AHardwareBuffer flags don't match");
+ return usage;
+}
+
+uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage) {
+ return usage;
+}
+
+const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) {
+ return reinterpret_cast<const GraphicBuffer*>(buffer);
+}
+
+GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) {
+ return reinterpret_cast<GraphicBuffer*>(buffer);
+}
+
+const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer) {
+ return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer();
+}
+
+ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer) {
+ return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer();
+}
+
+AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) {
+ return reinterpret_cast<AHardwareBuffer*>(buffer);
+}
+
+} // namespace android
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
new file mode 100644
index 0000000000..f64bab13f0
--- /dev/null
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ANativeWindow"
+
+#include <android/native_window.h>
+
+#include <grallocusage/GrallocUsageConversion.h>
+// from nativewindow/includes/system/window.h
+// (not to be confused with the compatibility-only window.h from system/core/includes)
+#include <system/window.h>
+
+#include <private/android/AHardwareBufferHelpers.h>
+
+#include <ui/GraphicBuffer.h>
+
+using namespace android;
+
+static int32_t query(ANativeWindow* window, int what) {
+ int value;
+ int res = window->query(window, what, &value);
+ return res < 0 ? res : value;
+}
+
+/**************************************************************************************************
+ * NDK
+ **************************************************************************************************/
+
+void ANativeWindow_acquire(ANativeWindow* window) {
+ // incStrong/decStrong token must be the same, doesn't matter what it is
+ window->incStrong((void*)ANativeWindow_acquire);
+}
+
+void ANativeWindow_release(ANativeWindow* window) {
+ // incStrong/decStrong token must be the same, doesn't matter what it is
+ window->decStrong((void*)ANativeWindow_acquire);
+}
+
+int32_t ANativeWindow_getWidth(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_WIDTH);
+}
+
+int32_t ANativeWindow_getHeight(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_HEIGHT);
+}
+
+int32_t ANativeWindow_getFormat(ANativeWindow* window) {
+ return query(window, NATIVE_WINDOW_FORMAT);
+}
+
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
+ int32_t width, int32_t height, int32_t format) {
+ int32_t err = native_window_set_buffers_format(window, format);
+ if (!err) {
+ err = native_window_set_buffers_user_dimensions(window, width, height);
+ if (!err) {
+ int mode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+ if (width && height) {
+ mode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+ }
+ err = native_window_set_scaling_mode(window, mode);
+ }
+ }
+ return err;
+}
+
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+ ARect* inOutDirtyBounds) {
+ return window->perform(window, NATIVE_WINDOW_LOCK, outBuffer, inOutDirtyBounds);
+}
+
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) {
+ return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST);
+}
+
+int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform) {
+ static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL == NATIVE_WINDOW_TRANSFORM_FLIP_H);
+ static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL == NATIVE_WINDOW_TRANSFORM_FLIP_V);
+ static_assert(ANATIVEWINDOW_TRANSFORM_ROTATE_90 == NATIVE_WINDOW_TRANSFORM_ROT_90);
+
+ constexpr int32_t kAllTransformBits =
+ ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
+ ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL |
+ ANATIVEWINDOW_TRANSFORM_ROTATE_90;
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID))
+ return -EINVAL;
+ if ((transform & ~kAllTransformBits) != 0)
+ return -EINVAL;
+
+ return native_window_set_buffers_transform(window, transform);
+}
+
+/**************************************************************************************************
+ * vndk-stable
+ **************************************************************************************************/
+
+AHardwareBuffer* ANativeWindowBuffer_getHardwareBuffer(ANativeWindowBuffer* anwb) {
+ return AHardwareBuffer_from_GraphicBuffer(static_cast<GraphicBuffer*>(anwb));
+}
+
+int ANativeWindow_OemStorageSet(ANativeWindow* window, uint32_t slot, intptr_t value) {
+ if (slot < 4) {
+ window->oem[slot] = value;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int ANativeWindow_OemStorageGet(ANativeWindow* window, uint32_t slot, intptr_t* value) {
+ if (slot >= 4) {
+ *value = window->oem[slot];
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+int ANativeWindow_setSwapInterval(ANativeWindow* window, int interval) {
+ return window->setSwapInterval(window, interval);
+}
+
+int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery what, int* value) {
+ switch (what) {
+ case ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS:
+ case ANATIVEWINDOW_QUERY_DEFAULT_WIDTH:
+ case ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT:
+ case ANATIVEWINDOW_QUERY_TRANSFORM_HINT:
+ // these are part of the VNDK API
+ break;
+ case ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL:
+ *value = window->minSwapInterval;
+ return 0;
+ case ANATIVEWINDOW_QUERY_MAX_SWAP_INTERVAL:
+ *value = window->maxSwapInterval;
+ return 0;
+ case ANATIVEWINDOW_QUERY_XDPI:
+ *value = (int)window->xdpi;
+ return 0;
+ case ANATIVEWINDOW_QUERY_YDPI:
+ *value = (int)window->ydpi;
+ return 0;
+ default:
+ // asked for an invalid query(), one that isn't part of the VNDK
+ return -EINVAL;
+ }
+ return window->query(window, int(what), value);
+}
+
+int ANativeWindow_queryf(const ANativeWindow* window, ANativeWindowQuery what, float* value) {
+ switch (what) {
+ case ANATIVEWINDOW_QUERY_XDPI:
+ *value = window->xdpi;
+ return 0;
+ case ANATIVEWINDOW_QUERY_YDPI:
+ *value = window->ydpi;
+ return 0;
+ default:
+ break;
+ }
+
+ int i;
+ int e = ANativeWindow_query(window, what, &i);
+ if (e == 0) {
+ *value = (float)i;
+ }
+ return e;
+}
+
+int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) {
+ return window->dequeueBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ return window->queueBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+ return window->cancelBuffer(window, buffer, fenceFd);
+}
+
+int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage) {
+ usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+ return native_window_set_usage(window, (uint32_t)usage); // FIXME: we need a 64-bits version
+}
+
+int ANativeWindow_setBufferCount(ANativeWindow* window, size_t bufferCount) {
+ return native_window_set_buffer_count(window, bufferCount);
+}
+
+int ANativeWindow_setBuffersDimensions(ANativeWindow* window, uint32_t w, uint32_t h) {
+ return native_window_set_buffers_dimensions(window, (int)w, (int)h);
+}
+
+int ANativeWindow_setBuffersFormat(ANativeWindow* window, int format) {
+ return native_window_set_buffers_format(window, format);
+}
+
+int ANativeWindow_setBuffersTimestamp(ANativeWindow* window, int64_t timestamp) {
+ return native_window_set_buffers_timestamp(window, timestamp);
+}
+
+int ANativeWindow_setBufferDataSpace(ANativeWindow* window, android_dataspace_t dataSpace) {
+ return native_window_set_buffers_data_space(window, dataSpace);
+}
+
+int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMode) {
+ return native_window_set_shared_buffer_mode(window, sharedBufferMode);
+}
+
+int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh) {
+ return native_window_set_auto_refresh(window, autoRefresh);
+}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
new file mode 100644
index 0000000000..d759acbbc1
--- /dev/null
+++ b/libs/nativewindow/Android.bp
@@ -0,0 +1,66 @@
+// Copyright (C) 2017 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.
+
+ndk_headers {
+ name: "libnativewindow_headers",
+ from: "include/android",
+ to: "android",
+ srcs: ["include/android/*.h"],
+ license: "NOTICE",
+}
+
+ndk_library {
+ name: "libnativewindow",
+ symbol_file: "libnativewindow.map.txt",
+
+ // Android O
+ first_version: "26",
+}
+
+cc_library {
+ name: "libnativewindow",
+ export_include_dirs: ["include"],
+
+ clang: true,
+
+ cppflags: [
+ "-std=c++1z"
+ ],
+
+ srcs: [
+ "AHardwareBuffer.cpp",
+ "ANativeWindow.cpp",
+ ],
+
+ shared_libs: [
+ "libhardware",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libui",
+ "android.hardware.graphics.common@1.0",
+ ],
+
+ static_libs: [
+ "libarect",
+ "libgrallocusage",
+ ],
+
+ // headers we include in our public headers
+ export_static_lib_headers: [
+ "libarect",
+ ],
+}
+
+subdirs = ["tests"]
diff --git a/libs/nativewindow/MODULE_LICENSE_APACHE2 b/libs/nativewindow/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/libs/nativewindow/MODULE_LICENSE_APACHE2
diff --git a/libs/nativewindow/NOTICE b/libs/nativewindow/NOTICE
new file mode 100644
index 0000000000..c5b1efa7aa
--- /dev/null
+++ b/libs/nativewindow/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
new file mode 100644
index 0000000000..52440a5b79
--- /dev/null
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2017 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.
+ */
+
+/**
+ * @file hardware_buffer.h
+ */
+
+#ifndef ANDROID_HARDWARE_BUFFER_H
+#define ANDROID_HARDWARE_BUFFER_H
+
+#include <inttypes.h>
+
+#include <sys/cdefs.h>
+
+#include <android/rect.h>
+
+__BEGIN_DECLS
+
+/**
+ * Buffer pixel formats.
+ */
+enum {
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8G8B8A8_UNORM
+ * OpenGL ES: GL_RGBA8
+ */
+ AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8G8B8A8_UNORM
+ * OpenGL ES: GL_RGBA8
+ */
+ AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8G8B8_UNORM
+ * OpenGL ES: GL_RGB8
+ */
+ AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R5G6B5_UNORM_PACK16
+ * OpenGL ES: GL_RGB565
+ */
+ AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = 4,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R16G16B16A16_SFLOAT
+ * OpenGL ES: GL_RGBA16F
+ */
+ AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT = 0x16,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_A2B10G10R10_UNORM_PACK32
+ * OpenGL ES: GL_RGB10_A2
+ */
+ AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 0x2b,
+
+ /**
+ * An opaque binary blob format that must have height 1, with width equal to
+ * the buffer size in bytes.
+ */
+ AHARDWAREBUFFER_FORMAT_BLOB = 0x21,
+};
+
+enum {
+ /* The buffer will never be read by the CPU */
+ AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL,
+ /* The buffer will sometimes be read by the CPU */
+ AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL,
+ /* The buffer will often be read by the CPU */
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL,
+ /* CPU read value mask */
+ AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL,
+
+ /* The buffer will never be written by the CPU */
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4,
+ /* The buffer will sometimes be written to by the CPU */
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4,
+ /* The buffer will often be written to by the CPU */
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 3UL << 4,
+ /* CPU write value mask */
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4,
+
+ /* The buffer will be read from by the GPU */
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8,
+ /* The buffer will be written to by the GPU */
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9,
+ /* The buffer must not be used outside of a protected hardware path */
+ AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14,
+ /* The buffer will be read by a hardware video encoder */
+ AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16,
+ /** The buffer will be used for sensor direct data */
+ AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23,
+ /* The buffer will be used as a shader storage or uniform buffer object*/
+ AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24,
+
+ AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28,
+ AHARDWAREBUFFER_USAGE_VENDOR_1 = 1ULL << 29,
+ AHARDWAREBUFFER_USAGE_VENDOR_2 = 1ULL << 30,
+ AHARDWAREBUFFER_USAGE_VENDOR_3 = 1ULL << 31,
+ AHARDWAREBUFFER_USAGE_VENDOR_4 = 1ULL << 48,
+ AHARDWAREBUFFER_USAGE_VENDOR_5 = 1ULL << 49,
+ AHARDWAREBUFFER_USAGE_VENDOR_6 = 1ULL << 50,
+ AHARDWAREBUFFER_USAGE_VENDOR_7 = 1ULL << 51,
+ AHARDWAREBUFFER_USAGE_VENDOR_8 = 1ULL << 52,
+ AHARDWAREBUFFER_USAGE_VENDOR_9 = 1ULL << 53,
+ AHARDWAREBUFFER_USAGE_VENDOR_10 = 1ULL << 54,
+ AHARDWAREBUFFER_USAGE_VENDOR_11 = 1ULL << 55,
+ AHARDWAREBUFFER_USAGE_VENDOR_12 = 1ULL << 56,
+ AHARDWAREBUFFER_USAGE_VENDOR_13 = 1ULL << 57,
+ AHARDWAREBUFFER_USAGE_VENDOR_14 = 1ULL << 58,
+ AHARDWAREBUFFER_USAGE_VENDOR_15 = 1ULL << 59,
+ AHARDWAREBUFFER_USAGE_VENDOR_16 = 1ULL << 60,
+ AHARDWAREBUFFER_USAGE_VENDOR_17 = 1ULL << 61,
+ AHARDWAREBUFFER_USAGE_VENDOR_18 = 1ULL << 62,
+ AHARDWAREBUFFER_USAGE_VENDOR_19 = 1ULL << 63,
+};
+
+typedef struct AHardwareBuffer_Desc {
+ uint32_t width; // width in pixels
+ uint32_t height; // height in pixels
+ uint32_t layers; // number of images
+ uint32_t format; // One of AHARDWAREBUFFER_FORMAT_*
+ uint64_t usage; // Combination of AHARDWAREBUFFER_USAGE_*
+ uint32_t stride; // Stride in pixels, ignored for AHardwareBuffer_allocate()
+ uint32_t rfu0; // Initialize to zero, reserved for future use
+ uint64_t rfu1; // Initialize to zero, reserved for future use
+} AHardwareBuffer_Desc;
+
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+/**
+ * Allocates a buffer that backs an AHardwareBuffer using the passed
+ * AHardwareBuffer_Desc.
+ *
+ * Returns NO_ERROR on success, or an error number of the allocation fails for
+ * any reason.
+ */
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
+ AHardwareBuffer** outBuffer);
+/**
+ * Acquire a reference on the given AHardwareBuffer object. This prevents the
+ * object from being deleted until the last reference is removed.
+ */
+void AHardwareBuffer_acquire(AHardwareBuffer* buffer);
+
+/**
+ * Remove a reference that was previously acquired with
+ * AHardwareBuffer_acquire().
+ */
+void AHardwareBuffer_release(AHardwareBuffer* buffer);
+
+/**
+ * Return a description of the AHardwareBuffer in the passed
+ * AHardwareBuffer_Desc struct.
+ */
+void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
+ AHardwareBuffer_Desc* outDesc);
+
+/*
+ * Lock the AHardwareBuffer for reading or writing, depending on the usage flags
+ * passed. This call may block if the hardware needs to finish rendering or if
+ * CPU caches need to be synchronized, or possibly for other implementation-
+ * specific reasons. If fence is not negative, then it specifies a fence file
+ * descriptor that will be signaled when the buffer is locked, otherwise the
+ * caller will block until the buffer is available.
+ *
+ * If rect is not NULL, the caller promises to modify only data in the area
+ * specified by rect. If rect is NULL, the caller may modify the contents of the
+ * entire buffer.
+ *
+ * The content of the buffer outside of the specified rect is NOT modified
+ * by this call.
+ *
+ * The buffer usage may only specify AHARDWAREBUFFER_USAGE_CPU_*. If set, then
+ * outVirtualAddress is filled with the address of the buffer in virtual memory,
+ * otherwise this function will fail.
+ *
+ * THREADING CONSIDERATIONS:
+ *
+ * It is legal for several different threads to lock a buffer for read access;
+ * none of the threads are blocked.
+ *
+ * Locking a buffer simultaneously for write or read/write is undefined, but
+ * will neither terminate the process nor block the caller; AHardwareBuffer_lock
+ * may return an error or leave the buffer's content into an indeterminate
+ * state.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL or if the usage
+ * flags are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
+ int32_t fence, const ARect* rect, void** outVirtualAddress);
+
+/*
+ * Unlock the AHardwareBuffer; must be called after all changes to the buffer
+ * are completed by the caller. If fence is not NULL then it will be set to a
+ * file descriptor that is signaled when all pending work on the buffer is
+ * completed. The caller is responsible for closing the fence when it is no
+ * longer needed.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence);
+
+/*
+ * Send the AHardwareBuffer to an AF_UNIX socket.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd);
+
+/*
+ * Receive the AHardwareBuffer from an AF_UNIX socket.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer);
+
+__END_DECLS
+
+#endif // ANDROID_HARDWARE_BUFFER_H
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
new file mode 100644
index 0000000000..5290dd51cd
--- /dev/null
+++ b/libs/nativewindow/include/android/native_window.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file native_window.h
+ * @brief API for accessing a native window.
+ */
+
+#ifndef ANDROID_NATIVE_WINDOW_H
+#define ANDROID_NATIVE_WINDOW_H
+
+#include <sys/cdefs.h>
+
+#include <android/hardware_buffer.h>
+#include <android/rect.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Legacy window pixel format names, kept for backwards compatibility.
+ * New code and APIs should use AHARDWAREBUFFER_FORMAT_*.
+ */
+enum {
+ // NOTE: these values must match the values from graphics/common/x.x/types.hal
+
+ /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
+ WINDOW_FORMAT_RGBA_8888 = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
+ WINDOW_FORMAT_RGBX_8888 = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+ /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
+ WINDOW_FORMAT_RGB_565 = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
+};
+
+/**
+ * Transforms that can be applied to buffers as they are displayed to a window.
+ *
+ * Supported transforms are any combination of horizontal mirror, vertical
+ * mirror, and clockwise 90 degree rotation, in that order. Rotations of 180
+ * and 270 degrees are made up of those basic transforms.
+ */
+enum ANativeWindowTransform {
+ ANATIVEWINDOW_TRANSFORM_IDENTITY = 0x00,
+ ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL = 0x01,
+ ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL = 0x02,
+ ANATIVEWINDOW_TRANSFORM_ROTATE_90 = 0x04,
+
+ ANATIVEWINDOW_TRANSFORM_ROTATE_180 = ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
+ ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL,
+ ANATIVEWINDOW_TRANSFORM_ROTATE_270 = ANATIVEWINDOW_TRANSFORM_ROTATE_180 |
+ ANATIVEWINDOW_TRANSFORM_ROTATE_90,
+};
+
+struct ANativeWindow;
+/**
+ * Opaque type that provides access to a native window.
+ *
+ * A pointer can be obtained using {@link ANativeWindow_fromSurface()}.
+ */
+typedef struct ANativeWindow ANativeWindow;
+
+/**
+ * Struct that represents a windows buffer.
+ *
+ * A pointer can be obtained using {@link ANativeWindow_lock()}.
+ */
+typedef struct ANativeWindow_Buffer {
+ // The number of pixels that are show horizontally.
+ int32_t width;
+
+ // The number of pixels that are shown vertically.
+ int32_t height;
+
+ // The number of *pixels* that a line in the buffer takes in
+ // memory. This may be >= width.
+ int32_t stride;
+
+ // The format of the buffer. One of AHARDWAREBUFFER_FORMAT_*
+ int32_t format;
+
+ // The actual bits.
+ void* bits;
+
+ // Do not touch.
+ uint32_t reserved[6];
+} ANativeWindow_Buffer;
+
+/**
+ * Acquire a reference on the given {@link ANativeWindow} object. This prevents the object
+ * from being deleted until the reference is removed.
+ */
+void ANativeWindow_acquire(ANativeWindow* window);
+
+/**
+ * Remove a reference that was previously acquired with {@link ANativeWindow_acquire()}.
+ */
+void ANativeWindow_release(ANativeWindow* window);
+
+/**
+ * Return the current width in pixels of the window surface.
+ *
+ * \return negative value on error.
+ */
+int32_t ANativeWindow_getWidth(ANativeWindow* window);
+
+/**
+ * Return the current height in pixels of the window surface.
+ *
+ * \return a negative value on error.
+ */
+int32_t ANativeWindow_getHeight(ANativeWindow* window);
+
+/**
+ * Return the current pixel format (AHARDWAREBUFFER_FORMAT_*) of the window surface.
+ *
+ * \return a negative value on error.
+ */
+int32_t ANativeWindow_getFormat(ANativeWindow* window);
+
+/**
+ * Change the format and size of the window buffers.
+ *
+ * The width and height control the number of pixels in the buffers, not the
+ * dimensions of the window on screen. If these are different than the
+ * window's physical size, then its buffer will be scaled to match that size
+ * when compositing it to the screen. The width and height must be either both zero
+ * or both non-zero.
+ *
+ * For all of these parameters, if 0 is supplied then the window's base
+ * value will come back in force.
+ *
+ * \param width width of the buffers in pixels.
+ * \param height height of the buffers in pixels.
+ * \param format one of AHARDWAREBUFFER_FORMAT_* constants.
+ * \return 0 for success, or a negative value on error.
+ */
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
+ int32_t width, int32_t height, int32_t format);
+
+/**
+ * Lock the window's next drawing surface for writing.
+ * inOutDirtyBounds is used as an in/out parameter, upon entering the
+ * function, it contains the dirty region, that is, the region the caller
+ * intends to redraw. When the function returns, inOutDirtyBounds is updated
+ * with the actual area the caller needs to redraw -- this region is often
+ * extended by {@link ANativeWindow_lock}.
+ *
+ * \return 0 for success, or a negative value on error.
+ */
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+ ARect* inOutDirtyBounds);
+
+/**
+ * Unlock the window's drawing surface after previously locking it,
+ * posting the new buffer to the display.
+ *
+ * \return 0 for success, or a negative value on error.
+ */
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+
+/**
+ * Set a transform that will be applied to future buffers posted to the window.
+ *
+ * \param transform combination of {@link ANativeWindowTransform} flags
+ * \return 0 for success, or -EINVAL if \p transform is invalid
+ */
+int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform);
+
+#endif // __ANDROID_API__ >= __ANDROID_API_O__
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_NATIVE_WINDOW_H
+
+/** @} */
diff --git a/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
new file mode 100644
index 0000000000..71f563467d
--- /dev/null
+++ b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
+#define ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
+
+/*
+ * This file contains utility functions related to AHardwareBuffer, mostly to
+ * convert to/from HAL formats.
+ *
+ * These are PRIVATE methods, so this file can NEVER appear in a public NDK
+ * header. They are used by higher level libraries such as core/jni.
+ */
+
+#include <stdint.h>
+
+struct AHardwareBuffer;
+struct ANativeWindowBuffer;
+
+namespace android {
+
+// whether this AHardwareBuffer format is valid
+bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
+
+// convert AHardwareBuffer format to HAL format (note: this is a no-op)
+uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
+
+// convert HAL format to AHardwareBuffer format (note: this is a no-op)
+uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
+
+// convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op)
+uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage);
+
+// convert HAL usage bits to AHardwareBuffer usage bits (note: this is a no-op)
+uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage);
+
+class GraphicBuffer;
+const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer);
+GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer);
+
+const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer);
+ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer);
+
+AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer);
+} // namespace android
+
+#endif // ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
new file mode 100644
index 0000000000..45110c490b
--- /dev/null
+++ b/libs/nativewindow/include/system/window.h
@@ -0,0 +1,972 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/*************************************************************************************************
+ *
+ * IMPORTANT:
+ *
+ * There is an old copy of this file in system/core/include/system/window.h, which exists only
+ * for backward source compatibility.
+ * But there are binaries out there as well, so this version of window.h must stay binary
+ * backward compatible with the one found in system/core.
+ *
+ *
+ * Source compatibility is also required for now, because this is how we're handling the
+ * transition from system/core/include (global include path) to nativewindow/include.
+ *
+ *************************************************************************************************/
+
+#pragma once
+
+#include <cutils/native_handle.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+// system/window.h is a superset of the vndk
+#include <vndk/window.h>
+
+
+#ifndef __UNUSED
+#define __UNUSED __attribute__((__unused__))
+#endif
+#ifndef __deprecated
+#define __deprecated __attribute__((__deprecated__))
+#endif
+
+__BEGIN_DECLS
+
+/*****************************************************************************/
+
+#define ANDROID_NATIVE_WINDOW_MAGIC ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
+
+// ---------------------------------------------------------------------------
+
+typedef const native_handle_t* buffer_handle_t;
+
+// ---------------------------------------------------------------------------
+
+typedef struct android_native_rect_t
+{
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} android_native_rect_t;
+
+// ---------------------------------------------------------------------------
+
+// Old typedef for backwards compatibility.
+typedef ANativeWindowBuffer_t android_native_buffer_t;
+
+// ---------------------------------------------------------------------------
+
+/* attributes queriable with query() */
+enum {
+ NATIVE_WINDOW_WIDTH = 0,
+ NATIVE_WINDOW_HEIGHT = 1,
+ NATIVE_WINDOW_FORMAT = 2,
+
+ /* see ANativeWindowQuery in vndk/window.h */
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS,
+
+ /* Check whether queueBuffer operations on the ANativeWindow send the buffer
+ * to the window compositor. The query sets the returned 'value' argument
+ * to 1 if the ANativeWindow DOES send queued buffers directly to the window
+ * compositor and 0 if the buffers do not go directly to the window
+ * compositor.
+ *
+ * This can be used to determine whether protected buffer content should be
+ * sent to the ANativeWindow. Note, however, that a result of 1 does NOT
+ * indicate that queued buffers will be protected from applications or users
+ * capturing their contents. If that behavior is desired then some other
+ * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in
+ * conjunction with this query.
+ */
+ NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4,
+
+ /* Get the concrete type of a ANativeWindow. See below for the list of
+ * possible return values.
+ *
+ * This query should not be used outside the Android framework and will
+ * likely be removed in the near future.
+ */
+ NATIVE_WINDOW_CONCRETE_TYPE = 5,
+
+
+ /*
+ * Default width and height of ANativeWindow buffers, these are the
+ * dimensions of the window buffers irrespective of the
+ * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window
+ * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS.
+ */
+ NATIVE_WINDOW_DEFAULT_WIDTH = ANATIVEWINDOW_QUERY_DEFAULT_WIDTH,
+ NATIVE_WINDOW_DEFAULT_HEIGHT = ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT,
+
+ /* see ANativeWindowQuery in vndk/window.h */
+ NATIVE_WINDOW_TRANSFORM_HINT = ANATIVEWINDOW_QUERY_TRANSFORM_HINT,
+
+ /*
+ * Boolean that indicates whether the consumer is running more than
+ * one buffer behind the producer.
+ */
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9,
+
+ /*
+ * The consumer gralloc usage bits currently set by the consumer.
+ * The values are defined in hardware/libhardware/include/gralloc.h.
+ */
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10,
+
+ /**
+ * Transformation that will by applied to buffers by the hwcomposer.
+ * This must not be set or checked by producer endpoints, and will
+ * disable the transform hint set in SurfaceFlinger (see
+ * NATIVE_WINDOW_TRANSFORM_HINT).
+ *
+ * INTENDED USE:
+ * Temporary - Please do not use this. This is intended only to be used
+ * by the camera's LEGACY mode.
+ *
+ * In situations where a SurfaceFlinger client wishes to set a transform
+ * that is not visible to the producer, and will always be applied in the
+ * hardware composer, the client can set this flag with
+ * native_window_set_buffers_sticky_transform. This can be used to rotate
+ * and flip buffers consumed by hardware composer without actually changing
+ * the aspect ratio of the buffers produced.
+ */
+ NATIVE_WINDOW_STICKY_TRANSFORM = 11,
+
+ /**
+ * The default data space for the buffers as set by the consumer.
+ * The values are defined in graphics.h.
+ */
+ NATIVE_WINDOW_DEFAULT_DATASPACE = 12,
+
+ /* see ANativeWindowQuery in vndk/window.h */
+ NATIVE_WINDOW_BUFFER_AGE = ANATIVEWINDOW_QUERY_BUFFER_AGE,
+
+ /*
+ * Returns the duration of the last dequeueBuffer call in microseconds
+ */
+ NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
+
+ /*
+ * Returns the duration of the last queueBuffer call in microseconds
+ */
+ NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
+
+ /*
+ * Returns the number of image layers that the ANativeWindow buffer
+ * contains. By default this is 1, unless a buffer is explicitly allocated
+ * to contain multiple layers.
+ */
+ NATIVE_WINDOW_LAYER_COUNT = 16,
+
+ /*
+ * Returns 1 if the native window is valid, 0 otherwise. native window is valid
+ * if it is safe (i.e. no crash will occur) to call any method on it.
+ */
+ NATIVE_WINDOW_IS_VALID = 17,
+
+ /*
+ * Returns 1 if NATIVE_WINDOW_GET_FRAME_TIMESTAMPS will return display
+ * present info, 0 if it won't.
+ */
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18,
+
+ /*
+ * The consumer end is capable of handling protected buffers, i.e. buffer
+ * with GRALLOC_USAGE_PROTECTED usage bits on.
+ */
+ NATIVE_WINDOW_CONSUMER_IS_PROTECTED = 19,
+};
+
+/* Valid operations for the (*perform)() hook.
+ *
+ * Values marked as 'deprecated' are supported, but have been superceded by
+ * other functionality.
+ *
+ * Values marked as 'private' should be considered private to the framework.
+ * HAL implementation code with access to an ANativeWindow should not use these,
+ * as it may not interact properly with the framework's use of the
+ * ANativeWindow.
+ */
+enum {
+// clang-format off
+ NATIVE_WINDOW_SET_USAGE = 0,
+ NATIVE_WINDOW_CONNECT = 1, /* deprecated */
+ NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
+ NATIVE_WINDOW_SET_CROP = 3, /* private */
+ NATIVE_WINDOW_SET_BUFFER_COUNT = 4,
+ NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */
+ NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6,
+ NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7,
+ NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8,
+ NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9,
+ NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */
+ NATIVE_WINDOW_LOCK = 11, /* private */
+ NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */
+ NATIVE_WINDOW_API_CONNECT = 13, /* private */
+ NATIVE_WINDOW_API_DISCONNECT = 14, /* private */
+ NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */
+ NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */
+ NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17,/* private */
+ NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18,
+ NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19,
+ NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */
+ NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21,
+ NATIVE_WINDOW_SET_AUTO_REFRESH = 22,
+ NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION= 23,
+ NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24,
+ NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25,
+ NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26,
+ NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27,
+ NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
+ NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
+// clang-format on
+};
+
+/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
+enum {
+ /* Buffers will be queued by EGL via eglSwapBuffers after being filled using
+ * OpenGL ES.
+ */
+ NATIVE_WINDOW_API_EGL = 1,
+
+ /* Buffers will be queued after being filled using the CPU
+ */
+ NATIVE_WINDOW_API_CPU = 2,
+
+ /* Buffers will be queued by Stagefright after being filled by a video
+ * decoder. The video decoder can either be a software or hardware decoder.
+ */
+ NATIVE_WINDOW_API_MEDIA = 3,
+
+ /* Buffers will be queued by the the camera HAL.
+ */
+ NATIVE_WINDOW_API_CAMERA = 4,
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
+enum {
+ /* flip source image horizontally */
+ NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
+ /* flip source image vertically */
+ NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
+ /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
+ NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
+ /* rotate source image 180 degrees */
+ NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
+ /* rotate source image 270 degrees clock-wise */
+ NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+ /* transforms source by the inverse transform of the screen it is displayed onto. This
+ * transform is applied last */
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08
+};
+
+/* parameter for NATIVE_WINDOW_SET_SCALING_MODE
+ * keep in sync with Surface.java in frameworks/base */
+enum {
+ /* the window content is not updated (frozen) until a buffer of
+ * the window size is received (enqueued)
+ */
+ NATIVE_WINDOW_SCALING_MODE_FREEZE = 0,
+ /* the buffer is scaled in both dimensions to match the window size */
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1,
+ /* the buffer is scaled uniformly such that the smaller dimension
+ * of the buffer matches the window size (cropping in the process)
+ */
+ NATIVE_WINDOW_SCALING_MODE_SCALE_CROP = 2,
+ /* the window is clipped to the size of the buffer's crop rectangle; pixels
+ * outside the crop rectangle are treated as if they are completely
+ * transparent.
+ */
+ NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP = 3,
+};
+
+/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
+enum {
+ NATIVE_WINDOW_FRAMEBUFFER = 0, /* FramebufferNativeWindow */
+ NATIVE_WINDOW_SURFACE = 1, /* Surface */
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+ *
+ * Special timestamp value to indicate that timestamps should be auto-generated
+ * by the native window when queueBuffer is called. This is equal to INT64_MIN,
+ * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
+ */
+static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
+
+/* parameter for NATIVE_WINDOW_GET_FRAME_TIMESTAMPS
+ *
+ * Special timestamp value to indicate the timestamps aren't yet known or
+ * that they are invalid.
+ */
+static const int64_t NATIVE_WINDOW_TIMESTAMP_PENDING = -2;
+static const int64_t NATIVE_WINDOW_TIMESTAMP_INVALID = -1;
+
+struct ANativeWindow
+{
+#ifdef __cplusplus
+ ANativeWindow()
+ : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
+ {
+ common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
+ common.version = sizeof(ANativeWindow);
+ memset(common.reserved, 0, sizeof(common.reserved));
+ }
+
+ /* Implement the methods that sp<ANativeWindow> expects so that it
+ can be used to automatically refcount ANativeWindow's. */
+ void incStrong(const void* /*id*/) const {
+ common.incRef(const_cast<android_native_base_t*>(&common));
+ }
+ void decStrong(const void* /*id*/) const {
+ common.decRef(const_cast<android_native_base_t*>(&common));
+ }
+#endif
+
+ struct android_native_base_t common;
+
+ /* flags describing some attributes of this surface or its updater */
+ const uint32_t flags;
+
+ /* min swap interval supported by this updated */
+ const int minSwapInterval;
+
+ /* max swap interval supported by this updated */
+ const int maxSwapInterval;
+
+ /* horizontal and vertical resolution in DPI */
+ const float xdpi;
+ const float ydpi;
+
+ /* Some storage reserved for the OEM's driver. */
+ intptr_t oem[4];
+
+ /*
+ * Set the swap interval for this surface.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*setSwapInterval)(struct ANativeWindow* window,
+ int interval);
+
+ /*
+ * Hook called by EGL to acquire a buffer. After this call, the buffer
+ * is not locked, so its content cannot be modified. This call may block if
+ * no buffers are available.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new dequeueBuffer function that
+ * outputs a fence file descriptor should be used in its place.
+ */
+ int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer** buffer);
+
+ /*
+ * hook called by EGL to lock a buffer. This MUST be called before modifying
+ * the content of a buffer. The buffer must have been acquired with
+ * dequeueBuffer first.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but it is essentially a no-op, and calls
+ * to it should be removed.
+ */
+ int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * Hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * Buffers MUST be queued in the same order than they were dequeued.
+ *
+ * Returns 0 on success or -errno on error.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new queueBuffer function that
+ * takes a fence file descriptor should be used in its place (pass a value
+ * of -1 for the fence file descriptor if there is no valid one to pass).
+ */
+ int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * hook used to retrieve information about the native window.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*query)(const struct ANativeWindow* window,
+ int what, int* value);
+
+ /*
+ * hook used to perform various operations on the surface.
+ * (*perform)() is a generic mechanism to add functionality to
+ * ANativeWindow while keeping backward binary compatibility.
+ *
+ * DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions
+ * defined below.
+ *
+ * (*perform)() returns -ENOENT if the 'what' parameter is not supported
+ * by the surface's implementation.
+ *
+ * See above for a list of valid operations, such as
+ * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT
+ */
+ int (*perform)(struct ANativeWindow* window,
+ int operation, ... );
+
+ /*
+ * Hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * XXX: This function is deprecated. It will continue to work for some
+ * time for binary compatibility, but the new cancelBuffer function that
+ * takes a fence file descriptor should be used in its place (pass a value
+ * of -1 for the fence file descriptor if there is no valid one to pass).
+ */
+ int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer);
+
+ /*
+ * Hook called by EGL to acquire a buffer. This call may block if no
+ * buffers are available.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The libsync fence file descriptor returned in the int pointed to by the
+ * fenceFd argument will refer to the fence that must signal before the
+ * dequeued buffer may be written to. A value of -1 indicates that the
+ * caller may access the buffer immediately without waiting on a fence. If
+ * a valid file descriptor is returned (i.e. any value except -1) then the
+ * caller is responsible for closing the file descriptor.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*dequeueBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer** buffer, int* fenceFd);
+
+ /*
+ * Hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file descriptor for a
+ * fence that must signal before the buffer can be accessed. If the buffer
+ * can be accessed immediately then a value of -1 should be used. The
+ * caller must not use the file descriptor after it is passed to
+ * queueBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*queueBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer, int fenceFd);
+
+ /*
+ * Hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file decsriptor for a
+ * fence that must signal before the buffer can be accessed. If the buffer
+ * can be accessed immediately then a value of -1 should be used.
+ *
+ * Note that if the client has not waited on the fence that was returned
+ * from dequeueBuffer, that same fence should be passed to cancelBuffer to
+ * ensure that future uses of the buffer are preceded by a wait on that
+ * fence. The caller must not use the file descriptor after it is passed
+ * to cancelBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*cancelBuffer)(struct ANativeWindow* window,
+ struct ANativeWindowBuffer* buffer, int fenceFd);
+};
+
+ /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C).
+ * android_native_window_t is deprecated.
+ */
+typedef struct ANativeWindow ANativeWindow;
+typedef struct ANativeWindow android_native_window_t __deprecated;
+
+/*
+ * native_window_set_usage(..., usage)
+ * Sets the intended usage flags for the next buffers
+ * acquired with (*lockBuffer)() and on.
+ * By default (if this function is never called), a usage of
+ * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
+ * is assumed.
+ * Calling this function will usually cause following buffers to be
+ * reallocated.
+ */
+
+static inline int native_window_set_usage(
+ struct ANativeWindow* window, int usage)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
+}
+
+/* deprecated. Always returns 0. Don't call. */
+static inline int native_window_connect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
+
+static inline int native_window_connect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) {
+ return 0;
+}
+
+/* deprecated. Always returns 0. Don't call. */
+static inline int native_window_disconnect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated;
+
+static inline int native_window_disconnect(
+ struct ANativeWindow* window __UNUSED, int api __UNUSED) {
+ return 0;
+}
+
+/*
+ * native_window_set_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * Depending on the scaling mode, a buffer's crop region is scaled and/or
+ * cropped to match the surface's size. This function sets the crop in
+ * pre-transformed buffer pixel coordinates.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * If 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid, out of the
+ * buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_crop(
+ struct ANativeWindow* window,
+ android_native_rect_t const * crop)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
+}
+
+/*
+ * native_window_set_post_transform_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * Depending on the scaling mode, a buffer's crop region is scaled and/or
+ * cropped to match the surface's size. This function sets the crop in
+ * post-transformed pixel coordinates.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * If 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid, out of the
+ * buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_post_transform_crop(
+ struct ANativeWindow* window,
+ android_native_rect_t const * crop)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop);
+}
+
+/*
+ * native_window_set_active_rect(..., active_rect)
+ *
+ * This function is deprecated and will be removed soon. For now it simply
+ * sets the post-transform crop for compatibility while multi-project commits
+ * get checked.
+ */
+static inline int native_window_set_active_rect(
+ struct ANativeWindow* window,
+ android_native_rect_t const * active_rect) __deprecated;
+
+static inline int native_window_set_active_rect(
+ struct ANativeWindow* window,
+ android_native_rect_t const * active_rect)
+{
+ return native_window_set_post_transform_crop(window, active_rect);
+}
+
+/*
+ * native_window_set_buffer_count(..., count)
+ * Sets the number of buffers associated with this native window.
+ */
+static inline int native_window_set_buffer_count(
+ struct ANativeWindow* window,
+ size_t bufferCount)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
+}
+
+/*
+ * native_window_set_buffers_geometry(..., int w, int h, int format)
+ * All buffers dequeued after this call will have the dimensions and format
+ * specified. A successful call to this function has the same effect as calling
+ * native_window_set_buffers_size and native_window_set_buffers_format.
+ *
+ * XXX: This function is deprecated. The native_window_set_buffers_dimensions
+ * and native_window_set_buffers_format functions should be used instead.
+ */
+static inline int native_window_set_buffers_geometry(
+ struct ANativeWindow* window,
+ int w, int h, int format) __deprecated;
+
+static inline int native_window_set_buffers_geometry(
+ struct ANativeWindow* window,
+ int w, int h, int format)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+ w, h, format);
+}
+
+/*
+ * native_window_set_buffers_dimensions(..., int w, int h)
+ * All buffers dequeued after this call will have the dimensions specified.
+ * In particular, all buffers will have a fixed-size, independent from the
+ * native-window size. They will be scaled according to the scaling mode
+ * (see native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
+ * following this call will be sized to match the window's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+static inline int native_window_set_buffers_dimensions(
+ struct ANativeWindow* window,
+ int w, int h)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS,
+ w, h);
+}
+
+/*
+ * native_window_set_buffers_user_dimensions(..., int w, int h)
+ *
+ * Sets the user buffer size for the window, which overrides the
+ * window's size. All buffers dequeued after this call will have the
+ * dimensions specified unless overridden by
+ * native_window_set_buffers_dimensions. All buffers will have a
+ * fixed-size, independent from the native-window size. They will be
+ * scaled according to the scaling mode (see
+ * native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, the
+ * default buffer size will match the windows's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+static inline int native_window_set_buffers_user_dimensions(
+ struct ANativeWindow* window,
+ int w, int h)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS,
+ w, h);
+}
+
+/*
+ * native_window_set_buffers_format(..., int format)
+ * All buffers dequeued after this call will have the format specified.
+ *
+ * If the specified format is 0, the default buffer format will be used.
+ */
+static inline int native_window_set_buffers_format(
+ struct ANativeWindow* window,
+ int format)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format);
+}
+
+/*
+ * native_window_set_buffers_data_space(..., int dataSpace)
+ * All buffers queued after this call will be associated with the dataSpace
+ * parameter specified.
+ *
+ * dataSpace specifies additional information about the buffer that's dependent
+ * on the buffer format and the endpoints. For example, it can be used to convey
+ * the color space of the image data in the buffer, or it can be used to
+ * indicate that the buffers contain depth measurement data instead of color
+ * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
+ * overridden by the consumer.
+ */
+static inline int native_window_set_buffers_data_space(
+ struct ANativeWindow* window,
+ android_dataspace_t dataSpace)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE,
+ dataSpace);
+}
+
+/*
+ * native_window_set_buffers_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified.
+ */
+static inline int native_window_set_buffers_transform(
+ struct ANativeWindow* window,
+ int transform)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+ transform);
+}
+
+/*
+ * native_window_set_buffers_sticky_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified applied on top of the regular buffer
+ * transform. Setting this transform will disable the transform hint.
+ *
+ * Temporary - This is only intended to be used by the LEGACY camera mode, do
+ * not use this for anything else.
+ */
+static inline int native_window_set_buffers_sticky_transform(
+ struct ANativeWindow* window,
+ int transform)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM,
+ transform);
+}
+
+/*
+ * native_window_set_buffers_timestamp(..., int64_t timestamp)
+ * All buffers queued after this call will be associated with the timestamp
+ * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
+ * (the default), timestamps will be generated automatically when queueBuffer is
+ * called. The timestamp is measured in nanoseconds, and is normally monotonically
+ * increasing. The timestamp should be unaffected by time-of-day adjustments,
+ * and for a camera should be strictly monotonic but for a media player may be
+ * reset when the position is set.
+ */
+static inline int native_window_set_buffers_timestamp(
+ struct ANativeWindow* window,
+ int64_t timestamp)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
+ timestamp);
+}
+
+/*
+ * native_window_set_scaling_mode(..., int mode)
+ * All buffers queued after this call will be associated with the scaling mode
+ * specified.
+ */
+static inline int native_window_set_scaling_mode(
+ struct ANativeWindow* window,
+ int mode)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE,
+ mode);
+}
+
+/*
+ * native_window_api_connect(..., int api)
+ * connects an API to this window. only one API can be connected at a time.
+ * Returns -EINVAL if for some reason the window cannot be connected, which
+ * can happen if it's connected to some other API.
+ */
+static inline int native_window_api_connect(
+ struct ANativeWindow* window, int api)
+{
+ return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
+}
+
+/*
+ * native_window_api_disconnect(..., int api)
+ * disconnect the API from this window.
+ * An error is returned if for instance the window wasn't connected in the
+ * first place.
+ */
+static inline int native_window_api_disconnect(
+ struct ANativeWindow* window, int api)
+{
+ return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api);
+}
+
+/*
+ * native_window_dequeue_buffer_and_wait(...)
+ * Dequeue a buffer and wait on the fence associated with that buffer. The
+ * buffer may safely be accessed immediately upon this function returning. An
+ * error is returned if either of the dequeue or the wait operations fail.
+ */
+static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
+ struct ANativeWindowBuffer** anb) {
+ return anw->dequeueBuffer_DEPRECATED(anw, anb);
+}
+
+/*
+ * native_window_set_sideband_stream(..., native_handle_t*)
+ * Attach a sideband buffer stream to a native window.
+ */
+static inline int native_window_set_sideband_stream(
+ struct ANativeWindow* window,
+ native_handle_t* sidebandHandle)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM,
+ sidebandHandle);
+}
+
+/*
+ * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects)
+ * Set the surface damage (i.e., the region of the surface that has changed
+ * since the previous frame). The damage set by this call will be reset (to the
+ * default of full-surface damage) after calling queue, so this must be called
+ * prior to every frame with damage that does not cover the whole surface if the
+ * caller desires downstream consumers to use this optimization.
+ *
+ * The damage region is specified as an array of rectangles, with the important
+ * caveat that the origin of the surface is considered to be the bottom-left
+ * corner, as in OpenGL ES.
+ *
+ * If numRects is set to 0, rects may be NULL, and the surface damage will be
+ * set to the full surface (the same as if this function had not been called for
+ * this frame).
+ */
+static inline int native_window_set_surface_damage(
+ struct ANativeWindow* window,
+ const android_native_rect_t* rects, size_t numRects)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE,
+ rects, numRects);
+}
+
+/*
+ * native_window_set_shared_buffer_mode(..., bool sharedBufferMode)
+ * Enable/disable shared buffer mode
+ */
+static inline int native_window_set_shared_buffer_mode(
+ struct ANativeWindow* window,
+ bool sharedBufferMode)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE,
+ sharedBufferMode);
+}
+
+/*
+ * native_window_set_auto_refresh(..., autoRefresh)
+ * Enable/disable auto refresh when in shared buffer mode
+ */
+static inline int native_window_set_auto_refresh(
+ struct ANativeWindow* window,
+ bool autoRefresh)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh);
+}
+
+static inline int native_window_get_refresh_cycle_duration(
+ struct ANativeWindow* window,
+ int64_t* outRefreshDuration)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION,
+ outRefreshDuration);
+}
+
+static inline int native_window_get_next_frame_id(
+ struct ANativeWindow* window, uint64_t* frameId)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, frameId);
+}
+
+static inline int native_window_enable_frame_timestamps(
+ struct ANativeWindow* window, bool enable)
+{
+ return window->perform(window, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS,
+ enable);
+}
+
+static inline int native_window_get_compositor_timing(
+ struct ANativeWindow* window,
+ int64_t* compositeDeadline, int64_t* compositeInterval,
+ int64_t* compositeToPresentLatency)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_COMPOSITOR_TIMING,
+ compositeDeadline, compositeInterval, compositeToPresentLatency);
+}
+
+static inline int native_window_get_frame_timestamps(
+ struct ANativeWindow* window, uint64_t frameId,
+ int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
+ int64_t* outLatchTime, int64_t* outFirstRefreshStartTime,
+ int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime,
+ int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime,
+ int64_t* outReleaseTime)
+{
+ return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS,
+ frameId, outRequestedPresentTime, outAcquireTime, outLatchTime,
+ outFirstRefreshStartTime, outLastRefreshStartTime,
+ outGpuCompositionDoneTime, outDisplayPresentTime,
+ outDequeueReadyTime, outReleaseTime);
+}
+
+static inline int native_window_get_wide_color_support(
+ struct ANativeWindow* window, bool* outSupport) {
+ return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT,
+ outSupport);
+}
+
+static inline int native_window_get_hdr_support(struct ANativeWindow* window,
+ bool* outSupport) {
+ return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
+}
+
+__END_DECLS
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
new file mode 100644
index 0000000000..802edcc17d
--- /dev/null
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 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 ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H
+#define ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H
+
+// vndk is a superset of the NDK
+#include <android/hardware_buffer.h>
+
+#include <cutils/native_handle.h>
+
+__BEGIN_DECLS
+
+const native_handle_t* AHardwareBuffer_getNativeHandle(const AHardwareBuffer* buffer);
+
+
+/**
+ * Buffer pixel formats.
+ */
+enum {
+ /* for future proofing, keep these in sync with system/graphics-base.h */
+
+ /* same as HAL_PIXEL_FORMAT_BGRA_8888 */
+ AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM = 5,
+ /* same as HAL_PIXEL_FORMAT_YV12 */
+ AHARDWAREBUFFER_FORMAT_YV12 = 0x32315659,
+ /* same as HAL_PIXEL_FORMAT_Y8 */
+ AHARDWAREBUFFER_FORMAT_Y8 = 0x20203859,
+ /* same as HAL_PIXEL_FORMAT_Y16 */
+ AHARDWAREBUFFER_FORMAT_Y16 = 0x20363159,
+ /* same as HAL_PIXEL_FORMAT_RAW16 */
+ AHARDWAREBUFFER_FORMAT_RAW16 = 0x20,
+ /* same as HAL_PIXEL_FORMAT_RAW10 */
+ AHARDWAREBUFFER_FORMAT_RAW10 = 0x25,
+ /* same as HAL_PIXEL_FORMAT_RAW12 */
+ AHARDWAREBUFFER_FORMAT_RAW12 = 0x26,
+ /* same as HAL_PIXEL_FORMAT_RAW_OPAQUE */
+ AHARDWAREBUFFER_FORMAT_RAW_OPAQUE = 0x24,
+ /* same as HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED */
+ AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
+ /* same as HAL_PIXEL_FORMAT_YCBCR_420_888 */
+ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
+ /* same as HAL_PIXEL_FORMAT_YCBCR_422_888 */
+ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422 = 0x27,
+ /* same as HAL_PIXEL_FORMAT_YCBCR_444_888 */
+ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444 = 0x28,
+ /* same as HAL_PIXEL_FORMAT_FLEX_RGB_888 */
+ AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8 = 0x29,
+ /* same as HAL_PIXEL_FORMAT_FLEX_RGBA_8888 */
+ AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8 = 0x2A,
+ /* same as HAL_PIXEL_FORMAT_YCBCR_422_SP */
+ AHARDWAREBUFFER_FORMAT_YCbCr_422_SP = 0x10,
+ /* same as HAL_PIXEL_FORMAT_YCRCB_420_SP */
+ AHARDWAREBUFFER_FORMAT_YCrCb_420_SP = 0x11,
+ /* same as HAL_PIXEL_FORMAT_YCBCR_422_I */
+ AHARDWAREBUFFER_FORMAT_YCbCr_422_I = 0x14,
+};
+
+__END_DECLS
+
+#endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */
diff --git a/libs/nativewindow/include/vndk/window.h b/libs/nativewindow/include/vndk/window.h
new file mode 100644
index 0000000000..95618c472d
--- /dev/null
+++ b/libs/nativewindow/include/vndk/window.h
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2017 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 ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H
+#define ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <system/graphics-base.h>
+#include <cutils/native_handle.h>
+
+// vndk is a superset of the NDK
+#include <android/native_window.h>
+
+__BEGIN_DECLS
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+#define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast<unsigned int>(x)
+#else
+#define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x))
+#endif
+
+#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \
+ ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \
+ (ANDROID_NATIVE_UNSIGNED_CAST(d)))
+
+#define ANDROID_NATIVE_BUFFER_MAGIC ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r')
+
+
+/*****************************************************************************/
+
+typedef struct android_native_base_t
+{
+ /* a magic value defined by the actual EGL native type */
+ int magic;
+
+ /* the sizeof() of the actual EGL native type */
+ int version;
+
+ void* reserved[4];
+
+ /* reference-counting interface */
+ void (*incRef)(struct android_native_base_t* base);
+ void (*decRef)(struct android_native_base_t* base);
+} android_native_base_t;
+
+typedef struct ANativeWindowBuffer
+{
+#ifdef __cplusplus
+ ANativeWindowBuffer() {
+ common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
+ common.version = sizeof(ANativeWindowBuffer);
+ memset(common.reserved, 0, sizeof(common.reserved));
+ }
+
+ // Implement the methods that sp<ANativeWindowBuffer> expects so that it
+ // can be used to automatically refcount ANativeWindowBuffer's.
+ void incStrong(const void* /*id*/) const {
+ common.incRef(const_cast<android_native_base_t*>(&common));
+ }
+ void decStrong(const void* /*id*/) const {
+ common.decRef(const_cast<android_native_base_t*>(&common));
+ }
+#endif
+
+ struct android_native_base_t common;
+
+ int width;
+ int height;
+ int stride;
+ int format;
+ int usage;
+ uintptr_t layerCount;
+
+ void* reserved[1];
+
+ const native_handle_t* handle;
+
+ void* reserved_proc[8];
+} ANativeWindowBuffer_t;
+
+typedef struct ANativeWindowBuffer ANativeWindowBuffer;
+
+/*
+ * Convert this ANativeWindowBuffer into a AHardwareBuffer
+ */
+AHardwareBuffer* ANativeWindowBuffer_getHardwareBuffer(ANativeWindowBuffer* anwb);
+
+/*****************************************************************************/
+
+/*
+ * Stores a value into one of the 4 available slots
+ * Retrieve the value with ANativeWindow_OemStorageGet()
+ *
+ * slot: 0 to 3
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_OemStorageSet(ANativeWindow* window, uint32_t slot, intptr_t value);
+
+
+/*
+ * Retrieves a value from one of the 4 available slots
+ * By default the returned value is 0 if it wasn't set by ANativeWindow_OemStorageSet()
+ *
+ * slot: 0 to 3
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_OemStorageGet(ANativeWindow* window, uint32_t slot, intptr_t* value);
+
+
+/*
+ * Set the swap interval for this surface.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_setSwapInterval(ANativeWindow* window, int interval);
+
+
+/*
+ * queries that can be used with ANativeWindow_query() and ANativeWindow_queryf()
+ */
+enum ANativeWindowQuery {
+ /* The minimum number of buffers that must remain un-dequeued after a buffer
+ * has been queued. This value applies only if set_buffer_count was used to
+ * override the number of buffers and if a buffer has since been queued.
+ * Users of the set_buffer_count ANativeWindow method should query this
+ * value before calling set_buffer_count. If it is necessary to have N
+ * buffers simultaneously dequeued as part of the steady-state operation,
+ * and this query returns M then N+M buffers should be requested via
+ * native_window_set_buffer_count.
+ *
+ * Note that this value does NOT apply until a single buffer has been
+ * queued. In particular this means that it is possible to:
+ *
+ * 1. Query M = min undequeued buffers
+ * 2. Set the buffer count to N + M
+ * 3. Dequeue all N + M buffers
+ * 4. Cancel M buffers
+ * 5. Queue, dequeue, queue, dequeue, ad infinitum
+ */
+ ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS = 3,
+
+ /*
+ * Default width of ANativeWindow buffers, these are the
+ * dimensions of the window buffers irrespective of the
+ * ANativeWindow_setBuffersDimensions() call and match the native window
+ * size.
+ */
+ ANATIVEWINDOW_QUERY_DEFAULT_WIDTH = 6,
+ ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT = 7,
+
+ /*
+ * transformation that will most-likely be applied to buffers. This is only
+ * a hint, the actual transformation applied might be different.
+ *
+ * INTENDED USE:
+ *
+ * The transform hint can be used by a producer, for instance the GLES
+ * driver, to pre-rotate the rendering such that the final transformation
+ * in the composer is identity. This can be very useful when used in
+ * conjunction with the h/w composer HAL, in situations where it
+ * cannot handle arbitrary rotations.
+ *
+ * 1. Before dequeuing a buffer, the GL driver (or any other ANW client)
+ * queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT.
+ *
+ * 2. The GL driver overrides the width and height of the ANW to
+ * account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying
+ * NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions
+ * according to NATIVE_WINDOW_TRANSFORM_HINT and calling
+ * native_window_set_buffers_dimensions().
+ *
+ * 3. The GL driver dequeues a buffer of the new pre-rotated size.
+ *
+ * 4. The GL driver renders to the buffer such that the image is
+ * already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT
+ * to the rendering.
+ *
+ * 5. The GL driver calls native_window_set_transform to apply
+ * inverse transformation to the buffer it just rendered.
+ * In order to do this, the GL driver needs
+ * to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is
+ * done easily:
+ *
+ * int hintTransform, inverseTransform;
+ * query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform);
+ * inverseTransform = hintTransform;
+ * if (hintTransform & HAL_TRANSFORM_ROT_90)
+ * inverseTransform ^= HAL_TRANSFORM_ROT_180;
+ *
+ *
+ * 6. The GL driver queues the pre-transformed buffer.
+ *
+ * 7. The composer combines the buffer transform with the display
+ * transform. If the buffer transform happens to cancel out the
+ * display transform then no rotation is needed.
+ *
+ */
+ ANATIVEWINDOW_QUERY_TRANSFORM_HINT = 8,
+
+ /*
+ * Returns the age of the contents of the most recently dequeued buffer as
+ * the number of frames that have elapsed since it was last queued. For
+ * example, if the window is double-buffered, the age of any given buffer in
+ * steady state will be 2. If the dequeued buffer has never been queued, its
+ * age will be 0.
+ */
+ ANATIVEWINDOW_QUERY_BUFFER_AGE = 13,
+
+ /* min swap interval supported by this compositor */
+ ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL = 0x10000,
+
+ /* max swap interval supported by this compositor */
+ ANATIVEWINDOW_QUERY_MAX_SWAP_INTERVAL = 0x10001,
+
+ /* horizontal resolution in DPI. value is float, use queryf() */
+ ANATIVEWINDOW_QUERY_XDPI = 0x10002,
+
+ /* vertical resolution in DPI. value is float, use queryf() */
+ ANATIVEWINDOW_QUERY_YDPI = 0x10003,
+};
+
+typedef enum ANativeWindowQuery ANativeWindowQuery;
+
+/*
+ * hook used to retrieve information about the native window.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery query, int* value);
+int ANativeWindow_queryf(const ANativeWindow* window, ANativeWindowQuery query, float* value);
+
+
+/*
+ * Hook called by EGL to acquire a buffer. This call may block if no
+ * buffers are available.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The libsync fence file descriptor returned in the int pointed to by the
+ * fenceFd argument will refer to the fence that must signal before the
+ * dequeued buffer may be written to. A value of -1 indicates that the
+ * caller may access the buffer immediately without waiting on a fence. If
+ * a valid file descriptor is returned (i.e. any value except -1) then the
+ * caller is responsible for closing the file descriptor.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd);
+
+
+/*
+ * Hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file descriptor for a
+ * fence that must signal before the buffer can be accessed. If the buffer
+ * can be accessed immediately then a value of -1 should be used. The
+ * caller must not use the file descriptor after it is passed to
+ * queueBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
+
+/*
+ * Hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ *
+ * The window holds a reference to the buffer between dequeueBuffer and
+ * either queueBuffer or cancelBuffer, so clients only need their own
+ * reference if they might use the buffer after queueing or canceling it.
+ * Holding a reference to a buffer after queueing or canceling it is only
+ * allowed if a specific buffer count has been set.
+ *
+ * The fenceFd argument specifies a libsync fence file decsriptor for a
+ * fence that must signal before the buffer can be accessed. If the buffer
+ * can be accessed immediately then a value of -1 should be used.
+ *
+ * Note that if the client has not waited on the fence that was returned
+ * from dequeueBuffer, that same fence should be passed to cancelBuffer to
+ * ensure that future uses of the buffer are preceded by a wait on that
+ * fence. The caller must not use the file descriptor after it is passed
+ * to cancelBuffer, and the ANativeWindow implementation is responsible for
+ * closing it.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
+/*
+ * Sets the intended usage flags for the next buffers.
+ *
+ * usage: one of AHARDWAREBUFFER_USAGE_* constant
+ *
+ * By default (if this function is never called), a usage of
+ * AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT
+ * is assumed.
+ *
+ * Calling this function will usually cause following buffers to be
+ * reallocated.
+ */
+int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage);
+
+
+/*
+ * Sets the number of buffers associated with this native window.
+ */
+int ANativeWindow_setBufferCount(ANativeWindow* window, size_t bufferCount);
+
+
+/*
+ * All buffers dequeued after this call will have the dimensions specified.
+ * In particular, all buffers will have a fixed-size, independent from the
+ * native-window size. They will be scaled according to the scaling mode
+ * (see native_window_set_scaling_mode) upon window composition.
+ *
+ * If w and h are 0, the normal behavior is restored. That is, dequeued buffers
+ * following this call will be sized to match the window's size.
+ *
+ * Calling this function will reset the window crop to a NULL value, which
+ * disables cropping of the buffers.
+ */
+int ANativeWindow_setBuffersDimensions(ANativeWindow* window, uint32_t w, uint32_t h);
+
+
+/*
+ * All buffers dequeued after this call will have the format specified.
+ * format: one of AHARDWAREBUFFER_FORMAT_* constant
+ *
+ * If the specified format is 0, the default buffer format will be used.
+ */
+int ANativeWindow_setBuffersFormat(ANativeWindow* window, int format);
+
+
+/*
+ * All buffers queued after this call will be associated with the timestamp in nanosecond
+ * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
+ * (the default), timestamps will be generated automatically when queueBuffer is
+ * called. The timestamp is measured in nanoseconds, and is normally monotonically
+ * increasing. The timestamp should be unaffected by time-of-day adjustments,
+ * and for a camera should be strictly monotonic but for a media player may be
+ * reset when the position is set.
+ */
+int ANativeWindow_setBuffersTimestamp(ANativeWindow* window, int64_t timestamp);
+
+
+/*
+ * All buffers queued after this call will be associated with the dataSpace
+ * parameter specified.
+ *
+ * dataSpace specifies additional information about the buffer that's dependent
+ * on the buffer format and the endpoints. For example, it can be used to convey
+ * the color space of the image data in the buffer, or it can be used to
+ * indicate that the buffers contain depth measurement data instead of color
+ * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been
+ * overridden by the consumer.
+ */
+int ANativeWindow_setBufferDataSpace(ANativeWindow* window, android_dataspace_t dataSpace);
+
+
+/*
+ * Enable/disable shared buffer mode
+ */
+int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMode);
+
+
+/*
+ * Enable/disable auto refresh when in shared buffer mode
+ */
+int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh);
+
+
+/*****************************************************************************/
+
+__END_DECLS
+
+#endif /* ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H */
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
new file mode 100644
index 0000000000..b1d1a725a1
--- /dev/null
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -0,0 +1,26 @@
+LIBNATIVEWINDOW {
+ global:
+ AHardwareBuffer_acquire;
+ AHardwareBuffer_allocate;
+ AHardwareBuffer_describe;
+ AHardwareBuffer_fromHardwareBuffer;
+ AHardwareBuffer_lock;
+ AHardwareBuffer_recvHandleFromUnixSocket;
+ AHardwareBuffer_release;
+ AHardwareBuffer_sendHandleToUnixSocket;
+ AHardwareBuffer_toHardwareBuffer;
+ AHardwareBuffer_unlock;
+ ANativeWindow_acquire;
+ ANativeWindow_fromSurface;
+ ANativeWindow_fromSurfaceTexture;
+ ANativeWindow_getFormat;
+ ANativeWindow_getHeight;
+ ANativeWindow_getWidth;
+ ANativeWindow_lock;
+ ANativeWindow_release;
+ ANativeWindow_setBuffersGeometry;
+ ANativeWindow_setBuffersTransform;
+ ANativeWindow_unlockAndPost;
+ local:
+ *;
+};
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
new file mode 100644
index 0000000000..cc2731d908
--- /dev/null
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHardwareBuffer_test"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware_buffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+using android::hardware::graphics::common::V1_0::BufferUsage;
+
+static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected,
+ uint64_t actual, const char* type) {
+ std::ostringstream ss;
+ ss << type << " 0x" << std::hex << actual
+ << " does not match expected " << type << " 0x" << std::hex
+ << expected;
+ return ::testing::AssertionFailure() << ss.str();
+}
+
+static ::testing::AssertionResult TestUsageConversion(
+ uint64_t grallocUsage, uint64_t hardwareBufferUsage) {
+ uint64_t convertedGrallocUsage = AHardwareBuffer_convertToGrallocUsageBits(hardwareBufferUsage);
+ if (convertedGrallocUsage != grallocUsage)
+ return BuildHexFailureMessage(grallocUsage, convertedGrallocUsage, "converToGralloc");
+
+ uint64_t convertedHArdwareBufferUsage = AHardwareBuffer_convertFromGrallocUsageBits(grallocUsage);
+ if (convertedHArdwareBufferUsage != grallocUsage)
+ return BuildHexFailureMessage(grallocUsage, convertedHArdwareBufferUsage, "convertFromGralloc");
+
+ return testing::AssertionSuccess();
+}
+
+// This is a unit test rather than going through AHardwareBuffer because not
+// all flags may be supported by the host device.
+TEST(AHardwareBufferTest, ConvertToAndFromGrallocBits) {
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_READ_RARELY,
+ AHARDWAREBUFFER_USAGE_CPU_READ_RARELY));
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_READ_OFTEN,
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN));
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_WRITE_RARELY,
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY));
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_WRITE_OFTEN,
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_TEXTURE,
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE));
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_RENDER_TARGET,
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT));
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_DATA_BUFFER,
+ AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER));
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::PROTECTED,
+ AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT));
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::SENSOR_DIRECT_DATA,
+ AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA));
+ EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::VIDEO_ENCODER,
+ AHARDWAREBUFFER_USAGE_VIDEO_ENCODE));
+
+ EXPECT_TRUE(TestUsageConversion(1ull<<28, AHARDWAREBUFFER_USAGE_VENDOR_0));
+ EXPECT_TRUE(TestUsageConversion(1ull<<29, AHARDWAREBUFFER_USAGE_VENDOR_1));
+ EXPECT_TRUE(TestUsageConversion(1ull<<30, AHARDWAREBUFFER_USAGE_VENDOR_2));
+ EXPECT_TRUE(TestUsageConversion(1ull<<31, AHARDWAREBUFFER_USAGE_VENDOR_3));
+ EXPECT_TRUE(TestUsageConversion(1ull<<48, AHARDWAREBUFFER_USAGE_VENDOR_4));
+ EXPECT_TRUE(TestUsageConversion(1ull<<49, AHARDWAREBUFFER_USAGE_VENDOR_5));
+ EXPECT_TRUE(TestUsageConversion(1ull<<50, AHARDWAREBUFFER_USAGE_VENDOR_6));
+ EXPECT_TRUE(TestUsageConversion(1ull<<51, AHARDWAREBUFFER_USAGE_VENDOR_7));
+ EXPECT_TRUE(TestUsageConversion(1ull<<52, AHARDWAREBUFFER_USAGE_VENDOR_8));
+ EXPECT_TRUE(TestUsageConversion(1ull<<53, AHARDWAREBUFFER_USAGE_VENDOR_9));
+ EXPECT_TRUE(TestUsageConversion(1ull<<54, AHARDWAREBUFFER_USAGE_VENDOR_10));
+ EXPECT_TRUE(TestUsageConversion(1ull<<55, AHARDWAREBUFFER_USAGE_VENDOR_11));
+ EXPECT_TRUE(TestUsageConversion(1ull<<56, AHARDWAREBUFFER_USAGE_VENDOR_12));
+ EXPECT_TRUE(TestUsageConversion(1ull<<57, AHARDWAREBUFFER_USAGE_VENDOR_13));
+ EXPECT_TRUE(TestUsageConversion(1ull<<58, AHARDWAREBUFFER_USAGE_VENDOR_14));
+ EXPECT_TRUE(TestUsageConversion(1ull<<59, AHARDWAREBUFFER_USAGE_VENDOR_15));
+ EXPECT_TRUE(TestUsageConversion(1ull<<60, AHARDWAREBUFFER_USAGE_VENDOR_16));
+ EXPECT_TRUE(TestUsageConversion(1ull<<61, AHARDWAREBUFFER_USAGE_VENDOR_17));
+ EXPECT_TRUE(TestUsageConversion(1ull<<62, AHARDWAREBUFFER_USAGE_VENDOR_18));
+ EXPECT_TRUE(TestUsageConversion(1ull<<63, AHARDWAREBUFFER_USAGE_VENDOR_19));
+
+ // Test some more complex flag combinations.
+ EXPECT_TRUE(TestUsageConversion(
+ (uint64_t)BufferUsage::CPU_READ_RARELY |
+ (uint64_t)BufferUsage::CPU_WRITE_RARELY,
+ AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY));
+
+EXPECT_TRUE(TestUsageConversion(
+ (uint64_t)BufferUsage::GPU_RENDER_TARGET | (uint64_t)BufferUsage::GPU_TEXTURE |
+ 1ull << 29 | 1ull << 57,
+ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+ AHARDWAREBUFFER_USAGE_VENDOR_1 | AHARDWAREBUFFER_USAGE_VENDOR_13));
+}
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
new file mode 100644
index 0000000000..b89c35ac64
--- /dev/null
+++ b/libs/nativewindow/tests/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2017 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_test {
+ name: "AHardwareBufferTest",
+ shared_libs: [
+ "libnativewindow",
+ "android.hardware.graphics.common@1.0",
+ ],
+ srcs: [
+ "AHardwareBufferTest.cpp",
+ "c_compatibility.c"],
+}
diff --git a/libs/nativewindow/tests/c_compatibility.c b/libs/nativewindow/tests/c_compatibility.c
new file mode 100644
index 0000000000..befd88fd07
--- /dev/null
+++ b/libs/nativewindow/tests/c_compatibility.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 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/hardware_buffer.h>
+#include <android/native_window.h>
+#include <vndk/hardware_buffer.h>
+#include <vndk/window.h>
+
+// this checks that all these headers are C-compatible
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
new file mode 100644
index 0000000000..171a627c7e
--- /dev/null
+++ b/libs/sensor/Android.bp
@@ -0,0 +1,61 @@
+// Copyright 2010 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_library_shared {
+ name: "libsensor",
+
+ clang: true,
+ cppflags: [
+ "-Weverything",
+ "-Werror",
+
+ // The static constructors and destructors in this library have not been noted to
+ // introduce significant overheads
+ "-Wno-exit-time-destructors",
+ "-Wno-global-constructors",
+
+ // We only care about compiling as C++14
+ "-Wno-c++98-compat-pedantic",
+
+ // android/sensors.h uses nested anonymous unions and anonymous structs
+ "-Wno-nested-anon-types",
+ "-Wno-gnu-anonymous-struct",
+
+ // Don't warn about struct padding
+ "-Wno-padded",
+ ],
+
+ srcs: [
+ "BitTube.cpp",
+ "ISensorEventConnection.cpp",
+ "ISensorServer.cpp",
+ "Sensor.cpp",
+ "SensorEventQueue.cpp",
+ "SensorManager.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libcutils",
+ "libutils",
+ "liblog",
+ "libhardware",
+ ],
+
+ export_include_dirs: ["include"],
+
+ export_shared_lib_headers: ["libbinder", "libhardware"],
+}
+
+subdirs = ["tests"]
diff --git a/libs/sensor/BitTube.cpp b/libs/sensor/BitTube.cpp
new file mode 100644
index 0000000000..93555c8a3a
--- /dev/null
+++ b/libs/sensor/BitTube.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 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 <sensor/BitTube.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+// Socket buffer size. The default is typically about 128KB, which is much larger than
+// we really need. So we make it smaller.
+static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
+
+
+BitTube::BitTube()
+ : mSendFd(-1), mReceiveFd(-1)
+{
+ init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
+}
+
+BitTube::BitTube(size_t bufsize)
+ : mSendFd(-1), mReceiveFd(-1)
+{
+ init(bufsize, bufsize);
+}
+
+BitTube::BitTube(const Parcel& data)
+ : mSendFd(-1), mReceiveFd(-1)
+{
+ mReceiveFd = dup(data.readFileDescriptor());
+ if (mReceiveFd < 0) {
+ mReceiveFd = -errno;
+ ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)",
+ strerror(-mReceiveFd));
+ }
+}
+
+BitTube::~BitTube()
+{
+ if (mSendFd >= 0)
+ close(mSendFd);
+
+ if (mReceiveFd >= 0)
+ close(mReceiveFd);
+}
+
+void BitTube::init(size_t rcvbuf, size_t sndbuf) {
+ int sockets[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
+ size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
+ setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+ setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
+ // sine we don't use the "return channel", we keep it small...
+ setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
+ setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
+ fcntl(sockets[0], F_SETFL, O_NONBLOCK);
+ fcntl(sockets[1], F_SETFL, O_NONBLOCK);
+ mReceiveFd = sockets[0];
+ mSendFd = sockets[1];
+ } else {
+ mReceiveFd = -errno;
+ ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
+ }
+}
+
+status_t BitTube::initCheck() const
+{
+ if (mReceiveFd < 0) {
+ return status_t(mReceiveFd);
+ }
+ return NO_ERROR;
+}
+
+int BitTube::getFd() const
+{
+ return mReceiveFd;
+}
+
+int BitTube::getSendFd() const
+{
+ return mSendFd;
+}
+
+ssize_t BitTube::write(void const* vaddr, size_t size)
+{
+ ssize_t err, len;
+ do {
+ len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
+ // cannot return less than size, since we're using SOCK_SEQPACKET
+ err = len < 0 ? errno : 0;
+ } while (err == EINTR);
+ return err == 0 ? len : -err;
+}
+
+ssize_t BitTube::read(void* vaddr, size_t size)
+{
+ ssize_t err, len;
+ do {
+ len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
+ err = len < 0 ? errno : 0;
+ } while (err == EINTR);
+ if (err == EAGAIN || err == EWOULDBLOCK) {
+ // EAGAIN means that we have non-blocking I/O but there was
+ // no data to be read. Nothing the client should care about.
+ return 0;
+ }
+ return err == 0 ? len : -err;
+}
+
+status_t BitTube::writeToParcel(Parcel* reply) const
+{
+ if (mReceiveFd < 0)
+ return -EINVAL;
+
+ status_t result = reply->writeDupFileDescriptor(mReceiveFd);
+ close(mReceiveFd);
+ mReceiveFd = -1;
+ return result;
+}
+
+
+ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
+ void const* events, size_t count, size_t objSize)
+{
+ const char* vaddr = reinterpret_cast<const char*>(events);
+ ssize_t size = tube->write(vaddr, count*objSize);
+
+ // should never happen because of SOCK_SEQPACKET
+ LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
+ "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)",
+ count, objSize, size);
+
+ //ALOGE_IF(size<0, "error %d sending %d events", size, count);
+ return size < 0 ? size : size / static_cast<ssize_t>(objSize);
+}
+
+ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
+ void* events, size_t count, size_t objSize)
+{
+ char* vaddr = reinterpret_cast<char*>(events);
+ ssize_t size = tube->read(vaddr, count*objSize);
+
+ // should never happen because of SOCK_SEQPACKET
+ LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
+ "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)",
+ count, objSize, size);
+
+ //ALOGE_IF(size<0, "error %d receiving %d events", size, count);
+ return size < 0 ? size : size / static_cast<ssize_t>(objSize);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/sensor/ISensorEventConnection.cpp
index 59ecee7501..8a3a623983 100644
--- a/libs/gui/ISensorEventConnection.cpp
+++ b/libs/sensor/ISensorEventConnection.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <sensor/ISensorEventConnection.h>
+
#include <stdint.h>
#include <sys/types.h>
@@ -24,8 +26,7 @@
#include <binder/Parcel.h>
#include <binder/IInterface.h>
-#include <gui/ISensorEventConnection.h>
-#include <gui/BitTube.h>
+#include <sensor/BitTube.h>
namespace android {
// ----------------------------------------------------------------------------
@@ -34,7 +35,8 @@ enum {
GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
ENABLE_DISABLE,
SET_EVENT_RATE,
- FLUSH_SENSOR
+ FLUSH_SENSOR,
+ CONFIGURE_CHANNEL
};
class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
@@ -85,6 +87,15 @@ public:
remote()->transact(FLUSH_SENSOR, data, &reply);
return reply.readInt32();
}
+
+ virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+ data.writeInt32(handle);
+ data.writeInt32(rateLevel);
+ remote()->transact(CONFIGURE_CHANNEL, data, &reply);
+ return reply.readInt32();
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -131,6 +142,15 @@ status_t BnSensorEventConnection::onTransact(
reply->writeInt32(result);
return NO_ERROR;
}
+ case CONFIGURE_CHANNEL: {
+ CHECK_INTERFACE(ISensorEventConnection, data, reply);
+ int handle = data.readInt32();
+ int rateLevel = data.readInt32();
+ status_t result = configureChannel(handle, rateLevel);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 07c507a082..74186dfcb9 100644
--- a/libs/gui/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+#include <sensor/ISensorServer.h>
+
#include <stdint.h>
#include <sys/types.h>
+#include <cutils/native_handle.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
@@ -25,9 +28,8 @@
#include <binder/Parcel.h>
#include <binder/IInterface.h>
-#include <gui/Sensor.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/ISensorEventConnection.h>
namespace android {
// ----------------------------------------------------------------------------
@@ -37,6 +39,8 @@ enum {
CREATE_SENSOR_EVENT_CONNECTION,
ENABLE_DATA_INJECTION,
GET_DYNAMIC_SENSOR_LIST,
+ CREATE_SENSOR_DIRECT_CONNECTION,
+ SET_OPERATION_PARAMETER,
};
class BpSensorServer : public BpInterface<ISensorServer>
@@ -101,6 +105,36 @@ public:
remote()->transact(ENABLE_DATA_INJECTION, data, &reply);
return reply.readInt32();
}
+
+ virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+ uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ data.writeString16(opPackageName);
+ data.writeUint32(size);
+ data.writeInt32(type);
+ data.writeInt32(format);
+ data.writeNativeHandle(resource);
+ remote()->transact(CREATE_SENSOR_DIRECT_CONNECTION, data, &reply);
+ return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
+ }
+
+ virtual int setOperationParameter(
+ int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ data.writeInt32(type);
+ data.writeUint32(static_cast<uint32_t>(floats.size()));
+ for (auto i : floats) {
+ data.writeFloat(i);
+ }
+ data.writeUint32(static_cast<uint32_t>(ints.size()));
+ for (auto i : ints) {
+ data.writeInt32(i);
+ }
+ remote()->transact(SET_OPERATION_PARAMETER, data, &reply);
+ return reply.readInt32();
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -153,6 +187,40 @@ status_t BnSensorServer::onTransact(
}
return NO_ERROR;
}
+ case CREATE_SENSOR_DIRECT_CONNECTION: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ const String16& opPackageName = data.readString16();
+ uint32_t size = data.readUint32();
+ int32_t type = data.readInt32();
+ int32_t format = data.readInt32();
+ native_handle_t *resource = data.readNativeHandle();
+ sp<ISensorEventConnection> ch =
+ createSensorDirectConnection(opPackageName, size, type, format, resource);
+ native_handle_close(resource);
+ native_handle_delete(resource);
+ reply->writeStrongBinder(IInterface::asBinder(ch));
+ return NO_ERROR;
+ }
+ case SET_OPERATION_PARAMETER: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ int32_t type;
+ Vector<float> floats;
+ Vector<int32_t> ints;
+
+ type = data.readInt32();
+ floats.resize(data.readUint32());
+ for (auto &i : floats) {
+ i = data.readFloat();
+ }
+ ints.resize(data.readUint32());
+ for (auto &i : ints) {
+ i = data.readInt32();
+ }
+
+ int32_t ret = setOperationParameter(type, floats, ints);
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/Sensor.cpp b/libs/sensor/Sensor.cpp
index 2c87562bd8..c2d477e4b5 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -14,19 +14,13 @@
* limitations under the License.
*/
+#include <sensor/Sensor.h>
+
#include <inttypes.h>
-#include <stdint.h>
-#include <sys/limits.h>
-#include <sys/types.h>
#include <binder/AppOpsManager.h>
+#include <binder/IPermissionController.h>
#include <binder/IServiceManager.h>
-#include <gui/Sensor.h>
-#include <hardware/sensors.h>
-#include <log/log.h>
-#include <utils/Errors.h>
-#include <utils/String8.h>
-#include <utils/Flattenable.h>
// ----------------------------------------------------------------------------
namespace android {
@@ -51,7 +45,7 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi
mHandle = hwSensor.handle;
mType = hwSensor.type;
mMinValue = 0; // FIXME: minValue
- mMaxValue = hwSensor.maxRange; // FIXME: maxValue
+ mMaxValue = hwSensor.maxRange; // FIXME: maxValue
mResolution = hwSensor.resolution;
mPower = hwSensor.power;
mMinDelay = hwSensor.minDelay;
@@ -210,6 +204,10 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi
mFlags |= SENSOR_FLAG_WAKE_UP;
}
break;
+ case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT:
+ mStringType = SENSOR_STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT;
+ mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+ break;
case SENSOR_TYPE_WRIST_TILT_GESTURE:
mStringType = SENSOR_STRING_TYPE_WRIST_TILT_GESTURE;
mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
@@ -246,6 +244,14 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi
mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
break;
+
+ // TODO: Placeholder for LLOB sensor type
+
+
+ case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED:
+ mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
+ mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+ break;
default:
// Only pipe the stringType, requiredPermission and flags for custom sensors.
if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
@@ -292,7 +298,15 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi
// Feature flags
// Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3.
if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
- mFlags |= (hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK));
+ mFlags |= hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK);
+ }
+ // Set DIRECT_REPORT_MASK and DIRECT_CHANNEL_MASK flags. Compatible with HAL 1_3.
+ if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+ // only on continuous sensors direct report mode is defined
+ if ((mFlags & REPORTING_MODE_MASK) == SENSOR_FLAG_CONTINUOUS_MODE) {
+ mFlags |= hwSensor.flags
+ & (SENSOR_FLAG_MASK_DIRECT_REPORT | SENSOR_FLAG_MASK_DIRECT_CHANNEL);
+ }
}
// Set DATA_INJECTION flag here. Defined in HAL 1_4.
if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) {
@@ -402,6 +416,21 @@ bool Sensor::hasAdditionalInfo() const {
return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0;
}
+int32_t Sensor::getHighestDirectReportRateLevel() const {
+ return ((mFlags & SENSOR_FLAG_MASK_DIRECT_REPORT) >> SENSOR_FLAG_SHIFT_DIRECT_REPORT);
+}
+
+bool Sensor::isDirectChannelTypeSupported(int32_t sharedMemType) const {
+ switch (sharedMemType) {
+ case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+ return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM;
+ case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+ return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC;
+ default:
+ return false;
+ }
+}
+
int32_t Sensor::getReportingMode() const {
return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
}
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 6d698390c1..6f68fb5f7b 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -16,20 +16,17 @@
#define LOG_TAG "Sensors"
+#include <sensor/SensorEventQueue.h>
+
#include <algorithm>
-#include <stdint.h>
-#include <sys/types.h>
#include <sys/socket.h>
-#include <linux/errno.h>
-#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
-#include <gui/Sensor.h>
-#include <gui/BitTube.h>
-#include <gui/SensorEventQueue.h>
-#include <gui/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorEventConnection.h>
#include <android/sensor.h>
@@ -138,7 +135,7 @@ status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
}
status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs,
- int maxBatchReportLatencyUs, int reservedFlags) const {
+ int64_t maxBatchReportLatencyUs, int reservedFlags) const {
return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs),
us2ns(maxBatchReportLatencyUs), reservedFlags);
}
diff --git a/libs/gui/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 57c3073bf4..3fbc5ebba8 100644
--- a/libs/gui/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -16,9 +16,12 @@
#define LOG_TAG "Sensors"
+#include <sensor/SensorManager.h>
+
#include <stdint.h>
#include <sys/types.h>
+#include <cutils/native_handle.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
@@ -26,24 +29,24 @@
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-#include <gui/Sensor.h>
-#include <gui/SensorManager.h>
-#include <gui/SensorEventQueue.h>
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
-android::Mutex android::SensorManager::sLock;
-std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
+Mutex SensorManager::sLock;
+std::map<String16, SensorManager*> SensorManager::sPackageInstances;
SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) {
+ waitForSensorService(nullptr);
+
Mutex::Autolock _l(sLock);
SensorManager* sensorManager;
- std::map<String16, SensorManager*>::iterator iterator =
- sPackageInstances.find(packageName);
+ auto iterator = sPackageInstances.find(packageName);
if (iterator != sPackageInstances.end()) {
sensorManager = iterator->second;
@@ -89,7 +92,7 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName)
}
SensorManager::SensorManager(const String16& opPackageName)
- : mSensorList(0), mOpPackageName(opPackageName) {
+ : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
// okay we're not locked here, but it's not needed during construction
assertStateLocked();
}
@@ -98,6 +101,28 @@ SensorManager::~SensorManager() {
free(mSensorList);
}
+status_t SensorManager::waitForSensorService(sp<ISensorServer> *server) {
+ // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ...
+ sp<ISensorServer> s;
+ const String16 name("sensorservice");
+ for (int i = 0; i < 60; i++) {
+ status_t err = getService(name, &s);
+ switch (err) {
+ case NAME_NOT_FOUND:
+ sleep(1);
+ continue;
+ case NO_ERROR:
+ if (server != nullptr) {
+ *server = s;
+ }
+ return NO_ERROR;
+ default:
+ return err;
+ }
+ }
+ return TIMED_OUT;
+}
+
void SensorManager::sensorManagerDied() {
Mutex::Autolock _l(mLock);
mSensorServer.clear();
@@ -118,19 +143,8 @@ status_t SensorManager::assertStateLocked() {
}
}
if (initSensorManager) {
- // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ...
- const String16 name("sensorservice");
- for (int i = 0; i < 60; i++) {
- status_t err = getService(name, &mSensorServer);
- if (err == NAME_NOT_FOUND) {
- sleep(1);
- continue;
- }
- if (err != NO_ERROR) {
- return err;
- }
- break;
- }
+ waitForSensorService(&mSensorServer);
+ LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL");
class DeathObserver : public IBinder::DeathRecipient {
SensorManager& mSensorManager;
@@ -142,8 +156,6 @@ status_t SensorManager::assertStateLocked() {
explicit DeathObserver(SensorManager& mgr) : mSensorManager(mgr) { }
};
- LOG_ALWAYS_FATAL_IF(mSensorServer.get() == NULL, "getService(SensorService) NULL");
-
mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this));
IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver);
@@ -195,7 +207,8 @@ Sensor const* SensorManager::getDefaultSensor(int type)
if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION ||
type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
- type == SENSOR_TYPE_WRIST_TILT_GESTURE) {
+ type == SENSOR_TYPE_WRIST_TILT_GESTURE ||
+ type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) {
wakeUpSensor = true;
}
// For now we just return the first sensor of that type we find.
@@ -237,5 +250,68 @@ bool SensorManager::isDataInjectionEnabled() {
return false;
}
+int SensorManager::createDirectChannel(
+ size_t size, int channelType, const native_handle_t *resourceHandle) {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() != NO_ERROR) {
+ return NO_INIT;
+ }
+
+ if (channelType != SENSOR_DIRECT_MEM_TYPE_ASHMEM
+ && channelType != SENSOR_DIRECT_MEM_TYPE_GRALLOC) {
+ ALOGE("Bad channel shared memory type %d", channelType);
+ return BAD_VALUE;
+ }
+
+ sp<ISensorEventConnection> conn =
+ mSensorServer->createSensorDirectConnection(mOpPackageName,
+ static_cast<uint32_t>(size),
+ static_cast<int32_t>(channelType),
+ SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle);
+ if (conn == nullptr) {
+ return NO_MEMORY;
+ }
+
+ int nativeHandle = mDirectConnectionHandle++;
+ mDirectConnection.emplace(nativeHandle, conn);
+ return nativeHandle;
+}
+
+void SensorManager::destroyDirectChannel(int channelNativeHandle) {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() == NO_ERROR) {
+ mDirectConnection.erase(channelNativeHandle);
+ }
+}
+
+int SensorManager::configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel) {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() != NO_ERROR) {
+ return NO_INIT;
+ }
+
+ auto i = mDirectConnection.find(channelNativeHandle);
+ if (i == mDirectConnection.end()) {
+ ALOGE("Cannot find the handle in client direct connection table");
+ return BAD_VALUE;
+ }
+
+ int ret;
+ ret = i->second->configureChannel(sensorHandle, rateLevel);
+ ALOGE_IF(ret < 0, "SensorManager::configureChannel (%d, %d) returns %d",
+ static_cast<int>(sensorHandle), static_cast<int>(rateLevel),
+ static_cast<int>(ret));
+ return ret;
+}
+
+int SensorManager::setOperationParameter(
+ int type, const Vector<float> &floats, const Vector<int32_t> &ints) {
+ Mutex::Autolock _l(mLock);
+ if (assertStateLocked() != NO_ERROR) {
+ return NO_INIT;
+ }
+ return mSensorServer->setOperationParameter(type, floats, ints);
+}
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/sensor/include/sensor/BitTube.h b/libs/sensor/include/sensor/BitTube.h
new file mode 100644
index 0000000000..c1fabe83ed
--- /dev/null
+++ b/libs/sensor/include/sensor/BitTube.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+class Parcel;
+
+class BitTube : public RefBase
+{
+public:
+
+ // creates a BitTube with a default (4KB) send buffer
+ BitTube();
+
+ // creates a BitTube with a a specified send and receive buffer size
+ explicit BitTube(size_t bufsize);
+
+ explicit BitTube(const Parcel& data);
+ virtual ~BitTube();
+
+ // check state after construction
+ status_t initCheck() const;
+
+ // get receive file-descriptor
+ int getFd() const;
+
+ // get the send file-descriptor.
+ int getSendFd() const;
+
+ // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
+ template <typename T>
+ static ssize_t sendObjects(const sp<BitTube>& tube,
+ T const* events, size_t count) {
+ return sendObjects(tube, events, count, sizeof(T));
+ }
+
+ // receive objects (sized blobs). If the receiving buffer isn't large enough,
+ // excess messages are silently discarded.
+ template <typename T>
+ static ssize_t recvObjects(const sp<BitTube>& tube,
+ T* events, size_t count) {
+ return recvObjects(tube, events, count, sizeof(T));
+ }
+
+ // parcels this BitTube
+ status_t writeToParcel(Parcel* reply) const;
+
+private:
+ void init(size_t rcvbuf, size_t sndbuf);
+
+ // send a message. The write is guaranteed to send the whole message or fail.
+ ssize_t write(void const* vaddr, size_t size);
+
+ // receive a message. the passed buffer must be at least as large as the
+ // write call used to send the message, excess data is silently discarded.
+ ssize_t read(void* vaddr, size_t size);
+
+ int mSendFd;
+ mutable int mReceiveFd;
+
+ static ssize_t sendObjects(const sp<BitTube>& tube,
+ void const* events, size_t count, size_t objSize);
+
+ static ssize_t recvObjects(const sp<BitTube>& tube,
+ void* events, size_t count, size_t objSize);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/ISensorEventConnection.h b/libs/sensor/include/sensor/ISensorEventConnection.h
new file mode 100644
index 0000000000..07cc7e84ad
--- /dev/null
+++ b/libs/sensor/include/sensor/ISensorEventConnection.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class BitTube;
+class Parcel;
+
+class ISensorEventConnection : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(SensorEventConnection)
+
+ virtual sp<BitTube> getSensorChannel() const = 0;
+ virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
+ nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0;
+ virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
+ virtual status_t flush() = 0;
+ virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorEventConnection : public BnInterface<ISensorEventConnection>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
new file mode 100644
index 0000000000..8d5006258f
--- /dev/null
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include <binder/IInterface.h>
+
+struct native_handle;
+typedef struct native_handle native_handle_t;
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorEventConnection;
+class Parcel;
+class Sensor;
+class String8;
+class String16;
+
+class ISensorServer : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(SensorServer)
+
+ virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
+ virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
+
+ virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
+ int mode, const String16& opPackageName) = 0;
+ virtual int32_t isDataInjectionEnabled() = 0;
+
+ virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+ uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0;
+
+ virtual int setOperationParameter(
+ int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorServer : public BnInterface<ISensorServer>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h
new file mode 100644
index 0000000000..043e6352a7
--- /dev/null
+++ b/libs/sensor/include/sensor/Sensor.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Flattenable.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+// FIXME: including from android/ breaks layering, as libandroid ultimately depends on libsensors
+#include <android/sensor.h>
+
+#include <hardware/sensors.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensor { };
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class Parcel;
+
+// ----------------------------------------------------------------------------
+
+class Sensor : public ASensor, public LightFlattenable<Sensor>
+{
+public:
+ enum {
+ TYPE_ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER,
+ TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
+ TYPE_GYROSCOPE = ASENSOR_TYPE_GYROSCOPE,
+ TYPE_LIGHT = ASENSOR_TYPE_LIGHT,
+ TYPE_PROXIMITY = ASENSOR_TYPE_PROXIMITY
+ };
+
+ struct uuid_t{
+ union {
+ uint8_t b[16];
+ int64_t i64[2];
+ };
+ uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));}
+ uuid_t() : b{0} {}
+ };
+
+ Sensor(const Sensor&) = default;
+ Sensor& operator=(const Sensor&) = default;
+
+ Sensor(const char * name = "");
+ Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
+ Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0);
+ ~Sensor();
+
+ const String8& getName() const;
+ const String8& getVendor() const;
+ int32_t getHandle() const;
+ int32_t getType() const;
+ float getMinValue() const;
+ float getMaxValue() const;
+ float getResolution() const;
+ float getPowerUsage() const;
+ int32_t getMinDelay() const;
+ nsecs_t getMinDelayNs() const;
+ int32_t getVersion() const;
+ uint32_t getFifoReservedEventCount() const;
+ uint32_t getFifoMaxEventCount() const;
+ const String8& getStringType() const;
+ const String8& getRequiredPermission() const;
+ bool isRequiredPermissionRuntime() const;
+ int32_t getRequiredAppOp() const;
+ int32_t getMaxDelay() const;
+ uint32_t getFlags() const;
+ bool isWakeUpSensor() const;
+ bool isDynamicSensor() const;
+ bool hasAdditionalInfo() const;
+ int32_t getHighestDirectReportRateLevel() const;
+ bool isDirectChannelTypeSupported(int32_t sharedMemType) const;
+ int32_t getReportingMode() const;
+
+ // Note that after setId() has been called, getUuid() no longer
+ // returns the UUID.
+ // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
+ // make sure setId() doesn't change the UuidIndex.
+ const uuid_t& getUuid() const;
+ int32_t getId() const;
+ void setId(int32_t id);
+
+ // LightFlattenable protocol
+ inline bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+private:
+ String8 mName;
+ String8 mVendor;
+ int32_t mHandle;
+ int32_t mType;
+ float mMinValue;
+ float mMaxValue;
+ float mResolution;
+ float mPower;
+ int32_t mMinDelay;
+ int32_t mVersion;
+ uint32_t mFifoReservedEventCount;
+ uint32_t mFifoMaxEventCount;
+ String8 mStringType;
+ String8 mRequiredPermission;
+ bool mRequiredPermissionRuntime = false;
+ int32_t mRequiredAppOp;
+ int32_t mMaxDelay;
+ uint32_t mFlags;
+ // TODO(b/29547335): Get rid of this field and replace with an index.
+ // The index will be into a separate global vector of UUIDs.
+ // Also add an mId field (and change flatten/unflatten appropriately).
+ uuid_t mUuid;
+ static void flattenString8(void*& buffer, size_t& size, const String8& string8);
+ static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
new file mode 100644
index 0000000000..baed2ee20d
--- /dev/null
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Mutex.h>
+
+#include <sensor/BitTube.h>
+
+// ----------------------------------------------------------------------------
+#define WAKE_UP_SENSOR_EVENT_NEEDS_ACK (1U << 31)
+struct ALooper;
+struct ASensorEvent;
+
+// Concrete types for the NDK
+struct ASensorEventQueue {
+ ALooper* looper;
+};
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorEventConnection;
+class Sensor;
+class Looper;
+
+// ----------------------------------------------------------------------------
+
+class SensorEventQueue : public ASensorEventQueue, public RefBase
+{
+public:
+
+ enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 };
+
+ /**
+ * Typical sensor delay (sample period) in microseconds.
+ */
+ // Fastest sampling, system will bound it to minDelay
+ static constexpr int32_t SENSOR_DELAY_FASTEST = 0;
+ // Typical sample period for game, 50Hz;
+ static constexpr int32_t SENSOR_DELAY_GAME = 20000;
+ // Typical sample period for UI, 15Hz
+ static constexpr int32_t SENSOR_DELAY_UI = 66667;
+ // Default sensor sample period
+ static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
+
+ SensorEventQueue(const sp<ISensorEventConnection>& connection);
+ virtual ~SensorEventQueue();
+ virtual void onFirstRef();
+
+ int getFd() const;
+
+ static ssize_t write(const sp<BitTube>& tube,
+ ASensorEvent const* events, size_t numEvents);
+
+ ssize_t read(ASensorEvent* events, size_t numEvents);
+
+ status_t waitForEvent() const;
+ status_t wake() const;
+
+ status_t enableSensor(Sensor const* sensor) const;
+ status_t enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const;
+ status_t disableSensor(Sensor const* sensor) const;
+ status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
+
+ // these are here only to support SensorManager.java and HIDL Frameworks SensorManager.
+ status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs,
+ int reservedFlags) const;
+ status_t disableSensor(int32_t handle) const;
+ status_t flush() const;
+ // Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK.
+ void sendAck(const ASensorEvent* events, int count);
+
+ status_t injectSensorEvent(const ASensorEvent& event);
+private:
+ sp<Looper> getLooper() const;
+ sp<ISensorEventConnection> mSensorEventConnection;
+ sp<BitTube> mSensorChannel;
+ mutable Mutex mLock;
+ mutable sp<Looper> mLooper;
+ ASensorEvent* mRecBuffer;
+ size_t mAvailable;
+ size_t mConsumed;
+ uint32_t mNumAcksToSend;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
new file mode 100644
index 0000000000..5fc85d329b
--- /dev/null
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 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 ANDROID_GUI_SENSOR_MANAGER_H
+#define ANDROID_GUI_SENSOR_MANAGER_H
+
+#include <map>
+#include <unordered_map>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+#include <utils/String8.h>
+
+#include <sensor/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensorManager { };
+
+struct native_handle;
+typedef struct native_handle native_handle_t;
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorServer;
+class Sensor;
+class SensorEventQueue;
+// ----------------------------------------------------------------------------
+
+class SensorManager : public ASensorManager
+{
+public:
+ static SensorManager& getInstanceForPackage(const String16& packageName);
+ ~SensorManager();
+
+ ssize_t getSensorList(Sensor const* const** list);
+ ssize_t getDynamicSensorList(Vector<Sensor>& list);
+ Sensor const* getDefaultSensor(int type);
+ sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
+ bool isDataInjectionEnabled();
+ int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
+ void destroyDirectChannel(int channelNativeHandle);
+ int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel);
+ int setOperationParameter(int type, const Vector<float> &floats, const Vector<int32_t> &ints);
+
+private:
+ // DeathRecipient interface
+ void sensorManagerDied();
+ static status_t waitForSensorService(sp<ISensorServer> *server);
+
+ SensorManager(const String16& opPackageName);
+ status_t assertStateLocked();
+
+private:
+ static Mutex sLock;
+ static std::map<String16, SensorManager*> sPackageInstances;
+
+ Mutex mLock;
+ sp<ISensorServer> mSensorServer;
+ Sensor const** mSensorList;
+ Vector<Sensor> mSensors;
+ sp<IBinder::DeathRecipient> mDeathObserver;
+ const String16 mOpPackageName;
+ std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
+ int32_t mDirectConnectionHandle;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SENSOR_MANAGER_H
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
new file mode 100644
index 0000000000..9d530fc5fa
--- /dev/null
+++ b/libs/sensor/tests/Android.bp
@@ -0,0 +1,29 @@
+// Copyright 2017 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_test {
+ name: "libsensor_test",
+
+ clang: true,
+
+ srcs: [
+ "Sensor_test.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libsensor",
+ "libutils",
+ ],
+}
diff --git a/libs/gui/tests/Sensor_test.cpp b/libs/sensor/tests/Sensor_test.cpp
index fbf282d1cf..ede20c93f4 100644
--- a/libs/gui/tests/Sensor_test.cpp
+++ b/libs/sensor/tests/Sensor_test.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "Sensor_test"
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
#include <hardware/sensors.h>
#include <utils/Errors.h>
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d5ff75325b..5edd664345 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -14,6 +14,7 @@
cc_library_shared {
name: "libui",
+ vendor_available: true,
clang: true,
cppflags: [
@@ -28,6 +29,9 @@ cc_library_shared {
// We only care about compiling as C++14
"-Wno-c++98-compat-pedantic",
+ // We are aware of the risks inherent in comparing floats for equality
+ "-Wno-float-equal",
+
// We use four-character constants for the GraphicBuffer header, and don't care
// that they're non-portable as long as they're consistent within one execution
"-Wno-four-char-constants",
@@ -41,13 +45,16 @@ cc_library_shared {
},
srcs: [
+ "ColorSpace.cpp",
+ "DebugUtils.cpp",
"Fence.cpp",
+ "FenceTime.cpp",
"FrameStats.cpp",
- "Gralloc1.cpp",
- "Gralloc1On0Adapter.cpp",
+ "Gralloc2.cpp",
"GraphicBuffer.cpp",
"GraphicBufferAllocator.cpp",
"GraphicBufferMapper.cpp",
+ "GraphicsEnv.cpp",
"HdrCapabilities.cpp",
"PixelFormat.cpp",
"Rect.cpp",
@@ -55,14 +62,37 @@ cc_library_shared {
"UiConfig.cpp",
],
+ include_dirs: [
+ "frameworks/native/include",
+ ],
+
shared_libs: [
- "libbinder",
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore-utils",
+ "libbase",
+ "libnativeloader",
"libcutils",
"libhardware",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
"libsync",
"libutils",
"liblog",
],
+
+ static_libs: [
+ "libarect",
+ "libgrallocusage",
+ "libmath",
+ ],
+
+ export_static_lib_headers: [
+ "libarect",
+ "libmath",
+ ],
}
subdirs = ["tests"]
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
new file mode 100644
index 0000000000..5b4bf2353e
--- /dev/null
+++ b/libs/ui/ColorSpace.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/ColorSpace.h>
+
+using namespace std::placeholders;
+
+namespace android {
+
+static constexpr float linearResponse(float v) {
+ return v;
+}
+
+static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) {
+ return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c;
+}
+
+static constexpr float response(float x, const ColorSpace::TransferParameters& p) {
+ return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x;
+}
+
+static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) {
+ return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c;
+}
+
+static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) {
+ return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f;
+}
+
+static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
+ float xx = std::abs(x);
+ return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x);
+}
+
+static float absResponse(float x, float g, float a, float b, float c, float d) {
+ float xx = std::abs(x);
+ return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x);
+}
+
+static float safePow(float x, float e) {
+ return powf(x < 0.0f ? 0.0f : x, e);
+}
+
+static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) {
+ if (parameters.e == 0.0f && parameters.f == 0.0f) {
+ return std::bind(rcpResponse, _1, parameters);
+ }
+ return std::bind(rcpFullResponse, _1, parameters);
+}
+
+static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) {
+ if (parameters.e == 0.0f && parameters.f == 0.0f) {
+ return std::bind(response, _1, parameters);
+ }
+ return std::bind(fullResponse, _1, parameters);
+}
+
+static ColorSpace::transfer_function toOETF(float gamma) {
+ if (gamma == 1.0f) {
+ return linearResponse;
+ }
+ return std::bind(safePow, _1, 1.0f / gamma);
+}
+
+static ColorSpace::transfer_function toEOTF(float gamma) {
+ if (gamma == 1.0f) {
+ return linearResponse;
+ }
+ return std::bind(safePow, _1, gamma);
+}
+
+static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) {
+ float3 r(rgbToXYZ * float3{1, 0, 0});
+ float3 g(rgbToXYZ * float3{0, 1, 0});
+ float3 b(rgbToXYZ * float3{0, 0, 1});
+
+ return {{r.xy / dot(r, float3{1}),
+ g.xy / dot(g, float3{1}),
+ b.xy / dot(b, float3{1})}};
+}
+
+static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) {
+ float3 w(rgbToXYZ * float3{1});
+ return w.xy / dot(w, float3{1});
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ transfer_function OETF,
+ transfer_function EOTF,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(rgbToXYZ)
+ , mXYZtoRGB(inverse(rgbToXYZ))
+ , mOETF(std::move(OETF))
+ , mEOTF(std::move(EOTF))
+ , mClamper(std::move(clamper))
+ , mPrimaries(computePrimaries(rgbToXYZ))
+ , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ const TransferParameters parameters,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(rgbToXYZ)
+ , mXYZtoRGB(inverse(rgbToXYZ))
+ , mParameters(parameters)
+ , mOETF(toOETF(mParameters))
+ , mEOTF(toEOTF(mParameters))
+ , mClamper(std::move(clamper))
+ , mPrimaries(computePrimaries(rgbToXYZ))
+ , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const mat3& rgbToXYZ,
+ float gamma,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(rgbToXYZ)
+ , mXYZtoRGB(inverse(rgbToXYZ))
+ , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
+ , mOETF(toOETF(gamma))
+ , mEOTF(toEOTF(gamma))
+ , mClamper(std::move(clamper))
+ , mPrimaries(computePrimaries(rgbToXYZ))
+ , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ transfer_function OETF,
+ transfer_function EOTF,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+ , mXYZtoRGB(inverse(mRGBtoXYZ))
+ , mOETF(std::move(OETF))
+ , mEOTF(std::move(EOTF))
+ , mClamper(std::move(clamper))
+ , mPrimaries(primaries)
+ , mWhitePoint(whitePoint) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ const TransferParameters parameters,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+ , mXYZtoRGB(inverse(mRGBtoXYZ))
+ , mParameters(parameters)
+ , mOETF(toOETF(mParameters))
+ , mEOTF(toEOTF(mParameters))
+ , mClamper(std::move(clamper))
+ , mPrimaries(primaries)
+ , mWhitePoint(whitePoint) {
+}
+
+ColorSpace::ColorSpace(
+ const std::string& name,
+ const std::array<float2, 3>& primaries,
+ const float2& whitePoint,
+ float gamma,
+ clamping_function clamper) noexcept
+ : mName(name)
+ , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+ , mXYZtoRGB(inverse(mRGBtoXYZ))
+ , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
+ , mOETF(toOETF(gamma))
+ , mEOTF(toEOTF(gamma))
+ , mClamper(std::move(clamper))
+ , mPrimaries(primaries)
+ , mWhitePoint(whitePoint) {
+}
+
+constexpr mat3 ColorSpace::computeXYZMatrix(
+ const std::array<float2, 3>& primaries, const float2& whitePoint) {
+ const float2& R = primaries[0];
+ const float2& G = primaries[1];
+ const float2& B = primaries[2];
+ const float2& W = whitePoint;
+
+ float oneRxRy = (1 - R.x) / R.y;
+ float oneGxGy = (1 - G.x) / G.y;
+ float oneBxBy = (1 - B.x) / B.y;
+ float oneWxWy = (1 - W.x) / W.y;
+
+ float RxRy = R.x / R.y;
+ float GxGy = G.x / G.y;
+ float BxBy = B.x / B.y;
+ float WxWy = W.x / W.y;
+
+ float BY =
+ ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
+ ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
+ float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
+ float RY = 1 - GY - BY;
+
+ float RYRy = RY / R.y;
+ float GYGy = GY / G.y;
+ float BYBy = BY / B.y;
+
+ return {
+ float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)},
+ float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)},
+ float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)}
+ };
+}
+
+const ColorSpace ColorSpace::sRGB() {
+ return {
+ "sRGB IEC61966-2.1",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::linearSRGB() {
+ return {
+ "sRGB IEC61966-2.1 (Linear)",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f}
+ };
+}
+
+const ColorSpace ColorSpace::extendedSRGB() {
+ return {
+ "scRGB-nl IEC 61966-2-2:2003",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+ std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+ std::bind(clamp<float>, _1, -0.799f, 2.399f)
+ };
+}
+
+const ColorSpace ColorSpace::linearExtendedSRGB() {
+ return {
+ "scRGB IEC 61966-2-2:2003",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ 1.0f,
+ std::bind(clamp<float>, _1, -0.5f, 7.499f)
+ };
+}
+
+const ColorSpace ColorSpace::NTSC() {
+ return {
+ "NTSC (1953)",
+ {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}},
+ {0.310f, 0.316f},
+ {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::BT709() {
+ return {
+ "Rec. ITU-R BT.709-5",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::BT2020() {
+ return {
+ "Rec. ITU-R BT.2020-1",
+ {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}},
+ {0.3127f, 0.3290f},
+ {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::AdobeRGB() {
+ return {
+ "Adobe RGB (1998)",
+ {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
+ {0.3127f, 0.3290f},
+ 2.2f
+ };
+}
+
+const ColorSpace ColorSpace::ProPhotoRGB() {
+ return {
+ "ROMM RGB ISO 22028-2:2013",
+ {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
+ {0.34567f, 0.35850f},
+ {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::DisplayP3() {
+ return {
+ "Display P3",
+ {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f},
+ {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f}
+ };
+}
+
+const ColorSpace ColorSpace::DCIP3() {
+ return {
+ "SMPTE RP 431-2-2007 DCI (P3)",
+ {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+ {0.314f, 0.351f},
+ 2.6f
+ };
+}
+
+const ColorSpace ColorSpace::ACES() {
+ return {
+ "SMPTE ST 2065-1:2012 ACES",
+ {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}},
+ {0.32168f, 0.33767f},
+ 1.0f,
+ std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+ };
+}
+
+const ColorSpace ColorSpace::ACEScg() {
+ return {
+ "Academy S-2014-004 ACEScg",
+ {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}},
+ {0.32168f, 0.33767f},
+ 1.0f,
+ std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+ };
+}
+
+std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size,
+ const ColorSpace& src, const ColorSpace& dst) {
+
+ size = clamp(size, 2u, 256u);
+ float m = 1.0f / float(size - 1);
+
+ std::unique_ptr<float3> lut(new float3[size * size * size]);
+ float3* data = lut.get();
+
+ ColorSpaceConnector connector(src, dst);
+
+ for (uint32_t z = 0; z < size; z++) {
+ for (int32_t y = int32_t(size - 1); y >= 0; y--) {
+ for (uint32_t x = 0; x < size; x++) {
+ *data++ = connector.transform({x * m, y * m, z * m});
+ }
+ }
+ }
+
+ return lut;
+}
+
+static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 BRADFORD = mat3{
+ float3{ 0.8951f, -0.7502f, 0.0389f},
+ float3{ 0.2664f, 1.7135f, -0.0685f},
+ float3{-0.1614f, 0.0367f, 1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+ float3 srcLMS = matrix * srcWhitePoint;
+ float3 dstLMS = matrix * dstWhitePoint;
+ return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+ColorSpaceConnector::ColorSpaceConnector(
+ const ColorSpace& src,
+ const ColorSpace& dst) noexcept
+ : mSource(src)
+ , mDestination(dst) {
+
+ if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) {
+ mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ();
+ } else {
+ mat3 rgbToXYZ(src.getRGBtoXYZ());
+ mat3 xyzToRGB(dst.getXYZtoRGB());
+
+ float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1});
+ float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1});
+
+ if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
+ rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ();
+ }
+
+ if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
+ xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ());
+ }
+
+ mTransform = xyzToRGB * rgbToXYZ;
+ }
+}
+
+}; // namespace android
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
new file mode 100644
index 0000000000..882bd7c7b2
--- /dev/null
+++ b/libs/ui/DebugUtils.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/DebugUtils.h>
+
+#include <android-base/stringprintf.h>
+#include <string>
+
+std::string decodeStandard(android_dataspace dataspace) {
+ const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
+ switch (dataspaceSelect) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return std::string("BT709");
+
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ return std::string("BT601_625");
+
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ return std::string("BT601_625_UNADJUSTED");
+
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ return std::string("BT601_525");
+
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ return std::string("BT601_525_UNADJUSTED");
+
+ case HAL_DATASPACE_STANDARD_BT2020:
+ return std::string("BT2020");
+
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ return std::string("BT2020 (constant luminance)");
+
+ case HAL_DATASPACE_STANDARD_BT470M:
+ return std::string("BT470M");
+
+ case HAL_DATASPACE_STANDARD_FILM:
+ return std::string("FILM");
+
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return std::string("DCI-P3");
+
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ return std::string("AdobeRGB");
+
+ case 0:
+ switch (dataspace & 0xffff) {
+ case HAL_DATASPACE_JFIF:
+ return std::string("(deprecated) JFIF (BT601_625, SMPTE_170M Full range)");
+
+ case HAL_DATASPACE_BT601_625:
+ return std::string("(deprecated) BT601_625 (BT601_625, SMPTE_170M Limited "
+ "range)");
+
+ case HAL_DATASPACE_BT601_525:
+ return std::string("(deprecated) BT601_525 (BT601_525, SMPTE_170M Limited "
+ "range)");
+
+ case HAL_DATASPACE_SRGB_LINEAR:
+ return std::string("(deprecated) SRGB Linear Full range");
+
+ case HAL_DATASPACE_SRGB:
+ return std::string("(deprecated) sRGB");
+
+ case HAL_DATASPACE_V0_BT709:
+ return std::string("(deprecated) BT709 (BT709, SMPTE_170M Limited range)");
+
+ case HAL_DATASPACE_ARBITRARY:
+ return std::string("ARBITRARY");
+
+ case HAL_DATASPACE_UNKNOWN:
+ // Fallthrough
+ default:
+ return android::base::StringPrintf("Unknown deprecated dataspace code %d",
+ dataspaceSelect);
+ }
+ }
+
+ return android::base::StringPrintf("Unknown dataspace code %d", dataspaceSelect);
+}
+
+std::string decodeTransfer(android_dataspace dataspace) {
+ const uint32_t dataspaceTransfer = (dataspace & HAL_DATASPACE_TRANSFER_MASK);
+ switch (dataspaceTransfer) {
+ case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
+ return std::string("Unspecified");
+
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ return std::string("Linear");
+
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ return std::string("sRGB");
+
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ return std::string("SMPTE_170M");
+
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ return std::string("gamma 2.2");
+
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ return std::string("gamma 2.6");
+
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ return std::string("gamma 2.8");
+
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ return std::string("SMPTE 2084");
+
+ case HAL_DATASPACE_TRANSFER_HLG:
+ return std::string("STD-B67");
+ }
+
+ return android::base::StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer);
+}
+
+std::string decodeRange(android_dataspace dataspace) {
+ const uint32_t dataspaceRange = (dataspace & HAL_DATASPACE_RANGE_MASK);
+ switch (dataspaceRange) {
+ case HAL_DATASPACE_RANGE_UNSPECIFIED:
+ return std::string("Range Unspecified");
+
+ case HAL_DATASPACE_RANGE_FULL:
+ return std::string("Full range");
+
+ case HAL_DATASPACE_RANGE_LIMITED:
+ return std::string("Limited range");
+
+ case HAL_DATASPACE_RANGE_EXTENDED:
+ return std::string("Extended range");
+ }
+
+ return android::base::StringPrintf("Unknown dataspace range %d", dataspaceRange);
+}
+
+std::string dataspaceDetails(android_dataspace dataspace) {
+ return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
+ decodeTransfer(dataspace).c_str(),
+ decodeRange(dataspace).c_str());
+}
+
+std::string decodeColorMode(android_color_mode colorMode) {
+ switch (colorMode) {
+ case HAL_COLOR_MODE_NATIVE:
+ return std::string("HAL_COLOR_MODE_NATIVE");
+
+ case HAL_COLOR_MODE_STANDARD_BT601_625:
+ return std::string("HAL_COLOR_MODE_BT601_625");
+
+ case HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED:
+ return std::string("HAL_COLOR_MODE_BT601_625_UNADJUSTED");
+
+ case HAL_COLOR_MODE_STANDARD_BT601_525:
+ return std::string("HAL_COLOR_MODE_BT601_525");
+
+ case HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED:
+ return std::string("HAL_COLOR_MODE_BT601_525_UNADJUSTED");
+
+ case HAL_COLOR_MODE_STANDARD_BT709:
+ return std::string("HAL_COLOR_MODE_BT709");
+
+ case HAL_COLOR_MODE_DCI_P3:
+ return std::string("HAL_COLOR_MODE_DCI_P3");
+
+ case HAL_COLOR_MODE_SRGB:
+ return std::string("HAL_COLOR_MODE_SRGB");
+
+ case HAL_COLOR_MODE_ADOBE_RGB:
+ return std::string("HAL_COLOR_MODE_ADOBE_RGB");
+
+ case HAL_COLOR_MODE_DISPLAY_P3:
+ return std::string("HAL_COLOR_MODE_DISPLAY_P3");
+ }
+
+ return android::base::StringPrintf("Unknown color mode %d", colorMode);
+}
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 7cf8233820..b67f4d9328 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <ui/Fence.h>
+
#define LOG_TAG "Fence"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -25,9 +27,10 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
-#include <ui/Fence.h>
+#include <sys/types.h>
#include <unistd.h>
#include <utils/Log.h>
+#include <utils/String8.h>
#include <utils/Trace.h>
namespace android {
@@ -109,17 +112,17 @@ int Fence::dup() const {
nsecs_t Fence::getSignalTime() const {
if (mFenceFd == -1) {
- return -1;
+ return SIGNAL_TIME_INVALID;
}
struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd);
if (finfo == NULL) {
ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd);
- return -1;
+ return SIGNAL_TIME_INVALID;
}
if (finfo->status != 1) {
sync_fence_info_free(finfo);
- return INT64_MAX;
+ return SIGNAL_TIME_PENDING;
}
struct sync_pt_info* pinfo = NULL;
@@ -162,7 +165,7 @@ status_t Fence::unflatten(void const*& buffer, size_t& size, int const*& fds, si
return INVALID_OPERATION;
}
- if (size < 1) {
+ if (size < getFlattenedSize()) {
return NO_MEMORY;
}
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
new file mode 100644
index 0000000000..14147663de
--- /dev/null
+++ b/libs/ui/FenceTime.cpp
@@ -0,0 +1,367 @@
+/*
+* Copyright 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <ui/FenceTime.h>
+
+#define LOG_TAG "FenceTime"
+
+#include <cutils/compiler.h> // For CC_[UN]LIKELY
+#include <utils/Log.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <memory>
+
+namespace android {
+
+// ============================================================================
+// FenceTime
+// ============================================================================
+
+const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE);
+
+void* FenceTime::operator new(size_t byteCount) noexcept {
+ void *p = nullptr;
+ if (posix_memalign(&p, alignof(FenceTime), byteCount)) {
+ return nullptr;
+ }
+ return p;
+}
+
+void FenceTime::operator delete(void *p) {
+ free(p);
+}
+
+FenceTime::FenceTime(const sp<Fence>& fence)
+ : mState(((fence.get() != nullptr) && fence->isValid()) ?
+ State::VALID : State::INVALID),
+ mFence(fence),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(sp<Fence>&& fence)
+ : mState(((fence.get() != nullptr) && fence->isValid()) ?
+ State::VALID : State::INVALID),
+ mFence(std::move(fence)),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(nsecs_t signalTime)
+ : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID),
+ mFence(nullptr),
+ mSignalTime(signalTime) {
+ if (CC_UNLIKELY(mSignalTime == Fence::SIGNAL_TIME_PENDING)) {
+ ALOGE("Pending signal time not allowed after signal.");
+ mSignalTime = Fence::SIGNAL_TIME_INVALID;
+ }
+}
+
+void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
+ if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
+ // Applying Snapshot::State::FENCE, could change the valid state of the
+ // FenceTime, which is not allowed. Callers should create a new
+ // FenceTime from the snapshot instead.
+ ALOGE("applyTrustedSnapshot: Unexpected fence.");
+ return;
+ }
+
+ if (src.state == Snapshot::State::EMPTY) {
+ return;
+ }
+
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ // We should always get the same signalTime here that we did in
+ // getSignalTime(). This check races with getSignalTime(), but it is
+ // only a sanity check so that's okay.
+ if (CC_UNLIKELY(signalTime != src.signalTime)) {
+ ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. "
+ "(%" PRId64 " (old) != %" PRId64 " (new))",
+ signalTime, src.signalTime);
+ }
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(src.signalTime, std::memory_order_relaxed);
+}
+
+bool FenceTime::isValid() const {
+ // We store the valid state in the constructors and return it here.
+ // This lets release code remember the valid state even after the
+ // underlying fence is destroyed.
+ return mState != State::INVALID;
+}
+
+nsecs_t FenceTime::getSignalTime() {
+ // See if we already have a cached value we can return.
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return signalTime;
+ }
+
+ // Hold a reference to the fence on the stack in case the class'
+ // reference is removed by another thread. This prevents the
+ // fence from being destroyed until the end of this method, where
+ // we conveniently do not have the lock held.
+ sp<Fence> fence;
+ {
+ // With the lock acquired this time, see if we have the cached
+ // value or if we need to poll the fence.
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mFence.get()) {
+ // Another thread set the signal time just before we added the
+ // reference to mFence.
+ return mSignalTime.load(std::memory_order_relaxed);
+ }
+ fence = mFence;
+ }
+
+ // Make the system call without the lock held.
+ signalTime = fence->getSignalTime();
+
+ // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests
+ // use invalid underlying Fences without real file descriptors.
+ if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) {
+ if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+ signalTime = Fence::SIGNAL_TIME_PENDING;
+ }
+ }
+
+ // Make the signal time visible to everyone if it is no longer pending
+ // and remove the class' reference to the fence.
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(signalTime, std::memory_order_relaxed);
+ }
+
+ return signalTime;
+}
+
+nsecs_t FenceTime::getCachedSignalTime() const {
+ // memory_order_acquire since we don't have a lock fallback path
+ // that will do an acquire.
+ return mSignalTime.load(std::memory_order_acquire);
+}
+
+FenceTime::Snapshot FenceTime::getSnapshot() const {
+ // Quick check without the lock.
+ nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return Snapshot(signalTime);
+ }
+
+ // Do the full check with the lock.
+ std::lock_guard<std::mutex> lock(mMutex);
+ signalTime = mSignalTime.load(std::memory_order_relaxed);
+ if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+ return Snapshot(signalTime);
+ }
+ return Snapshot(mFence);
+}
+
+// For tests only. If forceValidForTest is true, then getSignalTime will
+// never return SIGNAL_TIME_INVALID and isValid will always return true.
+FenceTime::FenceTime(const sp<Fence>& fence, bool forceValidForTest)
+ : mState(forceValidForTest ?
+ State::FORCED_VALID_FOR_TEST : State::INVALID),
+ mFence(fence),
+ mSignalTime(mState == State::INVALID ?
+ Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+void FenceTime::signalForTest(nsecs_t signalTime) {
+ // To be realistic, this should really set a hidden value that
+ // gets picked up in the next call to getSignalTime, but this should
+ // be good enough.
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFence.clear();
+ mSignalTime.store(signalTime, std::memory_order_relaxed);
+}
+
+// ============================================================================
+// FenceTime::Snapshot
+// ============================================================================
+FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence)
+ : state(State::FENCE), fence(srcFence) {
+}
+
+FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime)
+ : state(State::SIGNAL_TIME), signalTime(srcSignalTime) {
+}
+
+size_t FenceTime::Snapshot::getFlattenedSize() const {
+ constexpr size_t min = sizeof(state);
+ switch (state) {
+ case State::EMPTY:
+ return min;
+ case State::FENCE:
+ return min + fence->getFlattenedSize();
+ case State::SIGNAL_TIME:
+ return min + sizeof(signalTime);
+ }
+ return 0;
+}
+
+size_t FenceTime::Snapshot::getFdCount() const {
+ return state == State::FENCE ? fence->getFdCount() : 0u;
+}
+
+status_t FenceTime::Snapshot::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(buffer, size, state);
+ switch (state) {
+ case State::EMPTY:
+ return NO_ERROR;
+ case State::FENCE:
+ return fence->flatten(buffer, size, fds, count);
+ case State::SIGNAL_TIME:
+ FlattenableUtils::write(buffer, size, signalTime);
+ return NO_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+status_t FenceTime::Snapshot::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < sizeof(state)) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, state);
+ switch (state) {
+ case State::EMPTY:
+ return NO_ERROR;
+ case State::FENCE:
+ fence = new Fence;
+ return fence->unflatten(buffer, size, fds, count);
+ case State::SIGNAL_TIME:
+ if (size < sizeof(signalTime)) {
+ return NO_MEMORY;
+ }
+ FlattenableUtils::read(buffer, size, signalTime);
+ return NO_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+// ============================================================================
+// FenceTimeline
+// ============================================================================
+void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ while (mQueue.size() >= MAX_ENTRIES) {
+ // This is a sanity check to make sure the queue doesn't grow unbounded.
+ // MAX_ENTRIES should be big enough not to trigger this path.
+ // In case this path is taken though, users of FenceTime must make sure
+ // not to rely solely on FenceTimeline to get the final timestamp and
+ // should eventually call Fence::getSignalTime on their own.
+ std::shared_ptr<FenceTime> front = mQueue.front().lock();
+ if (front) {
+ // Make a last ditch effort to get the signalTime here since
+ // we are removing it from the timeline.
+ front->getSignalTime();
+ }
+ mQueue.pop();
+ }
+ mQueue.push(fence);
+}
+
+void FenceTimeline::updateSignalTimes() {
+ while (!mQueue.empty()) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ std::shared_ptr<FenceTime> fence = mQueue.front().lock();
+ if (!fence) {
+ // The shared_ptr no longer exists and no one cares about the
+ // timestamp anymore.
+ mQueue.pop();
+ continue;
+ } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ // The fence has signaled and we've removed the sp<Fence> ref.
+ mQueue.pop();
+ continue;
+ } else {
+ // The fence didn't signal yet. Break since the later ones
+ // shouldn't have signaled either.
+ break;
+ }
+ }
+}
+
+// ============================================================================
+// FenceToFenceTimeMap
+// ============================================================================
+std::shared_ptr<FenceTime> FenceToFenceTimeMap::createFenceTimeForTest(
+ const sp<Fence>& fence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ // Always garbage collecting isn't efficient, but this is only for testing.
+ garbageCollectLocked();
+ std::shared_ptr<FenceTime> fenceTime(new FenceTime(fence, true));
+ mMap[fence.get()].push_back(fenceTime);
+ return fenceTime;
+}
+
+void FenceToFenceTimeMap::signalAllForTest(
+ const sp<Fence>& fence, nsecs_t signalTime) {
+ bool signaled = false;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto it = mMap.find(fence.get());
+ if (it != mMap.end()) {
+ for (auto& weakFenceTime : it->second) {
+ std::shared_ptr<FenceTime> fenceTime = weakFenceTime.lock();
+ if (!fenceTime) {
+ continue;
+ }
+ ALOGE_IF(!fenceTime->isValid(),
+ "signalAllForTest: Signaling invalid fence.");
+ fenceTime->signalForTest(signalTime);
+ signaled = true;
+ }
+ }
+
+ ALOGE_IF(!signaled, "signalAllForTest: Nothing to signal.");
+}
+
+void FenceToFenceTimeMap::garbageCollectLocked() {
+ for (auto& it : mMap) {
+ // Erase all expired weak pointers from the vector.
+ auto& vect = it.second;
+ vect.erase(
+ std::remove_if(vect.begin(), vect.end(),
+ [](const std::weak_ptr<FenceTime>& ft) {
+ return ft.expired();
+ }),
+ vect.end());
+
+ // Also erase the map entry if the vector is now empty.
+ if (vect.empty()) {
+ mMap.erase(it.first);
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/ui/Gralloc1.cpp b/libs/ui/Gralloc1.cpp
deleted file mode 100644
index 4c73ce4535..0000000000
--- a/libs/ui/Gralloc1.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright 2016 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 <ui/Gralloc1.h>
-
-#include <vector>
-
-#undef LOG_TAG
-#define LOG_TAG GRALLOC1_LOG_TAG
-
-namespace android {
-
-namespace Gralloc1 {
-
-Descriptor::~Descriptor()
-{
- int32_t intError = mShimDevice.mFunctions.destroyDescriptor(
- mShimDevice.mDevice, mDeviceId);
- auto error = static_cast<gralloc1_error_t>(intError);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("destroyDescriptor failed: %d", intError);
- }
-}
-
-gralloc1_error_t Descriptor::setDimensions(uint32_t width, uint32_t height)
-{
- int32_t intError = mShimDevice.mFunctions.setDimensions(mShimDevice.mDevice,
- mDeviceId, width, height);
- auto error = static_cast<gralloc1_error_t>(intError);
- if (error != GRALLOC1_ERROR_NONE) {
- return error;
- }
- mWidth = width;
- mHeight = height;
- return error;
-}
-
-template <typename ApiType>
-struct Setter {
- typedef int32_t (*Type)(gralloc1_device_t*, gralloc1_buffer_descriptor_t,
- ApiType);
-};
-
-template <typename ApiType, typename ValueType>
-static inline gralloc1_error_t setHelper(
- typename Setter<ApiType>::Type setter, gralloc1_device_t* device,
- gralloc1_buffer_descriptor_t id, ValueType newValue,
- ValueType* cacheVariable)
-{
- int32_t intError = setter(device, id, static_cast<ApiType>(newValue));
- auto error = static_cast<gralloc1_error_t>(intError);
- if (error != GRALLOC1_ERROR_NONE) {
- return error;
- }
- *cacheVariable = newValue;
- return error;
-}
-
-gralloc1_error_t Descriptor::setFormat(android_pixel_format_t format)
-{
- return setHelper<int32_t>(mShimDevice.mFunctions.setFormat.pfn,
- mShimDevice.mDevice, mDeviceId, format, &mFormat);
-}
-
-gralloc1_error_t Descriptor::setProducerUsage(gralloc1_producer_usage_t usage)
-{
- return setHelper<uint64_t>(mShimDevice.mFunctions.setProducerUsage.pfn,
- mShimDevice.mDevice, mDeviceId, usage, &mProducerUsage);
-}
-
-gralloc1_error_t Descriptor::setConsumerUsage(gralloc1_consumer_usage_t usage)
-{
- return setHelper<uint64_t>(mShimDevice.mFunctions.setConsumerUsage.pfn,
- mShimDevice.mDevice, mDeviceId, usage, &mConsumerUsage);
-}
-
-Device::Device(gralloc1_device_t* device)
- : mDevice(device),
- mCapabilities(loadCapabilities()),
- mFunctions()
-{
- if (!loadFunctions()) {
- ALOGE("Failed to load a required function, aborting");
- abort();
- }
-}
-
-bool Device::hasCapability(gralloc1_capability_t capability) const
-{
- return mCapabilities.count(capability) > 0;
-}
-
-std::string Device::dump()
-{
- uint32_t length = 0;
- mFunctions.dump(mDevice, &length, nullptr);
-
- std::vector<char> output;
- output.resize(length);
- mFunctions.dump(mDevice, &length, output.data());
-
- return std::string(output.cbegin(), output.cend());
-}
-
-std::shared_ptr<Descriptor> Device::createDescriptor()
-{
- gralloc1_buffer_descriptor_t descriptorId;
- int32_t intError = mFunctions.createDescriptor(mDevice, &descriptorId);
- auto error = static_cast<gralloc1_error_t>(intError);
- if (error != GRALLOC1_ERROR_NONE) {
- return nullptr;
- }
- auto descriptor = std::make_shared<Descriptor>(*this, descriptorId);
- return descriptor;
-}
-
-gralloc1_error_t Device::getStride(buffer_handle_t buffer, uint32_t* outStride)
-{
- int32_t intError = mFunctions.getStride(mDevice, buffer, outStride);
- return static_cast<gralloc1_error_t>(intError);
-}
-
-static inline bool allocationSucceded(gralloc1_error_t error)
-{
- return error == GRALLOC1_ERROR_NONE || error == GRALLOC1_ERROR_NOT_SHARED;
-}
-
-gralloc1_error_t Device::allocate(
- const std::vector<std::shared_ptr<const Descriptor>>& descriptors,
- std::vector<buffer_handle_t>* outBuffers)
-{
- if (mFunctions.allocate.pfn == nullptr) {
- // Allocation is not supported on this device
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
-
- std::vector<gralloc1_buffer_descriptor_t> deviceIds;
- for (const auto& descriptor : descriptors) {
- deviceIds.emplace_back(descriptor->getDeviceId());
- }
-
- std::vector<buffer_handle_t> buffers(descriptors.size());
- int32_t intError = mFunctions.allocate(mDevice,
- static_cast<uint32_t>(descriptors.size()), deviceIds.data(),
- buffers.data());
- auto error = static_cast<gralloc1_error_t>(intError);
- if (allocationSucceded(error)) {
- *outBuffers = std::move(buffers);
- }
-
- return error;
-}
-
-gralloc1_error_t Device::allocate(
- const std::shared_ptr<const Descriptor>& descriptor,
- gralloc1_backing_store_t id, buffer_handle_t* outBuffer)
-{
- gralloc1_error_t error = GRALLOC1_ERROR_NONE;
-
- if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
- buffer_handle_t buffer = nullptr;
- int32_t intError = mFunctions.allocateWithId(mDevice,
- descriptor->getDeviceId(), id, &buffer);
- error = static_cast<gralloc1_error_t>(intError);
- if (allocationSucceded(error)) {
- *outBuffer = buffer;
- }
- } else {
- std::vector<std::shared_ptr<const Descriptor>> descriptors;
- descriptors.emplace_back(descriptor);
- std::vector<buffer_handle_t> buffers;
- error = allocate(descriptors, &buffers);
- if (allocationSucceded(error)) {
- *outBuffer = buffers[0];
- }
- }
-
- return error;
-}
-
-gralloc1_error_t Device::retain(buffer_handle_t buffer)
-{
- int32_t intError = mFunctions.retain(mDevice, buffer);
- return static_cast<gralloc1_error_t>(intError);
-}
-
-gralloc1_error_t Device::retain(const GraphicBuffer* buffer)
-{
- if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
- return mFunctions.retainGraphicBuffer(mDevice, buffer);
- } else {
- return retain(buffer->getNativeBuffer()->handle);
- }
-}
-
-gralloc1_error_t Device::release(buffer_handle_t buffer)
-{
- int32_t intError = mFunctions.release(mDevice, buffer);
- return static_cast<gralloc1_error_t>(intError);
-}
-
-gralloc1_error_t Device::getNumFlexPlanes(buffer_handle_t buffer,
- uint32_t* outNumPlanes)
-{
- uint32_t numPlanes = 0;
- int32_t intError = mFunctions.getNumFlexPlanes(mDevice, buffer, &numPlanes);
- auto error = static_cast<gralloc1_error_t>(intError);
- if (error == GRALLOC1_ERROR_NONE) {
- *outNumPlanes = numPlanes;
- }
- return error;
-}
-
-gralloc1_error_t Device::lock(buffer_handle_t buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t* accessRegion, void** outData,
- const sp<Fence>& acquireFence)
-{
- ALOGV("Calling lock(%p)", buffer);
- return lockHelper(mFunctions.lock.pfn, buffer, producerUsage,
- consumerUsage, accessRegion, outData, acquireFence);
-}
-
-gralloc1_error_t Device::lockFlex(buffer_handle_t buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t* accessRegion,
- struct android_flex_layout* outData,
- const sp<Fence>& acquireFence)
-{
- ALOGV("Calling lockFlex(%p)", buffer);
- return lockHelper(mFunctions.lockFlex.pfn, buffer, producerUsage,
- consumerUsage, accessRegion, outData, acquireFence);
-}
-
-gralloc1_error_t Device::lockYCbCr(buffer_handle_t buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t* accessRegion,
- struct android_ycbcr* outData,
- const sp<Fence>& acquireFence)
-{
- ALOGV("Calling lockYCbCr(%p)", buffer);
- return lockHelper(mFunctions.lockYCbCr.pfn, buffer, producerUsage,
- consumerUsage, accessRegion, outData, acquireFence);
-}
-
-gralloc1_error_t Device::unlock(buffer_handle_t buffer, sp<Fence>* outFence)
-{
- int32_t fenceFd = -1;
- int32_t intError = mFunctions.unlock(mDevice, buffer, &fenceFd);
- auto error = static_cast<gralloc1_error_t>(intError);
- if (error == GRALLOC1_ERROR_NONE) {
- *outFence = new Fence(fenceFd);
- }
- return error;
-}
-
-std::unordered_set<gralloc1_capability_t> Device::loadCapabilities()
-{
- std::vector<int32_t> intCapabilities;
- uint32_t numCapabilities = 0;
- mDevice->getCapabilities(mDevice, &numCapabilities, nullptr);
-
- intCapabilities.resize(numCapabilities);
- mDevice->getCapabilities(mDevice, &numCapabilities, intCapabilities.data());
-
- std::unordered_set<gralloc1_capability_t> capabilities;
- for (const auto intCapability : intCapabilities) {
- capabilities.emplace(static_cast<gralloc1_capability_t>(intCapability));
- }
- return capabilities;
-}
-
-bool Device::loadFunctions()
-{
- // Functions which must always be present
- if (!mFunctions.dump.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.createDescriptor.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.destroyDescriptor.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.setConsumerUsage.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.setDimensions.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.setFormat.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.setProducerUsage.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.getBackingStore.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.getConsumerUsage.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.getDimensions.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.getFormat.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.getProducerUsage.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.getStride.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.retain.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.release.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.getNumFlexPlanes.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.lock.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.lockFlex.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.unlock.load(mDevice, true)) {
- return false;
- }
-
- if (hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
- // These should always be present on the adapter
- if (!mFunctions.retainGraphicBuffer.load(mDevice, true)) {
- return false;
- }
- if (!mFunctions.lockYCbCr.load(mDevice, true)) {
- return false;
- }
-
- // allocateWithId may not be present if we're only able to map in this
- // process
- mFunctions.allocateWithId.load(mDevice, false);
- } else {
- // allocate may not be present if we're only able to map in this process
- mFunctions.allocate.load(mDevice, false);
- }
-
- return true;
-}
-
-std::unique_ptr<Gralloc1On0Adapter> Loader::mAdapter = nullptr;
-
-Loader::Loader()
- : mDevice(nullptr)
-{
- hw_module_t const* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- uint8_t majorVersion = (module->module_api_version >> 8) & 0xFF;
- uint8_t minorVersion = module->module_api_version & 0xFF;
- gralloc1_device_t* device = nullptr;
- if (majorVersion == 1) {
- gralloc1_open(module, &device);
- } else {
- if (!mAdapter) {
- mAdapter = std::make_unique<Gralloc1On0Adapter>(module);
- }
- device = mAdapter->getDevice();
- }
- mDevice = std::make_unique<Gralloc1::Device>(device);
-}
-
-Loader::~Loader() {}
-
-std::unique_ptr<Device> Loader::getDevice()
-{
- return std::move(mDevice);
-}
-
-} // namespace android::Gralloc1
-
-} // namespace android
diff --git a/libs/ui/Gralloc1On0Adapter.cpp b/libs/ui/Gralloc1On0Adapter.cpp
deleted file mode 100644
index ec7df31f9c..0000000000
--- a/libs/ui/Gralloc1On0Adapter.cpp
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * Copyright 2016 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 "Gralloc1On0Adapter"
-//#define LOG_NDEBUG 0
-
-#include <hardware/gralloc.h>
-
-#include <ui/Gralloc1On0Adapter.h>
-
-#include <utils/Log.h>
-
-#include <inttypes.h>
-
-template <typename PFN, typename T>
-static gralloc1_function_pointer_t asFP(T function)
-{
- static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
- return reinterpret_cast<gralloc1_function_pointer_t>(function);
-}
-
-namespace android {
-
-Gralloc1On0Adapter::Gralloc1On0Adapter(const hw_module_t* module)
- : mModule(reinterpret_cast<const gralloc_module_t*>(module)),
- mMinorVersion(mModule->common.module_api_version & 0xFF),
- mDevice(nullptr)
-{
- ALOGV("Constructing");
- getCapabilities = getCapabilitiesHook;
- getFunction = getFunctionHook;
- int error = ::gralloc_open(&(mModule->common), &mDevice);
- if (error) {
- ALOGE("Failed to open gralloc0 module: %d", error);
- }
- ALOGV("Opened gralloc0 device %p", mDevice);
-}
-
-Gralloc1On0Adapter::~Gralloc1On0Adapter()
-{
- ALOGV("Destructing");
- if (mDevice) {
- ALOGV("Closing gralloc0 device %p", mDevice);
- ::gralloc_close(mDevice);
- }
-}
-
-void Gralloc1On0Adapter::doGetCapabilities(uint32_t* outCount,
- int32_t* outCapabilities)
-{
- if (outCapabilities == nullptr) {
- *outCount = 1;
- return;
- }
- if (*outCount >= 1) {
- *outCapabilities = GRALLOC1_CAPABILITY_ON_ADAPTER;
- *outCount = 1;
- }
-}
-
-gralloc1_function_pointer_t Gralloc1On0Adapter::doGetFunction(
- int32_t intDescriptor)
-{
- constexpr auto lastDescriptor =
- static_cast<int32_t>(GRALLOC1_LAST_ADAPTER_FUNCTION);
- if (intDescriptor < 0 || intDescriptor > lastDescriptor) {
- ALOGE("Invalid function descriptor");
- return nullptr;
- }
-
- auto descriptor =
- static_cast<gralloc1_function_descriptor_t>(intDescriptor);
- switch (descriptor) {
- case GRALLOC1_FUNCTION_DUMP:
- return asFP<GRALLOC1_PFN_DUMP>(dumpHook);
- case GRALLOC1_FUNCTION_CREATE_DESCRIPTOR:
- return asFP<GRALLOC1_PFN_CREATE_DESCRIPTOR>(createDescriptorHook);
- case GRALLOC1_FUNCTION_DESTROY_DESCRIPTOR:
- return asFP<GRALLOC1_PFN_DESTROY_DESCRIPTOR>(destroyDescriptorHook);
- case GRALLOC1_FUNCTION_SET_CONSUMER_USAGE:
- return asFP<GRALLOC1_PFN_SET_CONSUMER_USAGE>(setConsumerUsageHook);
- case GRALLOC1_FUNCTION_SET_DIMENSIONS:
- return asFP<GRALLOC1_PFN_SET_DIMENSIONS>(setDimensionsHook);
- case GRALLOC1_FUNCTION_SET_FORMAT:
- return asFP<GRALLOC1_PFN_SET_FORMAT>(setFormatHook);
- case GRALLOC1_FUNCTION_SET_PRODUCER_USAGE:
- return asFP<GRALLOC1_PFN_SET_PRODUCER_USAGE>(setProducerUsageHook);
- case GRALLOC1_FUNCTION_GET_BACKING_STORE:
- return asFP<GRALLOC1_PFN_GET_BACKING_STORE>(
- bufferHook<decltype(&Buffer::getBackingStore),
- &Buffer::getBackingStore, gralloc1_backing_store_t*>);
- case GRALLOC1_FUNCTION_GET_CONSUMER_USAGE:
- return asFP<GRALLOC1_PFN_GET_CONSUMER_USAGE>(getConsumerUsageHook);
- case GRALLOC1_FUNCTION_GET_DIMENSIONS:
- return asFP<GRALLOC1_PFN_GET_DIMENSIONS>(
- bufferHook<decltype(&Buffer::getDimensions),
- &Buffer::getDimensions, uint32_t*, uint32_t*>);
- case GRALLOC1_FUNCTION_GET_FORMAT:
- return asFP<GRALLOC1_PFN_GET_FORMAT>(
- bufferHook<decltype(&Buffer::getFormat),
- &Buffer::getFormat, int32_t*>);
- case GRALLOC1_FUNCTION_GET_PRODUCER_USAGE:
- return asFP<GRALLOC1_PFN_GET_PRODUCER_USAGE>(getProducerUsageHook);
- case GRALLOC1_FUNCTION_GET_STRIDE:
- return asFP<GRALLOC1_PFN_GET_STRIDE>(
- bufferHook<decltype(&Buffer::getStride),
- &Buffer::getStride, uint32_t*>);
- case GRALLOC1_FUNCTION_ALLOCATE:
- // Not provided, since we'll use ALLOCATE_WITH_ID
- return nullptr;
- case GRALLOC1_FUNCTION_ALLOCATE_WITH_ID:
- if (mDevice != nullptr) {
- return asFP<GRALLOC1_PFN_ALLOCATE_WITH_ID>(allocateWithIdHook);
- } else {
- return nullptr;
- }
- case GRALLOC1_FUNCTION_RETAIN:
- return asFP<GRALLOC1_PFN_RETAIN>(
- managementHook<&Gralloc1On0Adapter::retain>);
- case GRALLOC1_FUNCTION_RELEASE:
- return asFP<GRALLOC1_PFN_RELEASE>(
- managementHook<&Gralloc1On0Adapter::release>);
- case GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER:
- return asFP<GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER>(
- retainGraphicBufferHook);
- case GRALLOC1_FUNCTION_GET_NUM_FLEX_PLANES:
- return asFP<GRALLOC1_PFN_GET_NUM_FLEX_PLANES>(
- bufferHook<decltype(&Buffer::getNumFlexPlanes),
- &Buffer::getNumFlexPlanes, uint32_t*>);
- case GRALLOC1_FUNCTION_LOCK:
- return asFP<GRALLOC1_PFN_LOCK>(
- lockHook<void*, &Gralloc1On0Adapter::lock>);
- case GRALLOC1_FUNCTION_LOCK_FLEX:
- return asFP<GRALLOC1_PFN_LOCK_FLEX>(
- lockHook<struct android_flex_layout,
- &Gralloc1On0Adapter::lockFlex>);
- case GRALLOC1_FUNCTION_LOCK_YCBCR:
- return asFP<GRALLOC1_PFN_LOCK_YCBCR>(
- lockHook<struct android_ycbcr,
- &Gralloc1On0Adapter::lockYCbCr>);
- case GRALLOC1_FUNCTION_UNLOCK:
- return asFP<GRALLOC1_PFN_UNLOCK>(unlockHook);
- case GRALLOC1_FUNCTION_INVALID:
- ALOGE("Invalid function descriptor");
- return nullptr;
- }
-
- ALOGE("Unknown function descriptor: %d", intDescriptor);
- return nullptr;
-}
-
-void Gralloc1On0Adapter::dump(uint32_t* outSize, char* outBuffer)
-{
- ALOGV("dump(%u (%p), %p", outSize ? *outSize : 0, outSize, outBuffer);
-
- if (!mDevice->dump) {
- // dump is optional on gralloc0 implementations
- *outSize = 0;
- return;
- }
-
- if (!outBuffer) {
- constexpr int32_t BUFFER_LENGTH = 4096;
- char buffer[BUFFER_LENGTH] = {};
- mDevice->dump(mDevice, buffer, BUFFER_LENGTH);
- buffer[BUFFER_LENGTH - 1] = 0; // Ensure the buffer is null-terminated
- size_t actualLength = std::strlen(buffer);
- mCachedDump.resize(actualLength);
- std::copy_n(buffer, actualLength, mCachedDump.begin());
- *outSize = static_cast<uint32_t>(actualLength);
- } else {
- *outSize = std::min(*outSize,
- static_cast<uint32_t>(mCachedDump.size()));
- outBuffer = std::copy_n(mCachedDump.cbegin(), *outSize, outBuffer);
- }
-}
-
-gralloc1_error_t Gralloc1On0Adapter::createDescriptor(
- gralloc1_buffer_descriptor_t* outDescriptor)
-{
- auto descriptorId = sNextBufferDescriptorId++;
- std::lock_guard<std::mutex> lock(mDescriptorMutex);
- mDescriptors.emplace(descriptorId,
- std::make_shared<Descriptor>(this, descriptorId));
-
- ALOGV("Created descriptor %" PRIu64, descriptorId);
-
- *outDescriptor = descriptorId;
- return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::destroyDescriptor(
- gralloc1_buffer_descriptor_t descriptor)
-{
- ALOGV("Destroying descriptor %" PRIu64, descriptor);
-
- std::lock_guard<std::mutex> lock(mDescriptorMutex);
- if (mDescriptors.count(descriptor) == 0) {
- return GRALLOC1_ERROR_BAD_DESCRIPTOR;
- }
-
- mDescriptors.erase(descriptor);
- return GRALLOC1_ERROR_NONE;
-}
-
-Gralloc1On0Adapter::Buffer::Buffer(buffer_handle_t handle,
- gralloc1_backing_store_t store, const Descriptor& descriptor,
- uint32_t stride, bool wasAllocated)
- : mHandle(handle),
- mReferenceCount(1),
- mStore(store),
- mDescriptor(descriptor),
- mStride(stride),
- mWasAllocated(wasAllocated) {}
-
-gralloc1_error_t Gralloc1On0Adapter::allocate(
- const std::shared_ptr<Descriptor>& descriptor,
- gralloc1_backing_store_t store,
- buffer_handle_t* outBufferHandle)
-{
- ALOGV("allocate(%" PRIu64 ", %#" PRIx64 ")", descriptor->id, store);
-
- // If this function is being called, it's because we handed out its function
- // pointer, which only occurs when mDevice has been loaded successfully and
- // we are permitted to allocate
-
- int usage = static_cast<int>(descriptor->producerUsage) |
- static_cast<int>(descriptor->consumerUsage);
- buffer_handle_t handle = nullptr;
- int stride = 0;
- ALOGV("Calling alloc(%p, %u, %u, %i, %u)", mDevice, descriptor->width,
- descriptor->height, descriptor->format, usage);
- auto error = mDevice->alloc(mDevice,
- static_cast<int>(descriptor->width),
- static_cast<int>(descriptor->height), descriptor->format,
- usage, &handle, &stride);
- if (error != 0) {
- ALOGE("gralloc0 allocation failed: %d (%s)", error,
- strerror(-error));
- return GRALLOC1_ERROR_NO_RESOURCES;
- }
-
- *outBufferHandle = handle;
- auto buffer = std::make_shared<Buffer>(handle, store, *descriptor, stride,
- true);
-
- std::lock_guard<std::mutex> lock(mBufferMutex);
- mBuffers.emplace(handle, std::move(buffer));
-
- return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::allocateWithIdHook(
- gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptorId,
- gralloc1_backing_store_t store, buffer_handle_t* outBuffer)
-{
- auto adapter = getAdapter(device);
-
- auto descriptor = adapter->getDescriptor(descriptorId);
- if (!descriptor) {
- return GRALLOC1_ERROR_BAD_DESCRIPTOR;
- }
-
- buffer_handle_t bufferHandle = nullptr;
- auto error = adapter->allocate(descriptor, store, &bufferHandle);
- if (error != GRALLOC1_ERROR_NONE) {
- return error;
- }
-
- *outBuffer = bufferHandle;
- return error;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::retain(
- const std::shared_ptr<Buffer>& buffer)
-{
- std::lock_guard<std::mutex> lock(mBufferMutex);
- buffer->retain();
- return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::release(
- const std::shared_ptr<Buffer>& buffer)
-{
- std::lock_guard<std::mutex> lock(mBufferMutex);
- if (!buffer->release()) {
- return GRALLOC1_ERROR_NONE;
- }
-
- buffer_handle_t handle = buffer->getHandle();
- if (buffer->wasAllocated()) {
- ALOGV("Calling free(%p)", handle);
- int result = mDevice->free(mDevice, handle);
- if (result != 0) {
- ALOGE("gralloc0 free failed: %d", result);
- }
- } else {
- ALOGV("Calling unregisterBuffer(%p)", handle);
- int result = mModule->unregisterBuffer(mModule, handle);
- if (result != 0) {
- ALOGE("gralloc0 unregister failed: %d", result);
- }
- }
-
- mBuffers.erase(handle);
- return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::retain(
- const android::GraphicBuffer* graphicBuffer)
-{
- ALOGV("retainGraphicBuffer(%p, %#" PRIx64 ")",
- graphicBuffer->getNativeBuffer()->handle, graphicBuffer->getId());
-
- buffer_handle_t handle = graphicBuffer->getNativeBuffer()->handle;
- std::lock_guard<std::mutex> lock(mBufferMutex);
- if (mBuffers.count(handle) != 0) {
- mBuffers[handle]->retain();
- return GRALLOC1_ERROR_NONE;
- }
-
- ALOGV("Calling registerBuffer(%p)", handle);
- int result = mModule->registerBuffer(mModule, handle);
- if (result != 0) {
- ALOGE("gralloc0 register failed: %d", result);
- return GRALLOC1_ERROR_NO_RESOURCES;
- }
-
- Descriptor descriptor{this, sNextBufferDescriptorId++};
- descriptor.setDimensions(graphicBuffer->getWidth(),
- graphicBuffer->getHeight());
- descriptor.setFormat(graphicBuffer->getPixelFormat());
- descriptor.setProducerUsage(
- static_cast<gralloc1_producer_usage_t>(graphicBuffer->getUsage()));
- descriptor.setConsumerUsage(
- static_cast<gralloc1_consumer_usage_t>(graphicBuffer->getUsage()));
- auto buffer = std::make_shared<Buffer>(handle,
- static_cast<gralloc1_backing_store_t>(graphicBuffer->getId()),
- descriptor, graphicBuffer->getStride(), false);
- mBuffers.emplace(handle, std::move(buffer));
- return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::lock(
- const std::shared_ptr<Buffer>& buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t& accessRegion, void** outData,
- const sp<Fence>& acquireFence)
-{
- if (mMinorVersion >= 3) {
- int result = mModule->lockAsync(mModule, buffer->getHandle(),
- static_cast<int32_t>(producerUsage | consumerUsage),
- accessRegion.left, accessRegion.top, accessRegion.width,
- accessRegion.height, outData, acquireFence->dup());
- if (result != 0) {
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- } else {
- acquireFence->waitForever("Gralloc1On0Adapter::lock");
- int result = mModule->lock(mModule, buffer->getHandle(),
- static_cast<int32_t>(producerUsage | consumerUsage),
- accessRegion.left, accessRegion.top, accessRegion.width,
- accessRegion.height, outData);
- ALOGV("gralloc0 lock returned %d", result);
- if (result != 0) {
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- }
- return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::lockFlex(
- const std::shared_ptr<Buffer>& /*buffer*/,
- gralloc1_producer_usage_t /*producerUsage*/,
- gralloc1_consumer_usage_t /*consumerUsage*/,
- const gralloc1_rect_t& /*accessRegion*/,
- struct android_flex_layout* /*outData*/,
- const sp<Fence>& /*acquireFence*/)
-{
- // TODO
- return GRALLOC1_ERROR_UNSUPPORTED;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::lockYCbCr(
- const std::shared_ptr<Buffer>& buffer,
- gralloc1_producer_usage_t producerUsage,
- gralloc1_consumer_usage_t consumerUsage,
- const gralloc1_rect_t& accessRegion, struct android_ycbcr* outData,
- const sp<Fence>& acquireFence)
-{
- if (mMinorVersion >= 3 && mModule->lockAsync_ycbcr) {
- int result = mModule->lockAsync_ycbcr(mModule, buffer->getHandle(),
- static_cast<int>(producerUsage | consumerUsage),
- accessRegion.left, accessRegion.top, accessRegion.width,
- accessRegion.height, outData, acquireFence->dup());
- if (result != 0) {
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- } else if (mModule->lock_ycbcr) {
- acquireFence->waitForever("Gralloc1On0Adapter::lockYCbCr");
- int result = mModule->lock_ycbcr(mModule, buffer->getHandle(),
- static_cast<int>(producerUsage | consumerUsage),
- accessRegion.left, accessRegion.top, accessRegion.width,
- accessRegion.height, outData);
- ALOGV("gralloc0 lockYCbCr returned %d", result);
- if (result != 0) {
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- } else {
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
-
- return GRALLOC1_ERROR_NONE;
-}
-
-gralloc1_error_t Gralloc1On0Adapter::unlock(
- const std::shared_ptr<Buffer>& buffer,
- sp<Fence>* outReleaseFence)
-{
- if (mMinorVersion >= 3) {
- int fenceFd = -1;
- int result = mModule->unlockAsync(mModule, buffer->getHandle(),
- &fenceFd);
- if (result != 0) {
- close(fenceFd);
- ALOGE("gralloc0 unlockAsync failed: %d", result);
- } else {
- *outReleaseFence = new Fence(fenceFd);
- }
- } else {
- int result = mModule->unlock(mModule, buffer->getHandle());
- if (result != 0) {
- ALOGE("gralloc0 unlock failed: %d", result);
- }
- }
- return GRALLOC1_ERROR_NONE;
-}
-
-std::shared_ptr<Gralloc1On0Adapter::Descriptor>
-Gralloc1On0Adapter::getDescriptor(gralloc1_buffer_descriptor_t descriptorId)
-{
- std::lock_guard<std::mutex> lock(mDescriptorMutex);
- if (mDescriptors.count(descriptorId) == 0) {
- return nullptr;
- }
-
- return mDescriptors[descriptorId];
-}
-
-std::shared_ptr<Gralloc1On0Adapter::Buffer> Gralloc1On0Adapter::getBuffer(
- buffer_handle_t bufferHandle)
-{
- std::lock_guard<std::mutex> lock(mBufferMutex);
- if (mBuffers.count(bufferHandle) == 0) {
- return nullptr;
- }
-
- return mBuffers[bufferHandle];
-}
-
-std::atomic<gralloc1_buffer_descriptor_t>
- Gralloc1On0Adapter::sNextBufferDescriptorId(1);
-
-} // namespace android
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
new file mode 100644
index 0000000000..87dbaf47d3
--- /dev/null
+++ b/libs/ui/Gralloc2.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Gralloc2"
+
+#include <hwbinder/IPCThreadState.h>
+#include <ui/Gralloc2.h>
+
+#include <log/log.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
+#include <sync/sync.h>
+#pragma clang diagnostic pop
+
+namespace android {
+
+namespace Gralloc2 {
+
+static constexpr Error kTransactionError = Error::NO_RESOURCES;
+
+Mapper::Mapper()
+{
+ mMapper = IMapper::getService();
+ if (mMapper == nullptr || mMapper->isRemote()) {
+ LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+ }
+}
+
+Error Mapper::createDescriptor(
+ const IMapper::BufferDescriptorInfo& descriptorInfo,
+ BufferDescriptor* outDescriptor) const
+{
+ Error error;
+ auto ret = mMapper->createDescriptor(descriptorInfo,
+ [&](const auto& tmpError, const auto& tmpDescriptor)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outDescriptor = tmpDescriptor;
+ });
+
+ return (ret.isOk()) ? error : kTransactionError;
+}
+
+Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+ buffer_handle_t* outBufferHandle) const
+{
+ Error error;
+ auto ret = mMapper->importBuffer(rawHandle,
+ [&](const auto& tmpError, const auto& tmpBuffer)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
+ });
+
+ return (ret.isOk()) ? error : kTransactionError;
+}
+
+void Mapper::freeBuffer(buffer_handle_t bufferHandle) const
+{
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+ auto ret = mMapper->freeBuffer(buffer);
+
+ auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+ ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d",
+ buffer, error);
+}
+
+Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
+ const IMapper::Rect& accessRegion,
+ int acquireFence, void** outData) const
+{
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ // put acquireFence in a hidl_handle
+ hardware::hidl_handle acquireFenceHandle;
+ NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+ if (acquireFence >= 0) {
+ auto h = native_handle_init(acquireFenceStorage, 1, 0);
+ h->data[0] = acquireFence;
+ acquireFenceHandle = h;
+ }
+
+ Error error;
+ auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpData)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outData = tmpData;
+ });
+
+ // we own acquireFence even on errors
+ if (acquireFence >= 0) {
+ close(acquireFence);
+ }
+
+ return (ret.isOk()) ? error : kTransactionError;
+}
+
+Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
+ const IMapper::Rect& accessRegion,
+ int acquireFence, YCbCrLayout* outLayout) const
+{
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ // put acquireFence in a hidl_handle
+ hardware::hidl_handle acquireFenceHandle;
+ NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+ if (acquireFence >= 0) {
+ auto h = native_handle_init(acquireFenceStorage, 1, 0);
+ h->data[0] = acquireFence;
+ acquireFenceHandle = h;
+ }
+
+ Error error;
+ auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion,
+ acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpLayout)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outLayout = tmpLayout;
+ });
+
+ // we own acquireFence even on errors
+ if (acquireFence >= 0) {
+ close(acquireFence);
+ }
+
+ return (ret.isOk()) ? error : kTransactionError;
+}
+
+int Mapper::unlock(buffer_handle_t bufferHandle) const
+{
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ int releaseFence = -1;
+ Error error;
+ auto ret = mMapper->unlock(buffer,
+ [&](const auto& tmpError, const auto& tmpReleaseFence)
+ {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ auto fenceHandle = tmpReleaseFence.getNativeHandle();
+ if (fenceHandle && fenceHandle->numFds == 1) {
+ int fd = dup(fenceHandle->data[0]);
+ if (fd >= 0) {
+ releaseFence = fd;
+ } else {
+ ALOGD("failed to dup unlock release fence");
+ sync_wait(fenceHandle->data[0], -1);
+ }
+ }
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("unlock(%p) failed with %d", buffer, error);
+ }
+
+ return releaseFence;
+}
+
+Allocator::Allocator(const Mapper& mapper)
+ : mMapper(mapper)
+{
+ mAllocator = IAllocator::getService();
+ if (mAllocator == nullptr) {
+ LOG_ALWAYS_FATAL("gralloc-alloc is missing");
+ }
+}
+
+std::string Allocator::dumpDebugInfo() const
+{
+ std::string debugInfo;
+
+ mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
+ debugInfo = tmpDebugInfo.c_str();
+ });
+
+ return debugInfo;
+}
+
+Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles) const
+{
+ Error error;
+ auto ret = mAllocator->allocate(descriptor, count,
+ [&](const auto& tmpError, const auto& tmpStride,
+ const auto& tmpBuffers) {
+ error = tmpError;
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ // import buffers
+ for (uint32_t i = 0; i < count; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != Error::NONE) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
+ }
+ }
+
+ *outStride = tmpStride;
+ });
+
+ // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+ hardware::IPCThreadState::self()->flushCommands();
+
+ return (ret.isOk()) ? error : kTransactionError;
+}
+
+} // namespace Gralloc2
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 97b948d979..ee85c9bad9 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -16,17 +16,15 @@
#define LOG_TAG "GraphicBuffer"
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
+#include <ui/GraphicBuffer.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
+#include <cutils/atomic.h>
-#include <ui/GraphicBuffer.h>
+#include <grallocusage/GrallocUsageConversion.h>
+
+#include <ui/Gralloc2.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
-#include <ui/PixelFormat.h>
namespace android {
@@ -41,6 +39,10 @@ static uint64_t getUniqueId() {
return id;
}
+sp<GraphicBuffer> GraphicBuffer::from(ANativeWindowBuffer* anwb) {
+ return static_cast<GraphicBuffer *>(anwb);
+}
+
GraphicBuffer::GraphicBuffer()
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
@@ -50,51 +52,46 @@ GraphicBuffer::GraphicBuffer()
stride =
format =
usage = 0;
+ layerCount = 0;
handle = NULL;
}
+// deprecated
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inUsage, std::string requestorName)
- : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
+ : GraphicBuffer(inWidth, inHeight, inFormat, 1, static_cast<uint64_t>(inUsage),
+ requestorName)
{
- width =
- height =
- stride =
- format =
- usage = 0;
- handle = NULL;
- mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage,
- std::move(requestorName));
}
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage, uint32_t inStride,
- native_handle_t* inHandle, bool keepOwnership)
- : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
- mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
+ PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage,
+ std::string requestorName)
+ : GraphicBuffer()
{
- width = static_cast<int>(inWidth);
- height = static_cast<int>(inHeight);
- stride = static_cast<int>(inStride);
- format = inFormat;
- usage = static_cast<int>(inUsage);
- handle = inHandle;
+ mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount,
+ usage, std::move(requestorName));
}
-GraphicBuffer::GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership)
- : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
- mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mWrappedBuffer(buffer), mId(getUniqueId()),
- mGenerationNumber(0)
+// deprecated
+GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
+ PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+ uint32_t inStride, native_handle_t* inHandle, bool keepOwnership)
+ : GraphicBuffer(inHandle, keepOwnership ? TAKE_HANDLE : WRAP_HANDLE,
+ inWidth, inHeight, inFormat, inLayerCount, static_cast<uint64_t>(inUsage),
+ inStride)
{
- width = buffer->width;
- height = buffer->height;
- stride = buffer->stride;
- format = buffer->format;
- usage = buffer->usage;
- handle = buffer->handle;
+}
+
+GraphicBuffer::GraphicBuffer(const native_handle_t* handle,
+ HandleWrapMethod method, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint32_t stride)
+ : GraphicBuffer()
+{
+ mInitCheck = initWithHandle(handle, method, width, height, format,
+ layerCount, usage, stride);
}
GraphicBuffer::~GraphicBuffer()
@@ -107,15 +104,12 @@ GraphicBuffer::~GraphicBuffer()
void GraphicBuffer::free_handle()
{
if (mOwner == ownHandle) {
- mBufferMapper.unregisterBuffer(handle);
- native_handle_close(handle);
- native_handle_delete(const_cast<native_handle*>(handle));
+ mBufferMapper.freeBuffer(handle);
} else if (mOwner == ownData) {
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
}
handle = NULL;
- mWrappedBuffer = 0;
}
status_t GraphicBuffer::initCheck() const {
@@ -135,7 +129,7 @@ ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const
}
status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage)
+ PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage)
{
if (mOwner != ownData)
return INVALID_OPERATION;
@@ -144,6 +138,7 @@ status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
static_cast<int>(inWidth) == width &&
static_cast<int>(inHeight) == height &&
inFormat == format &&
+ inLayerCount == layerCount &&
static_cast<int>(inUsage) == usage)
return NO_ERROR;
@@ -152,36 +147,78 @@ status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
allocator.free(handle);
handle = 0;
}
- return initSize(inWidth, inHeight, inFormat, inUsage, "[Reallocation]");
+ return initWithSize(inWidth, inHeight, inFormat, inLayerCount,
+ inUsage, "[Reallocation]");
}
bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage)
+ PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage)
{
if (static_cast<int>(inWidth) != width) return true;
if (static_cast<int>(inHeight) != height) return true;
if (inFormat != format) return true;
+ if (inLayerCount != layerCount) return true;
if ((static_cast<uint32_t>(usage) & inUsage) != inUsage) return true;
return false;
}
-status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight,
- PixelFormat inFormat, uint32_t inUsage, std::string requestorName)
+status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,
+ PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
+ std::string requestorName)
{
GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
uint32_t outStride = 0;
- status_t err = allocator.allocate(inWidth, inHeight, inFormat, inUsage,
- &handle, &outStride, mId, std::move(requestorName));
+ status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,
+ inUsage, &handle, &outStride, mId,
+ std::move(requestorName));
if (err == NO_ERROR) {
width = static_cast<int>(inWidth);
height = static_cast<int>(inHeight);
format = inFormat;
+ layerCount = inLayerCount;
usage = static_cast<int>(inUsage);
stride = static_cast<int>(outStride);
}
return err;
}
+status_t GraphicBuffer::initWithHandle(const native_handle_t* handle,
+ HandleWrapMethod method, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t stride)
+{
+ ANativeWindowBuffer::width = static_cast<int>(width);
+ ANativeWindowBuffer::height = static_cast<int>(height);
+ ANativeWindowBuffer::stride = static_cast<int>(stride);
+ ANativeWindowBuffer::format = format;
+ ANativeWindowBuffer::usage = static_cast<int>(usage);
+
+ ANativeWindowBuffer::layerCount = layerCount;
+
+ mOwner = (method == WRAP_HANDLE) ? ownNone : ownHandle;
+
+ if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) {
+ buffer_handle_t importedHandle;
+ status_t err = mBufferMapper.importBuffer(handle, &importedHandle);
+ if (err != NO_ERROR) {
+ initWithHandle(nullptr, WRAP_HANDLE, 0, 0, 0, 0, 0, 0);
+
+ return err;
+ }
+
+ if (method == TAKE_UNREGISTERED_HANDLE) {
+ native_handle_close(handle);
+ native_handle_delete(const_cast<native_handle_t*>(handle));
+ }
+
+ handle = importedHandle;
+ }
+
+ ANativeWindowBuffer::handle = handle;
+
+ return NO_ERROR;
+}
+
status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr)
{
const Rect lockBounds(width, height);
@@ -239,6 +276,12 @@ status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd)
status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect,
void** vaddr, int fenceFd)
{
+ return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd);
+}
+
+status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage,
+ uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd)
+{
if (rect.left < 0 || rect.right > width ||
rect.top < 0 || rect.bottom > height) {
ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
@@ -246,8 +289,8 @@ status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect,
width, height);
return BAD_VALUE;
}
- status_t res = getBufferMapper().lockAsync(handle, inUsage, rect, vaddr,
- fenceFd);
+ status_t res = getBufferMapper().lockAsync(handle, inProducerUsage,
+ inConsumerUsage, rect, vaddr, fenceFd);
return res;
}
@@ -281,7 +324,7 @@ status_t GraphicBuffer::unlockAsync(int *fenceFd)
}
size_t GraphicBuffer::getFlattenedSize() const {
- return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int);
+ return static_cast<size_t>(12 + (handle ? handle->numInts : 0)) * sizeof(int);
}
size_t GraphicBuffer::getFdCount() const {
@@ -301,19 +344,20 @@ status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t&
buf[2] = height;
buf[3] = stride;
buf[4] = format;
- buf[5] = usage;
- buf[6] = static_cast<int32_t>(mId >> 32);
- buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
- buf[8] = static_cast<int32_t>(mGenerationNumber);
- buf[9] = 0;
+ buf[5] = static_cast<int32_t>(layerCount);
+ buf[6] = usage;
+ buf[7] = static_cast<int32_t>(mId >> 32);
+ buf[8] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
+ buf[9] = static_cast<int32_t>(mGenerationNumber);
buf[10] = 0;
+ buf[11] = 0;
if (handle) {
- buf[9] = handle->numFds;
- buf[10] = handle->numInts;
+ buf[10] = handle->numFds;
+ buf[11] = handle->numInts;
memcpy(fds, handle->data,
static_cast<size_t>(handle->numFds) * sizeof(int));
- memcpy(&buf[11], handle->data + handle->numFds,
+ memcpy(&buf[12], handle->data + handle->numFds,
static_cast<size_t>(handle->numInts) * sizeof(int));
}
@@ -329,28 +373,28 @@ status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t&
status_t GraphicBuffer::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
- if (size < 11 * sizeof(int)) return NO_MEMORY;
+ if (size < 12 * sizeof(int)) return NO_MEMORY;
int const* buf = static_cast<int const*>(buffer);
if (buf[0] != 'GBFR') return BAD_TYPE;
- const size_t numFds = static_cast<size_t>(buf[9]);
- const size_t numInts = static_cast<size_t>(buf[10]);
+ const size_t numFds = static_cast<size_t>(buf[10]);
+ const size_t numInts = static_cast<size_t>(buf[11]);
// Limit the maxNumber to be relatively small. The number of fds or ints
// should not come close to this number, and the number itself was simply
// chosen to be high enough to not cause issues and low enough to prevent
// overflow problems.
const size_t maxNumber = 4096;
- if (numFds >= maxNumber || numInts >= (maxNumber - 11)) {
- width = height = stride = format = usage = 0;
+ if (numFds >= maxNumber || numInts >= (maxNumber - 12)) {
+ width = height = stride = format = layerCount = usage = 0;
handle = NULL;
ALOGE("unflatten: numFds or numInts is too large: %zd, %zd",
numFds, numInts);
return BAD_VALUE;
}
- const size_t sizeNeeded = (11 + numInts) * sizeof(int);
+ const size_t sizeNeeded = (12 + numInts) * sizeof(int);
if (size < sizeNeeded) return NO_MEMORY;
size_t fdCountNeeded = numFds;
@@ -366,39 +410,45 @@ status_t GraphicBuffer::unflatten(
height = buf[2];
stride = buf[3];
format = buf[4];
- usage = buf[5];
+ layerCount = static_cast<uintptr_t>(buf[5]);
+ usage = buf[6];
native_handle* h = native_handle_create(
static_cast<int>(numFds), static_cast<int>(numInts));
if (!h) {
- width = height = stride = format = usage = 0;
+ width = height = stride = format = layerCount = usage = 0;
handle = NULL;
ALOGE("unflatten: native_handle_create failed");
return NO_MEMORY;
}
memcpy(h->data, fds, numFds * sizeof(int));
- memcpy(h->data + numFds, &buf[11], numInts * sizeof(int));
+ memcpy(h->data + numFds, &buf[12], numInts * sizeof(int));
handle = h;
} else {
- width = height = stride = format = usage = 0;
+ width = height = stride = format = layerCount = usage = 0;
handle = NULL;
}
- mId = static_cast<uint64_t>(buf[6]) << 32;
- mId |= static_cast<uint32_t>(buf[7]);
+ mId = static_cast<uint64_t>(buf[7]) << 32;
+ mId |= static_cast<uint32_t>(buf[8]);
- mGenerationNumber = static_cast<uint32_t>(buf[8]);
+ mGenerationNumber = static_cast<uint32_t>(buf[9]);
mOwner = ownHandle;
if (handle != 0) {
- status_t err = mBufferMapper.registerBuffer(this);
+ buffer_handle_t importedHandle;
+ status_t err = mBufferMapper.importBuffer(handle, &importedHandle);
if (err != NO_ERROR) {
- width = height = stride = format = usage = 0;
+ width = height = stride = format = layerCount = usage = 0;
handle = NULL;
ALOGE("unflatten: registerBuffer failed: %s (%d)",
strerror(-err), err);
return err;
}
+
+ native_handle_close(handle);
+ native_handle_delete(const_cast<native_handle_t*>(handle));
+ handle = importedHandle;
}
buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 75dd8df9f4..eaba1ed1aa 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -18,13 +18,19 @@
#define LOG_TAG "GraphicBufferAllocator"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <ui/GraphicBufferAllocator.h>
+
+#include <stdio.h>
+
+#include <grallocusage/GrallocUsageConversion.h>
+
#include <log/log.h>
#include <utils/Singleton.h>
#include <utils/String8.h>
#include <utils/Trace.h>
-#include <ui/GraphicBufferAllocator.h>
-#include <ui/Gralloc1On0Adapter.h>
+#include <ui/Gralloc2.h>
+#include <ui/GraphicBufferMapper.h>
namespace android {
// ---------------------------------------------------------------------------
@@ -36,8 +42,11 @@ KeyedVector<buffer_handle_t,
GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
GraphicBufferAllocator::GraphicBufferAllocator()
- : mLoader(std::make_unique<Gralloc1::Loader>()),
- mDevice(mLoader->getDevice()) {}
+ : mMapper(GraphicBufferMapper::getInstance()),
+ mAllocator(std::make_unique<Gralloc2::Allocator>(
+ mMapper.getGrallocMapper()))
+{
+}
GraphicBufferAllocator::~GraphicBufferAllocator() {}
@@ -54,22 +63,25 @@ void GraphicBufferAllocator::dump(String8& result) const
for (size_t i=0 ; i<c ; i++) {
const alloc_rec_t& rec(list.valueAt(i));
if (rec.size) {
- snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+ snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
+ " | %s\n",
list.keyAt(i), rec.size/1024.0,
- rec.width, rec.stride, rec.height, rec.format, rec.usage,
- rec.requestorName.c_str());
+ rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+ rec.usage, rec.requestorName.c_str());
} else {
- snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+ snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
+ " | %s\n",
list.keyAt(i),
- rec.width, rec.stride, rec.height, rec.format, rec.usage,
- rec.requestorName.c_str());
+ rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+ rec.usage, rec.requestorName.c_str());
}
result.append(buffer);
total += rec.size;
}
snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0);
result.append(buffer);
- std::string deviceDump = mDevice->dump();
+
+ std::string deviceDump = mAllocator->dumpDebugInfo();
result.append(deviceDump.c_str(), deviceDump.size());
}
@@ -81,8 +93,9 @@ void GraphicBufferAllocator::dumpToSystemLog()
}
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
- PixelFormat format, uint32_t usage, buffer_handle_t* handle,
- uint32_t* stride, uint64_t graphicBufferId, std::string requestorName)
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ uint64_t /*graphicBufferId*/, std::string requestorName)
{
ATRACE_CALL();
@@ -91,46 +104,19 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
if (!width || !height)
width = height = 1;
- // Filter out any usage bits that should not be passed to the gralloc module
- usage &= GRALLOC_USAGE_ALLOC_MASK;
-
- auto descriptor = mDevice->createDescriptor();
- auto error = descriptor->setDimensions(width, height);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set dimensions to (%u, %u): %d", width, height, error);
- return BAD_VALUE;
- }
- error = descriptor->setFormat(static_cast<android_pixel_format_t>(format));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set format to %d: %d", format, error);
- return BAD_VALUE;
- }
- error = descriptor->setProducerUsage(
- static_cast<gralloc1_producer_usage_t>(usage));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set producer usage to %u: %d", usage, error);
- return BAD_VALUE;
- }
- error = descriptor->setConsumerUsage(
- static_cast<gralloc1_consumer_usage_t>(usage));
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to set consumer usage to %u: %d", usage, error);
- return BAD_VALUE;
- }
-
- error = mDevice->allocate(descriptor, graphicBufferId, handle);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d",
- width, height, format, usage, error);
- return NO_MEMORY;
- }
+ // Ensure that layerCount is valid.
+ if (layerCount < 1)
+ layerCount = 1;
- error = mDevice->getStride(*handle, stride);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGW("Failed to get stride from buffer: %d", error);
- }
+ Gralloc2::IMapper::BufferDescriptorInfo info = {};
+ info.width = width;
+ info.height = height;
+ info.layerCount = layerCount;
+ info.format = static_cast<Gralloc2::PixelFormat>(format);
+ info.usage = usage;
- if (error == NO_ERROR) {
+ Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
+ if (error == Gralloc2::Error::NONE) {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
uint32_t bpp = bytesPerPixel(format);
@@ -139,23 +125,29 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
rec.height = height;
rec.stride = *stride;
rec.format = format;
+ rec.layerCount = layerCount;
rec.usage = usage;
rec.size = static_cast<size_t>(height * (*stride) * bpp);
rec.requestorName = std::move(requestorName);
list.add(*handle, rec);
- }
- return NO_ERROR;
+ return NO_ERROR;
+ } else {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": %d",
+ width, height, layerCount, format, usage,
+ error);
+ return NO_MEMORY;
+ }
}
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
{
ATRACE_CALL();
- auto error = mDevice->release(handle);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("Failed to free buffer: %d", error);
- }
+ // We allocated a buffer from the allocator and imported it into the
+ // mapper to get the handle. We just need to free the handle now.
+ mMapper.freeBuffer(handle);
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 481d43ce42..b9fa6400f5 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -18,8 +18,9 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
-#include <stdint.h>
-#include <errno.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include <grallocusage/GrallocUsageConversion.h>
// We would eliminate the non-conforming zero-length array, but we can't since
// this is effectively included from the Linux kernel
@@ -28,13 +29,11 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
-#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Trace.h>
-#include <ui/Gralloc1On0Adapter.h>
-#include <ui/GraphicBufferMapper.h>
-#include <ui/Rect.h>
+#include <ui/Gralloc2.h>
+#include <ui/GraphicBuffer.h>
#include <system/graphics.h>
@@ -44,46 +43,35 @@ namespace android {
ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
GraphicBufferMapper::GraphicBufferMapper()
- : mLoader(std::make_unique<Gralloc1::Loader>()),
- mDevice(mLoader->getDevice()) {}
-
-
-
-status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle)
+ : mMapper(std::make_unique<const Gralloc2::Mapper>())
{
- ATRACE_CALL();
-
- gralloc1_error_t error = mDevice->retain(handle);
- ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
- handle, error);
-
- return error;
}
-status_t GraphicBufferMapper::registerBuffer(const GraphicBuffer* buffer)
+status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
+ buffer_handle_t* outHandle)
{
ATRACE_CALL();
- gralloc1_error_t error = mDevice->retain(buffer);
- ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
- buffer->getNativeBuffer()->handle, error);
+ Gralloc2::Error error = mMapper->importBuffer(
+ hardware::hidl_handle(rawHandle), outHandle);
- return error;
+ ALOGW_IF(error != Gralloc2::Error::NONE, "importBuffer(%p) failed: %d",
+ rawHandle, error);
+
+ return static_cast<status_t>(error);
}
-status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle)
+status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle)
{
ATRACE_CALL();
- gralloc1_error_t error = mDevice->release(handle);
- ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d",
- handle, error);
+ mMapper->freeBuffer(handle);
- return error;
+ return NO_ERROR;
}
-static inline gralloc1_rect_t asGralloc1Rect(const Rect& rect) {
- gralloc1_rect_t outRect{};
+static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) {
+ Gralloc2::IMapper::Rect outRect{};
outRect.left = rect.left;
outRect.top = rect.top;
outRect.width = rect.width();
@@ -117,18 +105,24 @@ status_t GraphicBufferMapper::unlock(buffer_handle_t handle)
status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd)
{
+ return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd);
+}
+
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
+ uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
+ void** vaddr, int fenceFd)
+{
ATRACE_CALL();
- gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
- sp<Fence> fence = new Fence(fenceFd);
- gralloc1_error_t error = mDevice->lock(handle,
- static_cast<gralloc1_producer_usage_t>(usage),
- static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, vaddr, fence);
- ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lock(%p, ...) failed: %d", handle,
- error);
+ const uint64_t usage = static_cast<uint64_t>(
+ android_convertGralloc1To0Usage(producerUsage, consumerUsage));
+ Gralloc2::Error error = mMapper->lock(handle, usage,
+ asGralloc2Rect(bounds), fenceFd, vaddr);
- return error;
+ ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d",
+ handle, error);
+
+ return static_cast<status_t>(error);
}
static inline bool isValidYCbCrPlane(const android_flex_plane_t& plane) {
@@ -159,132 +153,28 @@ status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
{
ATRACE_CALL();
- gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
- sp<Fence> fence = new Fence(fenceFd);
-
- if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
- gralloc1_error_t error = mDevice->lockYCbCr(handle,
- static_cast<gralloc1_producer_usage_t>(usage),
- static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, ycbcr, fence);
- ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lockYCbCr(%p, ...) failed: %d",
- handle, error);
- return error;
+ Gralloc2::YCbCrLayout layout;
+ Gralloc2::Error error = mMapper->lock(handle, usage,
+ asGralloc2Rect(bounds), fenceFd, &layout);
+ if (error == Gralloc2::Error::NONE) {
+ ycbcr->y = layout.y;
+ ycbcr->cb = layout.cb;
+ ycbcr->cr = layout.cr;
+ ycbcr->ystride = static_cast<size_t>(layout.yStride);
+ ycbcr->cstride = static_cast<size_t>(layout.cStride);
+ ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
}
- uint32_t numPlanes = 0;
- gralloc1_error_t error = mDevice->getNumFlexPlanes(handle, &numPlanes);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGV("Failed to retrieve number of flex planes: %d", error);
- return error;
- }
- if (numPlanes < 3) {
- ALOGV("Not enough planes for YCbCr (%u found)", numPlanes);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
-
- std::vector<android_flex_plane_t> planes(numPlanes);
- android_flex_layout_t flexLayout{};
- flexLayout.num_planes = numPlanes;
- flexLayout.planes = planes.data();
-
- error = mDevice->lockFlex(handle,
- static_cast<gralloc1_producer_usage_t>(usage),
- static_cast<gralloc1_consumer_usage_t>(usage),
- &accessRegion, &flexLayout, fence);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGW("lockFlex(%p, ...) failed: %d", handle, error);
- return error;
- }
- if (flexLayout.format != FLEX_FORMAT_YCbCr) {
- ALOGV("Unable to convert flex-format buffer to YCbCr");
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
-
- // Find planes
- auto yPlane = planes.cend();
- auto cbPlane = planes.cend();
- auto crPlane = planes.cend();
- for (auto planeIter = planes.cbegin(); planeIter != planes.cend();
- ++planeIter) {
- if (planeIter->component == FLEX_COMPONENT_Y) {
- yPlane = planeIter;
- } else if (planeIter->component == FLEX_COMPONENT_Cb) {
- cbPlane = planeIter;
- } else if (planeIter->component == FLEX_COMPONENT_Cr) {
- crPlane = planeIter;
- }
- }
- if (yPlane == planes.cend()) {
- ALOGV("Unable to find Y plane");
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- if (cbPlane == planes.cend()) {
- ALOGV("Unable to find Cb plane");
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- if (crPlane == planes.cend()) {
- ALOGV("Unable to find Cr plane");
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
-
- // Validate planes
- if (!isValidYCbCrPlane(*yPlane)) {
- ALOGV("Y plane is invalid");
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- if (!isValidYCbCrPlane(*cbPlane)) {
- ALOGV("Cb plane is invalid");
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- if (!isValidYCbCrPlane(*crPlane)) {
- ALOGV("Cr plane is invalid");
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- if (cbPlane->v_increment != crPlane->v_increment) {
- ALOGV("Cb and Cr planes have different step (%d vs. %d)",
- cbPlane->v_increment, crPlane->v_increment);
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
- if (cbPlane->h_increment != crPlane->h_increment) {
- ALOGV("Cb and Cr planes have different stride (%d vs. %d)",
- cbPlane->h_increment, crPlane->h_increment);
- unlock(handle);
- return GRALLOC1_ERROR_UNSUPPORTED;
- }
-
- // Pack plane data into android_ycbcr struct
- ycbcr->y = yPlane->top_left;
- ycbcr->cb = cbPlane->top_left;
- ycbcr->cr = crPlane->top_left;
- ycbcr->ystride = static_cast<size_t>(yPlane->v_increment);
- ycbcr->cstride = static_cast<size_t>(cbPlane->v_increment);
- ycbcr->chroma_step = static_cast<size_t>(cbPlane->h_increment);
-
- return error;
+ return static_cast<status_t>(error);
}
status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd)
{
ATRACE_CALL();
- sp<Fence> fence = Fence::NO_FENCE;
- gralloc1_error_t error = mDevice->unlock(handle, &fence);
- if (error != GRALLOC1_ERROR_NONE) {
- ALOGE("unlock(%p) failed: %d", handle, error);
- return error;
- }
+ *fenceFd = mMapper->unlock(handle);
- *fenceFd = fence->dup();
- return error;
+ return NO_ERROR;
}
// ---------------------------------------------------------------------------
diff --git a/libs/gui/GraphicsEnv.cpp b/libs/ui/GraphicsEnv.cpp
index 68f0f988e2..8182c07001 100644
--- a/libs/gui/GraphicsEnv.cpp
+++ b/libs/ui/GraphicsEnv.cpp
@@ -16,13 +16,18 @@
//#define LOG_NDEBUG 1
#define LOG_TAG "GraphicsEnv"
-#include <gui/GraphicsEnv.h>
+#include <ui/GraphicsEnv.h>
#include <mutex>
#include <log/log.h>
#include <nativeloader/dlext_namespaces.h>
+// TODO(b/37049319) Get this from a header once one exists
+extern "C" {
+ android_namespace_t* android_get_exported_namespace(const char*);
+}
+
namespace android {
/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
@@ -43,33 +48,19 @@ void GraphicsEnv::setDriverPath(const std::string path) {
android_namespace_t* GraphicsEnv::getDriverNamespace() {
static std::once_flag once;
std::call_once(once, [this]() {
- // TODO; In the next version of Android, all graphics drivers will be
- // loaded into a custom namespace. To minimize risk for this release,
- // only updated drivers use a custom namespace.
- //
- // Additionally, the custom namespace will be
- // ANDROID_NAMESPACE_TYPE_ISOLATED, and will only have access to a
- // subset of the system.
if (mDriverPath.empty())
return;
-
- char defaultPath[PATH_MAX];
- android_get_LD_LIBRARY_PATH(defaultPath, sizeof(defaultPath));
- size_t defaultPathLen = strlen(defaultPath);
-
- std::string path;
- path.reserve(mDriverPath.size() + 1 + defaultPathLen);
- path.append(mDriverPath);
- path.push_back(':');
- path.append(defaultPath, defaultPathLen);
-
- mDriverNamespace = android_create_namespace(
- "gfx driver",
- nullptr, // ld_library_path
- path.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED,
- nullptr, // permitted_when_isolated_path
- nullptr); // parent
+ // If the sphal namespace isn't configured for a device, don't support updatable drivers.
+ // We need a parent namespace to inherit the default search path from.
+ auto sphalNamespace = android_get_exported_namespace("sphal");
+ if (!sphalNamespace) return;
+ mDriverNamespace = android_create_namespace("gfx driver",
+ nullptr, // ld_library_path
+ mDriverPath.c_str(), // default_library_path
+ ANDROID_NAMESPACE_TYPE_SHARED |
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr, // permitted_when_isolated_path
+ sphalNamespace);
});
return mDriverNamespace;
}
diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp
index 511f68ab4c..39adc5e929 100644
--- a/libs/ui/HdrCapabilities.cpp
+++ b/libs/ui/HdrCapabilities.cpp
@@ -16,44 +16,76 @@
#include <ui/HdrCapabilities.h>
-#include <binder/Parcel.h>
-
namespace android {
-status_t HdrCapabilities::writeToParcel(Parcel* parcel) const
-{
- status_t result = parcel->writeInt32Vector(mSupportedHdrTypes);
- if (result != OK) {
- return result;
- }
- result = parcel->writeFloat(mMaxLuminance);
- if (result != OK) {
- return result;
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundefined-reinterpret-cast"
+#endif
+
+HdrCapabilities::~HdrCapabilities() = default;
+HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) = default;
+HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) = default;
+
+
+size_t HdrCapabilities::getFlattenedSize() const {
+ return sizeof(mMaxLuminance) +
+ sizeof(mMaxAverageLuminance) +
+ sizeof(mMinLuminance) +
+ sizeof(int32_t) +
+ mSupportedHdrTypes.size() * sizeof(int32_t);
+}
+
+status_t HdrCapabilities::flatten(void* buffer, size_t size) const {
+
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
}
- result = parcel->writeFloat(mMaxAverageLuminance);
- if (result != OK) {
- return result;
+
+ int32_t* const buf = static_cast<int32_t*>(buffer);
+ reinterpret_cast<float&>(buf[0]) = mMaxLuminance;
+ reinterpret_cast<float&>(buf[1]) = mMaxAverageLuminance;
+ reinterpret_cast<float&>(buf[2]) = mMinLuminance;
+ buf[3] = static_cast<int32_t>(mSupportedHdrTypes.size());
+ for (size_t i = 0, c = mSupportedHdrTypes.size(); i < c; ++i) {
+ buf[4 + i] = mSupportedHdrTypes[i];
}
- result = parcel->writeFloat(mMinLuminance);
- return result;
+ return NO_ERROR;
}
-status_t HdrCapabilities::readFromParcel(const Parcel* parcel)
-{
- status_t result = parcel->readInt32Vector(&mSupportedHdrTypes);
- if (result != OK) {
- return result;
+status_t HdrCapabilities::unflatten(void const* buffer, size_t size) {
+
+ size_t minSize = sizeof(mMaxLuminance) +
+ sizeof(mMaxAverageLuminance) +
+ sizeof(mMinLuminance) +
+ sizeof(int32_t);
+
+ if (size < minSize) {
+ return NO_MEMORY;
}
- result = parcel->readFloat(&mMaxLuminance);
- if (result != OK) {
- return result;
+
+ int32_t const * const buf = static_cast<int32_t const *>(buffer);
+ const size_t itemCount = size_t(buf[3]);
+
+ // check the buffer is large enough
+ if (size < minSize + itemCount * sizeof(int32_t)) {
+ return BAD_VALUE;
}
- result = parcel->readFloat(&mMaxAverageLuminance);
- if (result != OK) {
- return result;
+
+ mMaxLuminance = reinterpret_cast<float const&>(buf[0]);
+ mMaxAverageLuminance = reinterpret_cast<float const&>(buf[1]);
+ mMinLuminance = reinterpret_cast<float const&>(buf[2]);
+ if (itemCount) {
+ mSupportedHdrTypes.reserve(itemCount);
+ for (size_t i = 0; i < itemCount; ++i) {
+ mSupportedHdrTypes[i] = buf[4 + i];
+ }
}
- result = parcel->readFloat(&mMinLuminance);
- return result;
+ return NO_ERROR;
}
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
} // namespace android
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index cab1dde3fa..e88fdd5e84 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -22,9 +22,12 @@ namespace android {
uint32_t bytesPerPixel(PixelFormat format) {
switch (format) {
+ case PIXEL_FORMAT_RGBA_FP16:
+ return 8;
case PIXEL_FORMAT_RGBA_8888:
case PIXEL_FORMAT_RGBX_8888:
case PIXEL_FORMAT_BGRA_8888:
+ case PIXEL_FORMAT_RGBA_1010102:
return 4;
case PIXEL_FORMAT_RGB_888:
return 3;
@@ -38,9 +41,12 @@ uint32_t bytesPerPixel(PixelFormat format) {
uint32_t bitsPerPixel(PixelFormat format) {
switch (format) {
+ case PIXEL_FORMAT_RGBA_FP16:
+ return 64;
case PIXEL_FORMAT_RGBA_8888:
case PIXEL_FORMAT_RGBX_8888:
case PIXEL_FORMAT_BGRA_8888:
+ case PIXEL_FORMAT_RGBA_1010102:
return 32;
case PIXEL_FORMAT_RGB_888:
return 24;
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 8cdab8ccf8..6733505090 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -21,11 +21,7 @@ cc_test {
}
cc_test {
- name: "vec_test",
- srcs: ["vec_test.cpp"],
-}
-
-cc_test {
- name: "mat_test",
- srcs: ["mat_test.cpp"],
+ name: "colorspace_test",
+ shared_libs: ["libui"],
+ srcs: ["colorspace_test.cpp"],
}
diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp
new file mode 100644
index 0000000000..0a4873c8d3
--- /dev/null
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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_TAG "ColorSpaceTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <ui/ColorSpace.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class ColorSpaceTest : public testing::Test {
+protected:
+};
+
+TEST_F(ColorSpaceTest, XYZ) {
+ mat3 sRGBToXYZ(transpose(mat3{
+ 0.412391f, 0.357584f, 0.180481f,
+ 0.212639f, 0.715169f, 0.072192f,
+ 0.019331f, 0.119195f, 0.950532f
+ }));
+
+ mat3 XYZtoSRGB(inverse(sRGBToXYZ));
+
+ ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+ EXPECT_EQ(sRGBToXYZ, sRGB.getRGBtoXYZ());
+ EXPECT_EQ(XYZtoSRGB, sRGB.getXYZtoRGB());
+}
+
+TEST_F(ColorSpaceTest, XYZPrimaries) {
+ mat3 sRGBToXYZ(transpose(mat3{
+ 0.412391f, 0.357584f, 0.180481f,
+ 0.212639f, 0.715169f, 0.072192f,
+ 0.019331f, 0.119195f, 0.950532f
+ }));
+
+ ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+ EXPECT_NEAR(0.640f, sRGB.getPrimaries()[0].x, 1e-5f);
+ EXPECT_NEAR(0.330f, sRGB.getPrimaries()[0].y, 1e-5f);
+
+ EXPECT_NEAR(0.300f, sRGB.getPrimaries()[1].x, 1e-5f);
+ EXPECT_NEAR(0.600f, sRGB.getPrimaries()[1].y, 1e-5f);
+
+ EXPECT_NEAR(0.150f, sRGB.getPrimaries()[2].x, 1e-5f);
+ EXPECT_NEAR(0.060f, sRGB.getPrimaries()[2].y, 1e-5f);
+}
+
+TEST_F(ColorSpaceTest, XYZWhitePoint) {
+ mat3 sRGBToXYZ(transpose(mat3{
+ 0.412391f, 0.357584f, 0.180481f,
+ 0.212639f, 0.715169f, 0.072192f,
+ 0.019331f, 0.119195f, 0.950532f
+ }));
+
+ ColorSpace sRGB("sRGB", sRGBToXYZ);
+
+ EXPECT_NEAR(0.3127f, sRGB.getWhitePoint().x, 1e-5f);
+ EXPECT_NEAR(0.3290f, sRGB.getWhitePoint().y, 1e-5f);
+}
+
+TEST_F(ColorSpaceTest, XYZFromPrimaries) {
+ mat3 sRGBToXYZ(transpose(mat3{
+ 0.412391f, 0.357584f, 0.180481f,
+ 0.212639f, 0.715169f, 0.072192f,
+ 0.019331f, 0.119195f, 0.950532f
+ }));
+
+ ColorSpace sRGB1("sRGB", sRGBToXYZ);
+ ColorSpace sRGB2(
+ "sRGB",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f}
+ );
+
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j= 0; j < 3; j++) {
+ ASSERT_NEAR(sRGB1.getRGBtoXYZ()[i][j], sRGB2.getRGBtoXYZ()[i][j], 1e-5f);
+ }
+ }
+
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j= 0; j < 3; j++) {
+ ASSERT_NEAR(sRGB2.getXYZtoRGB()[i][j], sRGB2.getXYZtoRGB()[i][j], 1e-5f);
+ }
+ }
+}
+
+TEST_F(ColorSpaceTest, TransferFunctions) {
+ ColorSpace sRGB = ColorSpace::sRGB();
+
+ EXPECT_NEAR(0.0f, sRGB.getEOTF()(0.0f), 1e-6f);
+ EXPECT_NEAR(0.0f, sRGB.getOETF()(0.0f), 1e-6f);
+ EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f);
+ EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f);
+
+ for (float v = 0.0f; v <= 0.5f; v += 1e-3f) {
+ ASSERT_TRUE(v >= sRGB.getEOTF()(v));
+ ASSERT_TRUE(v <= sRGB.getOETF()(v));
+ }
+
+ float previousEOTF = std::numeric_limits<float>::lowest();
+ float previousOETF = std::numeric_limits<float>::lowest();
+ for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
+ ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v));
+ previousEOTF = sRGB.getEOTF()(v);
+ ASSERT_TRUE(previousOETF < sRGB.getOETF()(v));
+ previousOETF = sRGB.getOETF()(v);
+ }
+
+ ColorSpace sRGB2(
+ "sRGB",
+ {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
+ {0.3127f, 0.3290f}
+ // linear transfer functions
+ );
+ for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
+ ASSERT_EQ(v, sRGB2.getEOTF()(v));
+ ASSERT_EQ(v, sRGB2.getOETF()(v));
+ }
+}
+
+TEST_F(ColorSpaceTest, Clamping) {
+ // Pick a color outside of sRGB
+ float3 c(ColorSpace::BT2020().rgbToXYZ(float3{0, 1, 0}));
+
+ // The color will be clamped
+ float3 sRGB(ColorSpace::sRGB().xyzToRGB(c));
+ EXPECT_TRUE(sRGB > float3{0.0} && sRGB < float3{1.0});
+
+ // The color will not be clamped
+ float3 extendedSRGB(ColorSpace::linearExtendedSRGB().xyzToRGB(c));
+ EXPECT_TRUE(extendedSRGB.g > 1.0f);
+}
+
+TEST_F(ColorSpaceTest, Connect) {
+ // No chromatic adaptation
+ auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB())
+ .transform({1.0f, 0.5f, 0.0f});
+ EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
+
+ // Test with chromatic adaptation
+ r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB())
+ .transform({1.0f, 0.0f, 0.0f});
+ EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f})));
+}
+
+TEST_F(ColorSpaceTest, LUT) {
+ auto lut = ColorSpace::createLUT(17, ColorSpace::sRGB(), ColorSpace::AdobeRGB());
+ EXPECT_TRUE(lut != nullptr);
+
+ // {1.0f, 0.5f, 0.0f}
+ auto r = lut.get()[0 * 17 * 17 + 8 * 17 + 16];
+ EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
+
+ // {1.0f, 1.0f, 0.5f}
+ r = lut.get()[8 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped
+ EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 0.5290f}), float3{1e-4f})));
+
+ // {1.0f, 1.0f, 1.0f}
+ r = lut.get()[16 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped
+ EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 1.0f}), float3{1e-4f})));
+
+}
+
+}; // namespace android
diff --git a/libs/ui/tests/mat_test.cpp b/libs/ui/tests/mat_test.cpp
deleted file mode 100644
index a2c63ac7ca..0000000000
--- a/libs/ui/tests/mat_test.cpp
+++ /dev/null
@@ -1,139 +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_TAG "RegionTest"
-
-#include <stdlib.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
-#include <gtest/gtest.h>
-
-#include <ui/mat4.h>
-
-namespace android {
-
-class MatTest : public testing::Test {
-protected:
-};
-
-TEST_F(MatTest, Basics) {
- mat4 m0;
- EXPECT_EQ(sizeof(mat4), sizeof(float)*16);
-}
-
-TEST_F(MatTest, ComparisonOps) {
- mat4 m0;
- mat4 m1(2);
-
- EXPECT_TRUE(m0 == m0);
- EXPECT_TRUE(m0 != m1);
- EXPECT_FALSE(m0 != m0);
- EXPECT_FALSE(m0 == m1);
-}
-
-TEST_F(MatTest, Constructors) {
- mat4 m0;
- ASSERT_EQ(m0[0].x, 1);
- ASSERT_EQ(m0[0].y, 0);
- ASSERT_EQ(m0[0].z, 0);
- ASSERT_EQ(m0[0].w, 0);
- ASSERT_EQ(m0[1].x, 0);
- ASSERT_EQ(m0[1].y, 1);
- ASSERT_EQ(m0[1].z, 0);
- ASSERT_EQ(m0[1].w, 0);
- ASSERT_EQ(m0[2].x, 0);
- ASSERT_EQ(m0[2].y, 0);
- ASSERT_EQ(m0[2].z, 1);
- ASSERT_EQ(m0[2].w, 0);
- ASSERT_EQ(m0[3].x, 0);
- ASSERT_EQ(m0[3].y, 0);
- ASSERT_EQ(m0[3].z, 0);
- ASSERT_EQ(m0[3].w, 1);
-
- mat4 m1(2);
- mat4 m2(vec4(2));
- mat4 m3(m2);
-
- EXPECT_EQ(m1, m2);
- EXPECT_EQ(m2, m3);
- EXPECT_EQ(m3, m1);
-
- mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4));
-}
-
-TEST_F(MatTest, ArithmeticOps) {
- mat4 m0;
- mat4 m1(2);
- mat4 m2(vec4(2));
-
- m1 += m2;
- EXPECT_EQ(mat4(4), m1);
-
- m2 -= m1;
- EXPECT_EQ(mat4(-2), m2);
-
- m1 *= 2;
- EXPECT_EQ(mat4(8), m1);
-
- m1 /= 2;
- EXPECT_EQ(mat4(4), m1);
-
- m0 = -m0;
- EXPECT_EQ(mat4(-1), m0);
-}
-
-TEST_F(MatTest, UnaryOps) {
- const mat4 identity;
- mat4 m0;
-
- ++m0;
- EXPECT_EQ(mat4( vec4(2,1,1,1), vec4(1,2,1,1), vec4(1,1,2,1), vec4(1,1,1,2) ), m0);
- EXPECT_EQ(mat4( -vec4(2,1,1,1), -vec4(1,2,1,1), -vec4(1,1,2,1), -vec4(1,1,1,2) ), -m0);
-
- --m0;
- EXPECT_EQ(identity, m0);
-}
-
-TEST_F(MatTest, MiscOps) {
- const mat4 identity;
- mat4 m0;
- EXPECT_EQ(4, trace(m0));
-
- mat4 m1(vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16));
- mat4 m2(vec4(1,5,9,13), vec4(2,6,10,14), vec4(3,7,11,15), vec4(4,8,12,16));
- EXPECT_EQ(m1, transpose(m2));
- EXPECT_EQ(m2, transpose(m1));
- EXPECT_EQ(vec4(1,6,11,16), diag(m1));
-
- EXPECT_EQ(identity, inverse(identity));
-
- mat4 m3(vec4(4,3,0,0), vec4(3,2,0,0), vec4(0,0,1,0), vec4(0,0,0,1));
- mat4 m3i(inverse(m3));
- EXPECT_FLOAT_EQ(-2, m3i[0][0]);
- EXPECT_FLOAT_EQ( 3, m3i[0][1]);
- EXPECT_FLOAT_EQ( 3, m3i[1][0]);
- EXPECT_FLOAT_EQ(-4, m3i[1][1]);
-
- mat4 m3ii(inverse(m3i));
- EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
- EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
- EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
- EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
-
- EXPECT_EQ(m1, m1*identity);
-}
-
-}; // namespace android
diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp
new file mode 100644
index 0000000000..fb46c2b20c
--- /dev/null
+++ b/libs/ui/tools/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2017 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: "libui_tools_default",
+ clang_cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
+
+cc_binary {
+ name: "lutgen",
+ cppflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ shared_libs: ["libui"],
+ srcs: ["lutgen.cpp"],
+}
diff --git a/libs/ui/tools/lutgen.cpp b/libs/ui/tools/lutgen.cpp
new file mode 100644
index 0000000000..97b0822238
--- /dev/null
+++ b/libs/ui/tools/lutgen.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2017 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 <algorithm>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <string>
+
+#include <getopt.h>
+
+#include <ui/ColorSpace.h>
+
+using namespace android;
+using namespace std;
+
+uint32_t gSize = 32;
+ColorSpace gColorSpaceSrc = ColorSpace::DisplayP3();
+ColorSpace gColorSpaceDst = ColorSpace::extendedSRGB();
+string gNameSrc = "DisplayP3";
+string gNameDst = "extendedSRGB";
+
+static void printHelp() {
+ cout << "lutgen -d SIZE -s SOURCE -t TARGET <lut file>" << endl;
+ cout << endl;
+ cout << "Generate a 3D LUT to convert between two color spaces." << endl;
+ cout << endl;
+ cout << "If <lut file> ends in .inc, data is generated without the array declaration." << endl;
+ cout << endl;
+ cout << "Options:" << endl;
+ cout << " --help, -h" << endl;
+ cout << " print this message" << endl;
+ cout << " --dimension=, -d" << endl;
+ cout << " the dimension of the 3D LUT. Example: 17 for a 17x17x17 LUT. 32 by default" << endl;
+ cout << " --source=COLORSPACE, -s" << endl;
+ cout << " the source color space, see below for available names. DisplayP3 by default" << endl;
+ cout << " --target=COLORSPACE, -t" << endl;
+ cout << " the target color space, see below for available names. extendedSRGB by default" << endl;
+ cout << endl;
+ cout << "Colorspace names:" << endl;
+ cout << " sRGB" << endl;
+ cout << " linearSRGB" << endl;
+ cout << " extendedSRGB" << endl;
+ cout << " linearExtendedSRGB" << endl;
+ cout << " NTSC" << endl;
+ cout << " BT709" << endl;
+ cout << " BT2020" << endl;
+ cout << " AdobeRGB" << endl;
+ cout << " ProPhotoRGB" << endl;
+ cout << " DisplayP3" << endl;
+ cout << " DCIP3" << endl;
+ cout << " ACES" << endl;
+ cout << " ACEScg" << endl;
+}
+
+static const ColorSpace findColorSpace(const string& name) {
+ if (name == "linearSRGB") return ColorSpace::linearSRGB();
+ if (name == "extendedSRGB") return ColorSpace::extendedSRGB();
+ if (name == "linearExtendedSRGB") return ColorSpace::linearExtendedSRGB();
+ if (name == "NTSC") return ColorSpace::NTSC();
+ if (name == "BT709") return ColorSpace::BT709();
+ if (name == "BT2020") return ColorSpace::BT2020();
+ if (name == "AdobeRGB") return ColorSpace::AdobeRGB();
+ if (name == "ProPhotoRGB") return ColorSpace::ProPhotoRGB();
+ if (name == "DisplayP3") return ColorSpace::DisplayP3();
+ if (name == "DCIP3") return ColorSpace::DCIP3();
+ if (name == "ACES") return ColorSpace::ACES();
+ if (name == "ACEScg") return ColorSpace::ACEScg();
+ return ColorSpace::sRGB();
+}
+
+static int handleCommandLineArgments(int argc, char* argv[]) {
+ static constexpr const char* OPTSTR = "h:d:s:t:";
+ static const struct option OPTIONS[] = {
+ { "help", no_argument, 0, 'h' },
+ { "dimension", required_argument, 0, 'd' },
+ { "source", required_argument, 0, 's' },
+ { "target", required_argument, 0, 't' },
+ { 0, 0, 0, 0 } // termination of the option list
+ };
+
+ int opt;
+ int index = 0;
+
+ while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &index)) >= 0) {
+ string arg(optarg ? optarg : "");
+ switch (opt) {
+ default:
+ case 'h':
+ printHelp();
+ exit(0);
+ break;
+ case 'd':
+ gSize = max(2, min(stoi(arg), 256));
+ break;
+ case 's':
+ gNameSrc = arg;
+ gColorSpaceSrc = findColorSpace(arg);
+ break;
+ case 't':
+ gNameDst = arg;
+ gColorSpaceDst = findColorSpace(arg);
+ break;
+ }
+ }
+
+ return optind;
+}
+
+int main(int argc, char* argv[]) {
+ int optionIndex = handleCommandLineArgments(argc, argv);
+ int numArgs = argc - optionIndex;
+
+ if (numArgs < 1) {
+ printHelp();
+ return 1;
+ }
+
+ bool isInclude = false;
+
+ string filename(argv[optionIndex]);
+ size_t index = filename.find_last_of('.');
+
+ if (index != string::npos) {
+ string extension(filename.substr(index + 1));
+ isInclude = extension == "inc";
+ }
+
+ ofstream outputStream(filename, ios::trunc);
+ if (outputStream.good()) {
+ auto lut = ColorSpace::createLUT(gSize, gColorSpaceSrc, gColorSpaceDst);
+ auto data = lut.get();
+
+ outputStream << "// generated with lutgen " << filename.c_str() << endl;
+ outputStream << "// 3D LUT stored as an RGB16F texture, in GL order" << endl;
+ outputStream << "// Size is " << gSize << "x" << gSize << "x" << gSize << endl;
+
+ string src(gNameSrc);
+ string dst(gNameDst);
+
+ if (!isInclude) {
+ transform(src.begin(), src.end(), src.begin(), ::toupper);
+ transform(dst.begin(), dst.end(), dst.begin(), ::toupper);
+
+ outputStream << "const size_t LUT_" << src << "_TO_" << dst << "_SIZE = " << gSize << endl;
+ outputStream << "const uint16_t LUT_" << src << "_TO_" << dst << "[] = {";
+ } else {
+ outputStream << "// From " << src << " to " << dst << endl;
+ }
+
+ for (size_t z = 0; z < gSize; z++) {
+ for (size_t y = 0; y < gSize; y++) {
+ for (size_t x = 0; x < gSize; x++) {
+ if (x % 4 == 0) outputStream << endl << " ";
+
+ half3 rgb = half3(*data++);
+
+ const uint16_t r = rgb.r.getBits();
+ const uint16_t g = rgb.g.getBits();
+ const uint16_t b = rgb.b.getBits();
+
+ outputStream << "0x" << setfill('0') << setw(4) << hex << r << ", ";
+ outputStream << "0x" << setfill('0') << setw(4) << hex << g << ", ";
+ outputStream << "0x" << setfill('0') << setw(4) << hex << b << ", ";
+ }
+ }
+ }
+
+ if (!isInclude) {
+ outputStream << endl << "}; // end LUT" << endl;
+ }
+
+ outputStream << endl;
+ outputStream.flush();
+ outputStream.close();
+ } else {
+ cerr << "Could not write to file: " << filename << endl;
+ return 1;
+
+ }
+
+ return 0;
+}
diff --git a/libs/vr/.clang-format b/libs/vr/.clang-format
new file mode 100644
index 0000000000..04d7970bbe
--- /dev/null
+++ b/libs/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/libs/vr/Android.bp b/libs/vr/Android.bp
new file mode 100644
index 0000000000..e8176cf6b6
--- /dev/null
+++ b/libs/vr/Android.bp
@@ -0,0 +1,3 @@
+subdirs = [
+ "*",
+]
diff --git a/libs/vr/CPPLINT.cfg b/libs/vr/CPPLINT.cfg
new file mode 100644
index 0000000000..87fb641c28
--- /dev/null
+++ b/libs/vr/CPPLINT.cfg
@@ -0,0 +1,2 @@
+set noparent
+filter=-build/include_order,-legal/copyright,-build/include,-build/c++11,+build/include_alpha
diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp
new file mode 100644
index 0000000000..13af470a26
--- /dev/null
+++ b/libs/vr/libbroadcastring/Android.bp
@@ -0,0 +1,35 @@
+cc_library_static {
+ name: "libbroadcastring",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libbase",
+ ],
+ export_shared_lib_headers: [
+ "libbase",
+ ],
+}
+
+cc_test {
+ name: "broadcast_ring_tests",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "broadcast_ring_test.cc",
+ ],
+ static_libs: [
+ "libbroadcastring",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/libs/vr/libbroadcastring/broadcast_ring_test.cc b/libs/vr/libbroadcastring/broadcast_ring_test.cc
new file mode 100644
index 0000000000..dfdd4ef0db
--- /dev/null
+++ b/libs/vr/libbroadcastring/broadcast_ring_test.cc
@@ -0,0 +1,866 @@
+#include "libbroadcastring/broadcast_ring.h"
+
+#include <stdlib.h>
+#include <memory>
+#include <thread> // NOLINT
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+template <uint32_t N>
+struct alignas(8) Aligned {
+ char v[N];
+};
+
+template <uint32_t N>
+struct alignas(8) Sized {
+ Sized() { Clear(); }
+ explicit Sized(char c) { Fill(c); }
+ char v[sizeof(Aligned<N>)];
+ void Clear() { memset(v, 0, sizeof(v)); }
+ void Fill(char c) { memset(v, c, sizeof(v)); }
+ static Sized Pattern(uint8_t c) {
+ Sized sized;
+ for (size_t i = 0; i < sizeof(v); ++i) {
+ sized.v[i] = static_cast<char>(c + i);
+ }
+ return sized;
+ }
+ bool operator==(const Sized& right) const {
+ static_assert(sizeof(*this) == sizeof(v), "Size mismatch");
+ return !memcmp(v, right.v, sizeof(v));
+ }
+ template <typename SmallerSized>
+ SmallerSized Truncate() const {
+ SmallerSized val;
+ static_assert(sizeof(val.v) <= sizeof(v), "Cannot truncate to larger size");
+ memcpy(val.v, v, sizeof(val.v));
+ return val;
+ }
+};
+
+char FillChar(int val) { return static_cast<char>(val); }
+
+struct FakeMmap {
+ explicit FakeMmap(size_t size) : size(size), data(new char[size]) {}
+ size_t size;
+ std::unique_ptr<char[]> data;
+ void* mmap() { return static_cast<void*>(data.get()); }
+};
+
+template <typename Ring>
+FakeMmap CreateRing(Ring* ring, uint32_t count) {
+ FakeMmap mmap(Ring::MemorySize(count));
+ *ring = Ring::Create(mmap.mmap(), mmap.size, count);
+ return mmap;
+}
+
+template <typename RecordType, bool StaticSize = false,
+ uint32_t StaticCount = 0, uint32_t MaxReserved = 1,
+ uint32_t MinAvailable = 0>
+struct Traits {
+ using Record = RecordType;
+ static constexpr bool kUseStaticRecordSize = StaticSize;
+ static constexpr uint32_t kStaticRecordCount = StaticCount;
+ static constexpr uint32_t kMaxReservedRecords = MaxReserved;
+ static constexpr uint32_t kMinAvailableRecords = MinAvailable;
+ static constexpr uint32_t kMinRecordCount = MaxReserved + MinAvailable;
+};
+
+template <typename Record, bool StaticSize = false, uint32_t MaxReserved = 1,
+ uint32_t MinAvailable = 7>
+struct TraitsDynamic
+ : public Traits<Record, StaticSize, 0, MaxReserved, MinAvailable> {
+ using Ring = BroadcastRing<Record, TraitsDynamic>;
+ static uint32_t MinCount() { return MaxReserved + MinAvailable; }
+};
+
+template <typename Record, uint32_t StaticCount = 1, bool StaticSize = true,
+ uint32_t MaxReserved = 1, uint32_t MinAvailable = 0>
+struct TraitsStatic
+ : public Traits<Record, true, StaticCount, MaxReserved, MinAvailable> {
+ using Ring = BroadcastRing<Record, TraitsStatic>;
+ static uint32_t MinCount() { return StaticCount; }
+};
+
+using Dynamic_8_NxM = TraitsDynamic<Sized<8>>;
+using Dynamic_16_NxM = TraitsDynamic<Sized<16>>;
+using Dynamic_32_NxM = TraitsDynamic<Sized<32>>;
+using Dynamic_32_32xM = TraitsDynamic<Sized<32>, true>;
+using Dynamic_16_NxM_1plus0 = TraitsDynamic<Sized<16>, false, 1, 0>;
+using Dynamic_16_NxM_1plus1 = TraitsDynamic<Sized<16>, false, 1, 1>;
+using Dynamic_16_NxM_5plus11 = TraitsDynamic<Sized<16>, false, 5, 11>;
+using Dynamic_256_NxM_1plus0 = TraitsDynamic<Sized<256>, false, 1, 0>;
+
+using Static_8_8x1 = TraitsStatic<Sized<8>, 1>;
+using Static_8_8x16 = TraitsStatic<Sized<8>, 16>;
+using Static_16_16x8 = TraitsStatic<Sized<16>, 8>;
+using Static_16_16x16 = TraitsStatic<Sized<16>, 16>;
+using Static_16_16x32 = TraitsStatic<Sized<16>, 32>;
+using Static_32_Nx8 = TraitsStatic<Sized<32>, 8, false>;
+
+using TraitsList = ::testing::Types<Dynamic_8_NxM, //
+ Dynamic_16_NxM, //
+ Dynamic_32_NxM, //
+ Dynamic_32_32xM, //
+ Dynamic_16_NxM_1plus0, //
+ Dynamic_16_NxM_1plus1, //
+ Dynamic_16_NxM_5plus11, //
+ Dynamic_256_NxM_1plus0, //
+ Static_8_8x1, //
+ Static_8_8x16, //
+ Static_16_16x8, //
+ Static_16_16x16, //
+ Static_16_16x32, //
+ Static_32_Nx8>;
+
+} // namespace
+
+template <typename T>
+class BroadcastRingTest : public ::testing::Test {};
+
+TYPED_TEST_CASE(BroadcastRingTest, TraitsList);
+
+TYPED_TEST(BroadcastRingTest, Geometry) {
+ using Record = typename TypeParam::Record;
+ using Ring = typename TypeParam::Ring;
+ Ring ring;
+ auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+ EXPECT_EQ(Ring::Traits::MinCount(), ring.record_count());
+ EXPECT_EQ(sizeof(Record), ring.record_size());
+}
+
+TYPED_TEST(BroadcastRingTest, PutGet) {
+ using Record = typename TypeParam::Record;
+ using Ring = typename TypeParam::Ring;
+ Ring ring;
+ auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+ const uint32_t oldest_sequence_at_start = ring.GetOldestSequence();
+ const uint32_t next_sequence_at_start = ring.GetNextSequence();
+ {
+ uint32_t sequence = oldest_sequence_at_start;
+ Record record;
+ EXPECT_FALSE(ring.Get(&sequence, &record));
+ EXPECT_EQ(oldest_sequence_at_start, sequence);
+ EXPECT_EQ(Record(), record);
+ }
+ const Record original_record(0x1a);
+ ring.Put(original_record);
+ {
+ uint32_t sequence = next_sequence_at_start;
+ Record record;
+ EXPECT_TRUE(ring.Get(&sequence, &record));
+ EXPECT_EQ(next_sequence_at_start, sequence);
+ EXPECT_EQ(original_record, record);
+ }
+ {
+ uint32_t sequence = next_sequence_at_start + 1;
+ Record record;
+ EXPECT_FALSE(ring.Get(&sequence, &record));
+ EXPECT_EQ(next_sequence_at_start + 1, sequence);
+ EXPECT_EQ(Record(), record);
+ }
+}
+
+TYPED_TEST(BroadcastRingTest, FillOnce) {
+ using Record = typename TypeParam::Record;
+ using Ring = typename TypeParam::Ring;
+ Ring ring;
+ auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+ const uint32_t next_sequence_at_start = ring.GetNextSequence();
+ for (uint32_t i = 0; i < ring.record_count(); ++i)
+ ring.Put(Record(FillChar(i)));
+ for (uint32_t i = 0; i < ring.record_count(); ++i) {
+ const uint32_t expected_sequence = next_sequence_at_start + i;
+ const Record expected_record(FillChar(i));
+ {
+ uint32_t sequence = ring.GetOldestSequence() + i;
+ Record record;
+ EXPECT_TRUE(ring.Get(&sequence, &record));
+ EXPECT_EQ(expected_sequence, sequence);
+ EXPECT_EQ(expected_record, record);
+ }
+ }
+ {
+ uint32_t sequence = ring.GetOldestSequence() + ring.record_count();
+ Record record;
+ EXPECT_FALSE(ring.Get(&sequence, &record));
+ }
+}
+
+TYPED_TEST(BroadcastRingTest, FillTwice) {
+ using Record = typename TypeParam::Record;
+ using Ring = typename TypeParam::Ring;
+ Ring ring;
+ auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+ const uint32_t next_sequence_at_start = ring.GetNextSequence();
+ for (uint32_t i = 0; i < 2 * ring.record_count(); ++i) {
+ const Record newest_record(FillChar(i));
+ ring.Put(newest_record);
+
+ const uint32_t newest_sequence = next_sequence_at_start + i;
+ const uint32_t records_available = std::min(i + 1, ring.record_count());
+ const uint32_t oldest_sequence = newest_sequence - records_available + 1;
+ EXPECT_EQ(newest_sequence, ring.GetNewestSequence());
+ EXPECT_EQ(oldest_sequence, ring.GetOldestSequence());
+ EXPECT_EQ(newest_sequence + 1, ring.GetNextSequence());
+
+ for (uint32_t j = 0; j < records_available; ++j) {
+ const uint32_t sequence_jth_newest = newest_sequence - j;
+ const Record record_jth_newest(FillChar(i - j));
+
+ {
+ uint32_t sequence = sequence_jth_newest;
+ Record record;
+ EXPECT_TRUE(ring.Get(&sequence, &record));
+ EXPECT_EQ(sequence_jth_newest, sequence);
+ EXPECT_EQ(record_jth_newest, record);
+ }
+
+ {
+ uint32_t sequence = sequence_jth_newest;
+ Record record;
+ EXPECT_TRUE(ring.GetNewest(&sequence, &record));
+ EXPECT_EQ(newest_sequence, sequence);
+ EXPECT_EQ(newest_record, record);
+ }
+ }
+
+ const Record oldest_record(
+ FillChar(i + (oldest_sequence - newest_sequence)));
+ const uint32_t sequence_0th_overwritten = oldest_sequence - 1;
+ const uint32_t sequence_0th_future = newest_sequence + 1;
+ const uint32_t sequence_1st_future = newest_sequence + 2;
+
+ {
+ uint32_t sequence = sequence_0th_overwritten;
+ Record record;
+ EXPECT_TRUE(ring.Get(&sequence, &record));
+ EXPECT_EQ(oldest_sequence, sequence);
+ EXPECT_EQ(oldest_record, record);
+ }
+
+ {
+ uint32_t sequence = sequence_0th_overwritten;
+ Record record;
+ EXPECT_TRUE(ring.GetNewest(&sequence, &record));
+ EXPECT_EQ(newest_sequence, sequence);
+ EXPECT_EQ(newest_record, record);
+ }
+
+ {
+ uint32_t sequence = sequence_0th_future;
+ Record record;
+ EXPECT_FALSE(ring.Get(&sequence, &record));
+ EXPECT_EQ(sequence_0th_future, sequence);
+ EXPECT_EQ(Record(), record);
+ }
+
+ {
+ uint32_t sequence = sequence_0th_future;
+ Record record;
+ EXPECT_FALSE(ring.GetNewest(&sequence, &record));
+ EXPECT_EQ(sequence_0th_future, sequence);
+ EXPECT_EQ(Record(), record);
+ }
+
+ {
+ uint32_t sequence = sequence_1st_future;
+ Record record;
+ EXPECT_TRUE(ring.Get(&sequence, &record));
+ EXPECT_EQ(oldest_sequence, sequence);
+ EXPECT_EQ(oldest_record, record);
+ }
+
+ {
+ uint32_t sequence = sequence_1st_future;
+ Record record;
+ EXPECT_TRUE(ring.GetNewest(&sequence, &record));
+ EXPECT_EQ(newest_sequence, sequence);
+ EXPECT_EQ(newest_record, record);
+ }
+ }
+}
+
+TYPED_TEST(BroadcastRingTest, Import) {
+ using Record = typename TypeParam::Record;
+ using Ring = typename TypeParam::Ring;
+ Ring ring;
+ auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
+
+ const uint32_t sequence_0 = ring.GetNextSequence();
+ const uint32_t sequence_1 = ring.GetNextSequence() + 1;
+ const Record record_0 = Record::Pattern(0x00);
+ const Record record_1 = Record::Pattern(0x80);
+ ring.Put(record_0);
+ ring.Put(record_1);
+
+ {
+ Ring imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) = Ring::Import(mmap.mmap(), mmap.size);
+ EXPECT_TRUE(import_ok);
+ EXPECT_EQ(ring.record_size(), imported_ring.record_size());
+ EXPECT_EQ(ring.record_count(), imported_ring.record_count());
+
+ if (ring.record_count() != 1) {
+ uint32_t sequence = sequence_0;
+ Record imported_record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+ EXPECT_EQ(sequence_0, sequence);
+ EXPECT_EQ(record_0, imported_record);
+ }
+
+ {
+ uint32_t sequence = sequence_1;
+ Record imported_record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+ EXPECT_EQ(sequence_1, sequence);
+ EXPECT_EQ(record_1, imported_record);
+ }
+ }
+}
+
+TEST(BroadcastRingTest, ShouldFailImportIfStaticSizeMismatch) {
+ using OriginalRing = typename Static_16_16x16::Ring;
+ using RecordSizeMismatchRing = typename Static_8_8x16::Ring;
+ using RecordCountMismatchRing = typename Static_16_16x8::Ring;
+
+ OriginalRing original_ring;
+ auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+ {
+ using ImportedRing = RecordSizeMismatchRing;
+ ImportedRing imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), mmap.size);
+ EXPECT_FALSE(import_ok);
+ auto mmap_imported =
+ CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
+ EXPECT_NE(original_ring.record_size(), imported_ring.record_size());
+ EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+ }
+
+ {
+ using ImportedRing = RecordCountMismatchRing;
+ ImportedRing imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), mmap.size);
+ EXPECT_FALSE(import_ok);
+ auto mmap_imported =
+ CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
+ EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+ EXPECT_NE(original_ring.record_count(), imported_ring.record_count());
+ }
+}
+
+TEST(BroadcastRingTest, ShouldFailImportIfDynamicSizeGrows) {
+ using OriginalRing = typename Dynamic_8_NxM::Ring;
+ using RecordSizeGrowsRing = typename Dynamic_16_NxM::Ring;
+
+ OriginalRing original_ring;
+ auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+ {
+ using ImportedRing = RecordSizeGrowsRing;
+ ImportedRing imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), mmap.size);
+ EXPECT_FALSE(import_ok);
+ auto mmap_imported =
+ CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
+ EXPECT_LT(original_ring.record_size(), imported_ring.record_size());
+ EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+ }
+}
+
+TEST(BroadcastRingTest, ShouldFailImportIfCountTooSmall) {
+ using OriginalRing = typename Dynamic_16_NxM_1plus0::Ring;
+ using MinCountRing = typename Dynamic_16_NxM_1plus1::Ring;
+
+ OriginalRing original_ring;
+ auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+ {
+ using ImportedRing = MinCountRing;
+ ImportedRing imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), mmap.size);
+ EXPECT_FALSE(import_ok);
+ auto mmap_imported =
+ CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
+ EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+ EXPECT_LT(original_ring.record_count(), imported_ring.record_count());
+ }
+}
+
+TEST(BroadcastRingTest, ShouldFailImportIfMmapTooSmall) {
+ using OriginalRing = typename Dynamic_16_NxM::Ring;
+
+ OriginalRing original_ring;
+ auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+ {
+ using ImportedRing = OriginalRing;
+ ImportedRing imported_ring;
+ bool import_ok;
+ const size_t kMinSize =
+ ImportedRing::MemorySize(original_ring.record_count());
+ std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), 0);
+ EXPECT_FALSE(import_ok);
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), kMinSize - 1);
+ EXPECT_FALSE(import_ok);
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), kMinSize);
+ EXPECT_TRUE(import_ok);
+ EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+ EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+ }
+}
+
+TEST(BroadcastRingTest, ShouldImportIfDynamicSizeShrinks) {
+ using OriginalRing = typename Dynamic_16_NxM::Ring;
+ using RecordSizeShrinksRing = typename Dynamic_8_NxM::Ring;
+
+ OriginalRing original_ring;
+ auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
+
+ using OriginalRecord = typename OriginalRing::Record;
+ const uint32_t original_sequence_0 = original_ring.GetNextSequence();
+ const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
+ const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
+ const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
+ original_ring.Put(original_record_0);
+ original_ring.Put(original_record_1);
+
+ {
+ using ImportedRing = RecordSizeShrinksRing;
+ using ImportedRecord = typename ImportedRing::Record;
+ ImportedRing imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), mmap.size);
+ EXPECT_TRUE(import_ok);
+ EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+ EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+ EXPECT_GT(sizeof(OriginalRecord), sizeof(ImportedRecord));
+
+ {
+ uint32_t sequence = original_sequence_0;
+ ImportedRecord shrunk_record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record));
+ EXPECT_EQ(original_sequence_0, sequence);
+ EXPECT_EQ(original_record_0.Truncate<ImportedRecord>(), shrunk_record);
+ }
+
+ {
+ uint32_t sequence = original_sequence_1;
+ ImportedRecord shrunk_record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record));
+ EXPECT_EQ(original_sequence_1, sequence);
+ EXPECT_EQ(original_record_1.Truncate<ImportedRecord>(), shrunk_record);
+ }
+ }
+}
+
+TEST(BroadcastRingTest, ShouldImportIfCompatibleDynamicToStatic) {
+ using OriginalRing = typename Dynamic_16_NxM::Ring;
+ using ImportedRing = typename Static_16_16x16::Ring;
+ using OriginalRecord = typename OriginalRing::Record;
+ using ImportedRecord = typename ImportedRing::Record;
+ using StaticRing = ImportedRing;
+
+ OriginalRing original_ring;
+ auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount());
+
+ const uint32_t original_sequence_0 = original_ring.GetNextSequence();
+ const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
+ const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
+ const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
+ original_ring.Put(original_record_0);
+ original_ring.Put(original_record_1);
+
+ {
+ ImportedRing imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), mmap.size);
+ EXPECT_TRUE(import_ok);
+ EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+ EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+
+ {
+ uint32_t sequence = original_sequence_0;
+ ImportedRecord imported_record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+ EXPECT_EQ(original_sequence_0, sequence);
+ EXPECT_EQ(original_record_0, imported_record);
+ }
+
+ {
+ uint32_t sequence = original_sequence_1;
+ ImportedRecord imported_record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+ EXPECT_EQ(original_sequence_1, sequence);
+ EXPECT_EQ(original_record_1, imported_record);
+ }
+ }
+}
+
+TEST(BroadcastRingTest, ShouldImportIfCompatibleStaticToDynamic) {
+ using OriginalRing = typename Static_16_16x16::Ring;
+ using ImportedRing = typename Dynamic_16_NxM::Ring;
+ using OriginalRecord = typename OriginalRing::Record;
+ using ImportedRecord = typename ImportedRing::Record;
+ using StaticRing = OriginalRing;
+
+ OriginalRing original_ring;
+ auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount());
+
+ const uint32_t original_sequence_0 = original_ring.GetNextSequence();
+ const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
+ const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
+ const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
+ original_ring.Put(original_record_0);
+ original_ring.Put(original_record_1);
+
+ {
+ ImportedRing imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) =
+ ImportedRing::Import(mmap.mmap(), mmap.size);
+ EXPECT_TRUE(import_ok);
+ EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
+ EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
+
+ {
+ uint32_t sequence = original_sequence_0;
+ ImportedRecord imported_record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+ EXPECT_EQ(original_sequence_0, sequence);
+ EXPECT_EQ(original_record_0, imported_record);
+ }
+
+ {
+ uint32_t sequence = original_sequence_1;
+ ImportedRecord imported_record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
+ EXPECT_EQ(original_sequence_1, sequence);
+ EXPECT_EQ(original_record_1, imported_record);
+ }
+ }
+}
+
+TEST(BroadcastRingTest, ShouldImportIfReadonlyMmap) {
+ using Ring = Dynamic_32_NxM::Ring;
+ using Record = Ring::Record;
+
+ uint32_t record_count = Ring::Traits::MinCount();
+ size_t ring_size = Ring::MemorySize(record_count);
+
+ size_t page_size = sysconf(_SC_PAGESIZE);
+ size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1);
+ ASSERT_GE(mmap_size, ring_size);
+
+ void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, mmap_base);
+
+ Ring ring = Ring::Create(mmap_base, mmap_size, record_count);
+ for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i)));
+
+ ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ));
+
+ {
+ Ring imported_ring;
+ bool import_ok;
+ std::tie(imported_ring, import_ok) = Ring::Import(mmap_base, mmap_size);
+ EXPECT_TRUE(import_ok);
+ EXPECT_EQ(ring.record_size(), imported_ring.record_size());
+ EXPECT_EQ(ring.record_count(), imported_ring.record_count());
+
+ uint32_t oldest_sequence = imported_ring.GetOldestSequence();
+ for (uint32_t i = 0; i < record_count; ++i) {
+ uint32_t sequence = oldest_sequence + i;
+ Record record;
+ EXPECT_TRUE(imported_ring.Get(&sequence, &record));
+ EXPECT_EQ(Record(FillChar(i)), record);
+ }
+ }
+
+ ASSERT_EQ(0, munmap(mmap_base, mmap_size));
+}
+
+TEST(BroadcastRingTest, ShouldDieIfPutReadonlyMmap) {
+ using Ring = Dynamic_32_NxM::Ring;
+ using Record = Ring::Record;
+
+ uint32_t record_count = Ring::Traits::MinCount();
+ size_t ring_size = Ring::MemorySize(record_count);
+
+ size_t page_size = sysconf(_SC_PAGESIZE);
+ size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1);
+ ASSERT_GE(mmap_size, ring_size);
+
+ void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ ASSERT_NE(MAP_FAILED, mmap_base);
+
+ Ring ring = Ring::Create(mmap_base, mmap_size, record_count);
+ for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i)));
+
+ ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ));
+
+ EXPECT_DEATH_IF_SUPPORTED({ ring.Put(Record(7)); }, "");
+
+ ASSERT_EQ(0, munmap(mmap_base, mmap_size));
+}
+
+TEST(BroadcastRingTest, ShouldDieIfCreationMmapTooSmall) {
+ using Ring = Dynamic_32_NxM::Ring;
+ using Record = Ring::Record;
+
+ uint32_t record_count = Ring::Traits::MinCount();
+ size_t ring_size = Ring::MemorySize(record_count);
+ FakeMmap mmap(ring_size);
+
+ EXPECT_DEATH_IF_SUPPORTED({
+ Ring ring = Ring::Create(mmap.mmap(), ring_size - 1, record_count);
+ }, "");
+
+ Ring ring = Ring::Create(mmap.mmap(), ring_size, record_count);
+
+ ring.Put(Record(3));
+
+ {
+ uint32_t sequence = ring.GetNewestSequence();
+ Record record;
+ EXPECT_TRUE(ring.Get(&sequence, &record));
+ EXPECT_EQ(Record(3), record);
+ }
+}
+
+TEST(BroadcastRingTest, ShouldDieIfCreationMmapMisaligned) {
+ using Ring = Static_8_8x1::Ring;
+ using Record = Ring::Record;
+
+ constexpr int kAlign = Ring::mmap_alignment();
+ constexpr int kMisalign = kAlign / 2;
+ size_t ring_size = Ring::MemorySize();
+ std::unique_ptr<char[]> buf(new char[ring_size + kMisalign]);
+
+ EXPECT_DEATH_IF_SUPPORTED(
+ { Ring ring = Ring::Create(buf.get() + kMisalign, ring_size); }, "");
+
+ Ring ring = Ring::Create(buf.get(), ring_size);
+
+ ring.Put(Record(3));
+
+ {
+ uint32_t sequence = ring.GetNewestSequence();
+ Record record;
+ EXPECT_TRUE(ring.Get(&sequence, &record));
+ EXPECT_EQ(Record(3), record);
+ }
+}
+
+template <typename Ring>
+std::unique_ptr<std::thread> CopyTask(std::atomic<bool>* quit, void* in_base,
+ size_t in_size, void* out_base,
+ size_t out_size) {
+ return std::unique_ptr<std::thread>(
+ new std::thread([quit, in_base, in_size, out_base, out_size]() {
+ using Record = typename Ring::Record;
+
+ bool import_ok;
+ Ring in_ring;
+ Ring out_ring;
+ std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size);
+ ASSERT_TRUE(import_ok);
+ std::tie(out_ring, import_ok) = Ring::Import(out_base, out_size);
+ ASSERT_TRUE(import_ok);
+
+ uint32_t sequence = in_ring.GetOldestSequence();
+ while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) {
+ Record record;
+ if (in_ring.Get(&sequence, &record)) {
+ out_ring.Put(record);
+ sequence++;
+ }
+ }
+ }));
+}
+
+TEST(BroadcastRingTest, ThreadedCopySingle) {
+ using Ring = Dynamic_32_NxM::Ring;
+ using Record = Ring::Record;
+ Ring in_ring;
+ auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
+
+ Ring out_ring;
+ auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
+
+ std::atomic<bool> quit(false);
+ std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
+ &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
+
+ const Record out_record(0x1c);
+ out_ring.Put(out_record);
+
+ uint32_t in_sequence = in_ring.GetOldestSequence();
+ Record in_record;
+ while (!in_ring.Get(&in_sequence, &in_record)) {
+ // Do nothing.
+ }
+
+ EXPECT_EQ(out_record, in_record);
+ std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
+ copy_task->join();
+}
+
+TEST(BroadcastRingTest, ThreadedCopyLossless) {
+ using Ring = Dynamic_32_NxM::Ring;
+ using Record = Ring::Record;
+ Ring in_ring;
+ auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
+
+ Ring out_ring;
+ auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
+
+ std::atomic<bool> quit(false);
+ std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
+ &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
+
+ constexpr uint32_t kRecordsToProcess = 10000;
+ uint32_t out_records = 0;
+ uint32_t in_records = 0;
+ uint32_t in_sequence = in_ring.GetNextSequence();
+ while (out_records < kRecordsToProcess || in_records < kRecordsToProcess) {
+ if (out_records < kRecordsToProcess &&
+ out_records - in_records < out_ring.record_count()) {
+ const Record out_record(FillChar(out_records));
+ out_ring.Put(out_record);
+ out_records++;
+ }
+
+ Record in_record;
+ while (in_ring.Get(&in_sequence, &in_record)) {
+ EXPECT_EQ(Record(FillChar(in_records)), in_record);
+ in_records++;
+ in_sequence++;
+ }
+ }
+
+ EXPECT_EQ(kRecordsToProcess, out_records);
+ EXPECT_EQ(kRecordsToProcess, in_records);
+
+ std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
+ copy_task->join();
+}
+
+TEST(BroadcastRingTest, ThreadedCopyLossy) {
+ using Ring = Dynamic_32_NxM::Ring;
+ using Record = Ring::Record;
+ Ring in_ring;
+ auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
+
+ Ring out_ring;
+ auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
+
+ std::atomic<bool> quit(false);
+ std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
+ &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
+
+ constexpr uint32_t kRecordsToProcess = 100000;
+ uint32_t out_records = 0;
+ uint32_t in_records = 0;
+ uint32_t in_sequence = in_ring.GetNextSequence();
+ while (out_records < kRecordsToProcess) {
+ const Record out_record(FillChar(out_records));
+ out_ring.Put(out_record);
+ out_records++;
+
+ Record in_record;
+ if (in_ring.GetNewest(&in_sequence, &in_record)) {
+ EXPECT_EQ(Record(in_record.v[0]), in_record);
+ in_records++;
+ in_sequence++;
+ }
+ }
+
+ EXPECT_EQ(kRecordsToProcess, out_records);
+ EXPECT_GE(kRecordsToProcess, in_records);
+
+ std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
+ copy_task->join();
+}
+
+template <typename Ring>
+std::unique_ptr<std::thread> CheckFillTask(std::atomic<bool>* quit,
+ void* in_base, size_t in_size) {
+ return std::unique_ptr<std::thread>(
+ new std::thread([quit, in_base, in_size]() {
+ using Record = typename Ring::Record;
+
+ bool import_ok;
+ Ring in_ring;
+ std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size);
+ ASSERT_TRUE(import_ok);
+
+ uint32_t sequence = in_ring.GetOldestSequence();
+ while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) {
+ Record record;
+ if (in_ring.Get(&sequence, &record)) {
+ ASSERT_EQ(Record(record.v[0]), record);
+ sequence++;
+ }
+ }
+ }));
+}
+
+template <typename Ring>
+void ThreadedOverwriteTorture() {
+ using Record = typename Ring::Record;
+
+ // Maximize overwrites by having few records.
+ const int kMinRecordCount = 1;
+ const int kMaxRecordCount = 4;
+
+ for (int count = kMinRecordCount; count <= kMaxRecordCount; count *= 2) {
+ Ring out_ring;
+ auto out_mmap = CreateRing(&out_ring, count);
+
+ std::atomic<bool> quit(false);
+ std::unique_ptr<std::thread> check_task =
+ CheckFillTask<Ring>(&quit, out_mmap.mmap(), out_mmap.size);
+
+ constexpr int kIterations = 10000;
+ for (int i = 0; i < kIterations; ++i) {
+ const Record record(FillChar(i));
+ out_ring.Put(record);
+ }
+
+ std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
+ check_task->join();
+ }
+}
+
+TEST(BroadcastRingTest, ThreadedOverwriteTortureSmall) {
+ ThreadedOverwriteTorture<Dynamic_16_NxM_1plus0::Ring>();
+}
+
+TEST(BroadcastRingTest, ThreadedOverwriteTortureLarge) {
+ ThreadedOverwriteTorture<Dynamic_256_NxM_1plus0::Ring>();
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h b/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h
new file mode 100644
index 0000000000..69cb64826e
--- /dev/null
+++ b/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h
@@ -0,0 +1,668 @@
+#ifndef ANDROID_DVR_BROADCAST_RING_H_
+#define ANDROID_DVR_BROADCAST_RING_H_
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <atomic>
+#include <limits>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "android-base/logging.h"
+
+#if ATOMIC_LONG_LOCK_FREE != 2 || ATOMIC_INT_LOCK_FREE != 2
+#error "This file requires lock free atomic uint32_t and long"
+#endif
+
+namespace android {
+namespace dvr {
+
+struct DefaultRingTraits {
+ // Set this to false to allow compatibly expanding the record size.
+ static constexpr bool kUseStaticRecordSize = false;
+
+ // Set this to a nonzero value to fix the number of records in the ring.
+ static constexpr uint32_t kStaticRecordCount = 0;
+
+ // Set this to the max number of records that can be written simultaneously.
+ static constexpr uint32_t kMaxReservedRecords = 1;
+
+ // Set this to the min number of records that must be readable.
+ static constexpr uint32_t kMinAvailableRecords = 1;
+};
+
+// Nonblocking ring suitable for concurrent single-writer, multi-reader access.
+//
+// Readers never block the writer and thus this is a nondeterministically lossy
+// transport in the absence of external synchronization. Don't use this as a
+// transport when deterministic behavior is required.
+//
+// Readers may have a read-only mapping; each reader's state is a single local
+// sequence number.
+//
+// The implementation takes care to avoid data races on record access.
+// Inconsistent data can only be returned if at least 2^32 records are written
+// during the read-side critical section.
+//
+// In addition, both readers and the writer are careful to avoid accesses
+// outside the bounds of the mmap area passed in during initialization even if
+// there is a misbehaving or malicious task with write access to the mmap area.
+//
+// When dynamic record size is enabled, readers use the record size in the ring
+// header when indexing the ring, so that it is possible to extend the record
+// type without breaking the read-side ABI.
+//
+// Avoid calling Put() in a tight loop; there should be significantly more time
+// between successive puts than it takes to read one record from memory to
+// ensure Get() completes quickly. This requirement should not be difficult to
+// achieve for most practical uses; 4kB puts at 10,000Hz is well below the
+// scaling limit on current mobile chips.
+//
+// Example Writer Usage:
+//
+// using Record = MyRecordType;
+// using Ring = BroadcastRing<Record>;
+//
+// uint32_t record_count = kMyDesiredCount;
+// uint32_t ring_size = Ring::MemorySize(record_count);
+//
+// size_t page_size = sysconf(_SC_PAGESIZE);
+// uint32_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1);
+//
+// // Allocate & map via your preferred mechanism, e.g.
+// int fd = open("/dev/shm/ring_test", O_CREAT|O_RDWR|O_CLOEXEC, 0600);
+// CHECK(fd >= 0);
+// CHECK(!ftruncate(fd, ring_size));
+// void *mmap_base = mmap(nullptr, mmap_size, PROT_READ|PROT_WRITE,
+// MAP_SHARED, fd, 0);
+// CHECK(mmap_base != MAP_FAILED);
+// close(fd);
+//
+// Ring ring = Ring::Create(mmap_base, mmap_size, record_count);
+//
+// while (!done)
+// ring.Put(BuildNextRecordBlocking());
+//
+// CHECK(!munmap(mmap_base, mmap_size));
+//
+// Example Reader Usage:
+//
+// using Record = MyRecordType;
+// using Ring = BroadcastRing<Record>;
+//
+// // Map via your preferred mechanism, e.g.
+// int fd = open("/dev/shm/ring_test", O_RDONLY|O_CLOEXEC);
+// CHECK(fd >= 0);
+// struct stat st;
+// CHECK(!fstat(fd, &st));
+// size_t mmap_size = st.st_size;
+// void *mmap_base = mmap(nullptr, mmap_size, PROT_READ,
+// MAP_SHARED, fd, 0);
+// CHECK(mmap_base != MAP_FAILED);
+// close(fd);
+//
+// Ring ring;
+// bool import_ok;
+// std::tie(ring, import_ok) = Ring::Import(mmap_base, mmap_size);
+// CHECK(import_ok);
+//
+// uint32_t sequence;
+//
+// // Choose starting point (using "0" is unpredictable but not dangerous)
+// sequence = ring.GetOldestSequence(); // The oldest available
+// sequence = ring.GetNewestSequence(); // The newest available
+// sequence = ring.GetNextSequence(); // The next one produced
+//
+// while (!done) {
+// Record record;
+//
+// if (you_want_to_process_all_available_records) {
+// while (ring.Get(&sequence, &record)) {
+// ProcessRecord(sequence, record);
+// sequence++;
+// }
+// } else if (you_want_to_skip_to_the_newest_record) {
+// if (ring.GetNewest(&sequence, &record)) {
+// ProcessRecord(sequence, record);
+// sequence++;
+// }
+// }
+//
+// DoSomethingExpensiveOrBlocking();
+// }
+//
+// CHECK(!munmap(mmap_base, mmap_size));
+//
+template <typename RecordType, typename BaseTraits = DefaultRingTraits>
+class BroadcastRing {
+ public:
+ using Record = RecordType;
+ struct Traits : public BaseTraits {
+ // Must have enough space for writers, plus enough space for readers.
+ static constexpr int kMinRecordCount =
+ BaseTraits::kMaxReservedRecords + BaseTraits::kMinAvailableRecords;
+
+ // Count of zero means dynamic, non-zero means static.
+ static constexpr bool kUseStaticRecordCount =
+ (BaseTraits::kStaticRecordCount != 0);
+
+ // If both record size and count are static then the overall size is too.
+ static constexpr bool kIsStaticSize =
+ BaseTraits::kUseStaticRecordSize && kUseStaticRecordCount;
+ };
+
+ static constexpr bool IsPowerOfTwo(uint32_t size) {
+ return (size & (size - 1)) == 0;
+ }
+
+ // Sanity check the options provided in Traits.
+ static_assert(Traits::kMinRecordCount >= 1, "Min record count too small");
+ static_assert(!Traits::kUseStaticRecordCount ||
+ Traits::kStaticRecordCount >= Traits::kMinRecordCount,
+ "Static record count is too small");
+ static_assert(!Traits::kStaticRecordCount ||
+ IsPowerOfTwo(Traits::kStaticRecordCount),
+ "Static record count is not a power of two");
+ static_assert(std::is_standard_layout<Record>::value,
+ "Record type must be standard layout");
+
+ BroadcastRing() {}
+
+ // Creates a new ring at |mmap| with |record_count| records.
+ //
+ // There must be at least |MemorySize(record_count)| bytes of space already
+ // allocated at |mmap|. The ring does not take ownership.
+ //
+ // Use this function for dynamically sized rings.
+ static BroadcastRing Create(void* mmap, size_t mmap_size,
+ uint32_t record_count) {
+ BroadcastRing ring(mmap);
+ CHECK(ring.ValidateGeometry(mmap_size, sizeof(Record), record_count));
+ ring.InitializeHeader(sizeof(Record), record_count);
+ return ring;
+ }
+
+ // Creates a new ring at |mmap|.
+ //
+ // There must be at least |MemorySize()| bytes of space already allocated at
+ // |mmap|. The ring does not take ownership.
+ //
+ // Use this function for statically sized rings.
+ static BroadcastRing Create(void* mmap, size_t mmap_size) {
+ static_assert(Traits::kUseStaticRecordCount,
+ "Wrong Create() function called for dynamic record count");
+ return Create(mmap, mmap_size, Traits::kStaticRecordCount);
+ }
+
+ // Imports an existing ring at |mmap|.
+ //
+ // Import may fail if the ring parameters in the mmap header are not sensible.
+ // In this case the returned boolean is false; make sure to check this value.
+ static std::tuple<BroadcastRing, bool> Import(void* mmap, size_t mmap_size) {
+ BroadcastRing ring(mmap);
+ uint32_t record_size = 0;
+ uint32_t record_count = 0;
+ if (mmap_size >= sizeof(Header)) {
+ record_size = std::atomic_load_explicit(&ring.header_mmap()->record_size,
+ std::memory_order_relaxed);
+ record_count = std::atomic_load_explicit(
+ &ring.header_mmap()->record_count, std::memory_order_relaxed);
+ }
+ bool ok = ring.ValidateGeometry(mmap_size, record_size, record_count);
+ return std::make_tuple(ring, ok);
+ }
+
+ ~BroadcastRing() {}
+
+ // Calculates the space necessary for a ring of size |record_count|.
+ //
+ // Use this function for dynamically sized rings.
+ static constexpr size_t MemorySize(uint32_t record_count) {
+ return sizeof(Header) + sizeof(Record) * record_count;
+ }
+
+ // Calculates the space necessary for a statically sized ring.
+ //
+ // Use this function for statically sized rings.
+ static constexpr size_t MemorySize() {
+ static_assert(
+ Traits::kUseStaticRecordCount,
+ "Wrong MemorySize() function called for dynamic record count");
+ return MemorySize(Traits::kStaticRecordCount);
+ }
+
+ // Writes a record to the ring.
+ //
+ // The oldest record is overwritten unless the ring is not already full.
+ void Put(const Record& record) {
+ const int kRecordCount = 1;
+ Reserve(kRecordCount);
+ Geometry geometry = GetGeometry();
+ PutRecordInternal(&record, record_mmap_writer(geometry.tail_index));
+ Publish(kRecordCount);
+ }
+
+ // Gets sequence number of the oldest currently available record.
+ uint32_t GetOldestSequence() const {
+ return std::atomic_load_explicit(&header_mmap()->head,
+ std::memory_order_relaxed);
+ }
+
+ // Gets sequence number of the first future record.
+ //
+ // If the returned value is passed to Get() and there is no concurrent Put(),
+ // Get() will return false.
+ uint32_t GetNextSequence() const {
+ return std::atomic_load_explicit(&header_mmap()->tail,
+ std::memory_order_relaxed);
+ }
+
+ // Gets sequence number of the newest currently available record.
+ uint32_t GetNewestSequence() const { return GetNextSequence() - 1; }
+
+ // Copies the oldest available record with sequence at least |*sequence| to
+ // |record|.
+ //
+ // Returns false if there is no recent enough record available.
+ //
+ // Updates |*sequence| with the sequence number of the record returned. To get
+ // the following record, increment this number by one.
+ //
+ // This function synchronizes with two other operations:
+ //
+ // (1) Load-Acquire of |tail|
+ //
+ // Together with the store-release in Publish(), this load-acquire
+ // ensures each store to a record in PutRecordInternal() happens-before
+ // any corresponding load in GetRecordInternal().
+ //
+ // i.e. the stores for the records with sequence numbers < |tail| have
+ // completed from our perspective
+ //
+ // (2) Acquire Fence between record access & final load of |head|
+ //
+ // Together with the release fence in Reserve(), this ensures that if
+ // GetRecordInternal() loads a value stored in some execution of
+ // PutRecordInternal(), then the store of |head| in the Reserve() that
+ // preceeded it happens-before our final load of |head|.
+ //
+ // i.e. if we read a record with sequence number >= |final_head| then
+ // no later store to that record has completed from our perspective
+ bool Get(uint32_t* sequence /*inout*/, Record* record /*out*/) const {
+ for (;;) {
+ uint32_t tail = std::atomic_load_explicit(&header_mmap()->tail,
+ std::memory_order_acquire);
+ uint32_t head = std::atomic_load_explicit(&header_mmap()->head,
+ std::memory_order_relaxed);
+
+ if (tail - head > record_count())
+ continue; // Concurrent modification; re-try.
+
+ if (*sequence - head > tail - head)
+ *sequence = head; // Out of window, skip forward to first available.
+
+ if (*sequence == tail) return false; // No new records available.
+
+ Geometry geometry =
+ CalculateGeometry(record_count(), record_size(), *sequence, tail);
+
+ // Compute address explicitly in case record_size > sizeof(Record).
+ RecordStorage* record_storage = record_mmap_reader(geometry.head_index);
+
+ GetRecordInternal(record_storage, record);
+
+ // NB: It is not sufficient to change this to a load-acquire of |head|.
+ std::atomic_thread_fence(std::memory_order_acquire);
+
+ uint32_t final_head = std::atomic_load_explicit(
+ &header_mmap()->head, std::memory_order_relaxed);
+
+ if (final_head - head > *sequence - head)
+ continue; // Concurrent modification; re-try.
+
+ // Note: Combining the above 4 comparisons gives:
+ // 0 <= final_head - head <= sequence - head < tail - head <= record_count
+ //
+ // We can also write this as:
+ // head <=* final_head <=* sequence <* tail <=* head + record_count
+ //
+ // where <* orders by difference from head: x <* y if x - head < y - head.
+ // This agrees with the order of sequence updates during "put" operations.
+ return true;
+ }
+ }
+
+ // Copies the newest available record with sequence at least |*sequence| to
+ // |record|.
+ //
+ // Returns false if there is no recent enough record available.
+ //
+ // Updates |*sequence| with the sequence number of the record returned. To get
+ // the following record, increment this number by one.
+ bool GetNewest(uint32_t* sequence, Record* record) const {
+ uint32_t newest_sequence = GetNewestSequence();
+ if (*sequence == newest_sequence + 1) return false;
+ *sequence = newest_sequence;
+ return Get(sequence, record);
+ }
+
+ uint32_t record_count() const { return record_count_internal(); }
+ uint32_t record_size() const { return record_size_internal(); }
+ static constexpr uint32_t mmap_alignment() { return alignof(Mmap); }
+
+ private:
+ struct Header {
+ // Record size for reading out of the ring. Writers always write the full
+ // length; readers may need to read a prefix of each record.
+ std::atomic<uint32_t> record_size;
+
+ // Number of records in the ring.
+ std::atomic<uint32_t> record_count;
+
+ // Readable region is [head % record_count, tail % record_count).
+ //
+ // The region in [tail % record_count, head % record_count) was either never
+ // populated or is being updated.
+ //
+ // These are sequences numbers, not indexes - indexes should be computed
+ // with a modulus.
+ //
+ // To ensure consistency:
+ //
+ // (1) Writes advance |head| past any updated records before writing to
+ // them, and advance |tail| after they are written.
+ // (2) Readers check |tail| before reading data and |head| after,
+ // making sure to discard any data that was written to concurrently.
+ std::atomic<uint32_t> head;
+ std::atomic<uint32_t> tail;
+ };
+
+ // Store using the standard word size.
+ using StorageType = long; // NOLINT
+
+ // Always require 8 byte alignment so that the same record sizes are legal on
+ // 32 and 64 bit builds.
+ static constexpr size_t kRecordAlignment = 8;
+ static_assert(kRecordAlignment % sizeof(StorageType) == 0,
+ "Bad record alignment");
+
+ struct RecordStorage {
+ // This is accessed with relaxed atomics to prevent data races on the
+ // contained data, which would be undefined behavior.
+ std::atomic<StorageType> data[sizeof(Record) / sizeof(StorageType)];
+ };
+
+ static_assert(sizeof(StorageType) *
+ std::extent<decltype(RecordStorage::data)>() ==
+ sizeof(Record),
+ "Record length must be a multiple of sizeof(StorageType)");
+
+ struct Geometry {
+ // Static geometry.
+ uint32_t record_count;
+ uint32_t record_size;
+
+ // Copy of atomic sequence counts.
+ uint32_t head;
+ uint32_t tail;
+
+ // First index of readable region.
+ uint32_t head_index;
+
+ // First index of writable region.
+ uint32_t tail_index;
+
+ // Number of records in readable region.
+ uint32_t count;
+
+ // Number of records in writable region.
+ uint32_t space;
+ };
+
+ // Mmap area layout.
+ //
+ // Readers should not index directly into |records| as this is not valid when
+ // dynamic record sizes are used; use record_mmap_reader() instead.
+ struct Mmap {
+ Header header;
+ RecordStorage records[];
+ };
+
+ static_assert(std::is_standard_layout<Mmap>::value,
+ "Mmap must be standard layout");
+ static_assert(sizeof(std::atomic<uint32_t>) == sizeof(uint32_t),
+ "Lockless atomics contain extra state");
+ static_assert(sizeof(std::atomic<StorageType>) == sizeof(StorageType),
+ "Lockless atomics contain extra state");
+
+ explicit BroadcastRing(void* mmap) {
+ CHECK_EQ(0U, reinterpret_cast<uintptr_t>(mmap) % alignof(Mmap));
+ data_.mmap = reinterpret_cast<Mmap*>(mmap);
+ }
+
+ // Initializes the mmap area header for a new ring.
+ void InitializeHeader(uint32_t record_size, uint32_t record_count) {
+ constexpr uint32_t kInitialSequence = -256; // Force an early wrap.
+ std::atomic_store_explicit(&header_mmap()->record_size, record_size,
+ std::memory_order_relaxed);
+ std::atomic_store_explicit(&header_mmap()->record_count, record_count,
+ std::memory_order_relaxed);
+ std::atomic_store_explicit(&header_mmap()->head, kInitialSequence,
+ std::memory_order_relaxed);
+ std::atomic_store_explicit(&header_mmap()->tail, kInitialSequence,
+ std::memory_order_relaxed);
+ }
+
+ // Validates ring geometry.
+ //
+ // Ring geometry is validated carefully on import and then cached. This allows
+ // us to avoid out-of-range accesses even if the parameters in the header are
+ // later changed.
+ bool ValidateGeometry(size_t mmap_size, uint32_t header_record_size,
+ uint32_t header_record_count) {
+ set_record_size(header_record_size);
+ set_record_count(header_record_count);
+
+ if (record_size() != header_record_size) return false;
+ if (record_count() != header_record_count) return false;
+ if (record_count() < Traits::kMinRecordCount) return false;
+ if (record_size() < sizeof(Record)) return false;
+ if (record_size() % kRecordAlignment != 0) return false;
+ if (!IsPowerOfTwo(record_count())) return false;
+
+ size_t memory_size = record_count() * record_size();
+ if (memory_size / record_size() != record_count()) return false;
+ if (memory_size + sizeof(Header) < memory_size) return false;
+ if (memory_size + sizeof(Header) > mmap_size) return false;
+
+ return true;
+ }
+
+ // Copies a record into the ring.
+ //
+ // This is done with relaxed atomics because otherwise it is racy according to
+ // the C++ memory model. This is very low overhead once optimized.
+ static inline void PutRecordInternal(const Record* in, RecordStorage* out) {
+ StorageType data[sizeof(Record) / sizeof(StorageType)];
+ memcpy(data, in, sizeof(*in));
+ for (size_t i = 0; i < std::extent<decltype(data)>(); ++i) {
+ std::atomic_store_explicit(&out->data[i], data[i],
+ std::memory_order_relaxed);
+ }
+ }
+
+ // Copies a record out of the ring.
+ //
+ // This is done with relaxed atomics because otherwise it is racy according to
+ // the C++ memory model. This is very low overhead once optimized.
+ static inline void GetRecordInternal(RecordStorage* in, Record* out) {
+ StorageType data[sizeof(Record) / sizeof(StorageType)];
+ for (size_t i = 0; i < std::extent<decltype(data)>(); ++i) {
+ data[i] =
+ std::atomic_load_explicit(&in->data[i], std::memory_order_relaxed);
+ }
+ memcpy(out, &data, sizeof(*out));
+ }
+
+ // Converts a record's sequence number into a storage index.
+ static uint32_t SequenceToIndex(uint32_t sequence, uint32_t record_count) {
+ return sequence & (record_count - 1);
+ }
+
+ // Computes readable & writable ranges from ring parameters.
+ static Geometry CalculateGeometry(uint32_t record_count, uint32_t record_size,
+ uint32_t head, uint32_t tail) {
+ Geometry geometry;
+ geometry.record_count = record_count;
+ geometry.record_size = record_size;
+ DCHECK_EQ(0U, geometry.record_size % kRecordAlignment);
+ geometry.head = head;
+ geometry.tail = tail;
+ geometry.head_index = SequenceToIndex(head, record_count);
+ geometry.tail_index = SequenceToIndex(tail, record_count);
+ geometry.count = geometry.tail - geometry.head;
+ DCHECK_LE(geometry.count, record_count);
+ geometry.space = geometry.record_count - geometry.count;
+ return geometry;
+ }
+
+ // Gets the current ring readable & writable regions.
+ //
+ // This this is always safe from the writing thread since it is the only
+ // thread allowed to update the header.
+ Geometry GetGeometry() const {
+ return CalculateGeometry(
+ record_count(), record_size(),
+ std::atomic_load_explicit(&header_mmap()->head,
+ std::memory_order_relaxed),
+ std::atomic_load_explicit(&header_mmap()->tail,
+ std::memory_order_relaxed));
+ }
+
+ // Makes space for at least |reserve_count| records.
+ //
+ // There is nothing to prevent overwriting records that have concurrent
+ // readers. We do however ensure that this situation can be detected: the
+ // fence ensures the |head| update will be the first update seen by readers,
+ // and readers check this value after reading and discard data that may have
+ // been concurrently modified.
+ void Reserve(uint32_t reserve_count) {
+ Geometry geometry = GetGeometry();
+ DCHECK_LE(reserve_count, Traits::kMaxReservedRecords);
+ uint32_t needed =
+ (geometry.space >= reserve_count ? 0 : reserve_count - geometry.space);
+
+ std::atomic_store_explicit(&header_mmap()->head, geometry.head + needed,
+ std::memory_order_relaxed);
+
+ // NB: It is not sufficient to change this to a store-release of |head|.
+ std::atomic_thread_fence(std::memory_order_release);
+ }
+
+ // Makes |publish_count| records visible to readers.
+ //
+ // Space must have been reserved by a previous call to Reserve().
+ void Publish(uint32_t publish_count) {
+ Geometry geometry = GetGeometry();
+ DCHECK_LE(publish_count, geometry.space);
+ std::atomic_store_explicit(&header_mmap()->tail,
+ geometry.tail + publish_count,
+ std::memory_order_release);
+ }
+
+ // Helpers to compute addresses in mmap area.
+ Mmap* mmap() const { return data_.mmap; }
+ Header* header_mmap() const { return &data_.mmap->header; }
+ RecordStorage* record_mmap_writer(uint32_t index) const {
+ DCHECK_EQ(sizeof(Record), record_size());
+ return &data_.mmap->records[index];
+ }
+ RecordStorage* record_mmap_reader(uint32_t index) const {
+ if (Traits::kUseStaticRecordSize) {
+ return &data_.mmap->records[index];
+ } else {
+ // Calculate the location of a record in the ring without assuming that
+ // sizeof(Record) == record_size.
+ return reinterpret_cast<RecordStorage*>(
+ reinterpret_cast<char*>(data_.mmap->records) + index * record_size());
+ }
+ }
+
+ // The following horrifying template gunk enables us to store just the mmap
+ // base pointer for compile-time statically sized rings. Dynamically sized
+ // rings also store the validated copy of the record size & count.
+ //
+ // This boils down to: use a compile time constant if available, and otherwise
+ // load the value that was validated on import from a member variable.
+ template <typename T = Traits>
+ typename std::enable_if<T::kUseStaticRecordSize, uint32_t>::type
+ record_size_internal() const {
+ return sizeof(Record);
+ }
+
+ template <typename T = Traits>
+ typename std::enable_if<!T::kUseStaticRecordSize, uint32_t>::type
+ record_size_internal() const {
+ return data_.record_size;
+ }
+
+ template <typename T = Traits>
+ typename std::enable_if<T::kUseStaticRecordSize, void>::type set_record_size(
+ uint32_t /*record_size*/) {}
+
+ template <typename T = Traits>
+ typename std::enable_if<!T::kUseStaticRecordSize, void>::type set_record_size(
+ uint32_t record_size) {
+ data_.record_size = record_size;
+ }
+
+ template <typename T = Traits>
+ typename std::enable_if<T::kUseStaticRecordCount, uint32_t>::type
+ record_count_internal() const {
+ return Traits::kStaticRecordCount;
+ }
+
+ template <typename T = Traits>
+ typename std::enable_if<!T::kUseStaticRecordCount, uint32_t>::type
+ record_count_internal() const {
+ return data_.record_count;
+ }
+
+ template <typename T = Traits>
+ typename std::enable_if<T::kUseStaticRecordCount, void>::type
+ set_record_count(uint32_t /*record_count*/) const {}
+
+ template <typename T = Traits>
+ typename std::enable_if<!T::kUseStaticRecordCount, void>::type
+ set_record_count(uint32_t record_count) {
+ data_.record_count = record_count;
+ }
+
+ // Data we need to store for statically sized rings.
+ struct DataStaticSize {
+ Mmap* mmap = nullptr;
+ };
+
+ // Data we need to store for dynamically sized rings.
+ struct DataDynamicSize {
+ Mmap* mmap = nullptr;
+
+ // These are cached to make sure misbehaving writers cannot cause
+ // out-of-bounds memory accesses by updating the values in the mmap header.
+ uint32_t record_size = 0;
+ uint32_t record_count = 0;
+ };
+
+ using DataStaticOrDynamic =
+ typename std::conditional<Traits::kIsStaticSize, DataStaticSize,
+ DataDynamicSize>::type;
+
+ DataStaticOrDynamic data_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BROADCAST_RING_H_
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
new file mode 100644
index 0000000000..452bad0bce
--- /dev/null
+++ b/libs/vr/libbufferhub/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2016 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.
+
+sourceFiles = [
+ "buffer_hub_client.cpp",
+ "buffer_hub_rpc.cpp",
+ "ion_buffer.cpp",
+]
+
+localIncludeFiles = [
+ "include",
+]
+
+staticLibraries = [
+ "libdvrcommon",
+ "libpdx_default_transport",
+]
+
+sharedLibraries = [
+ "libbase",
+ "libcutils",
+ "libhardware",
+ "liblog",
+ "libui",
+ "libutils",
+]
+
+cc_library {
+ srcs: sourceFiles,
+ cflags: [
+ "-DLOG_TAG=\"libbufferhub\"",
+ "-DTRACE=0"
+ ],
+ export_include_dirs: localIncludeFiles,
+ static_libs: staticLibraries,
+ shared_libs: sharedLibraries,
+ name: "libbufferhub",
+}
+
+cc_test {
+ tags: ["optional"],
+ srcs: ["bufferhub_tests.cpp"],
+ static_libs: ["libbufferhub"] + staticLibraries,
+ shared_libs: sharedLibraries,
+ name: "bufferhub_tests",
+}
+
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
new file mode 100644
index 0000000000..b9a53b0ced
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -0,0 +1,412 @@
+#include <private/dvr/buffer_hub_client.h>
+
+#include <log/log.h>
+#include <poll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+
+#include "include/private/dvr/bufferhub_rpc.h"
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::WrapBuffer;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
+ : Client{pdx::default_transport::ClientChannel::Create(
+ std::move(channel_handle))},
+ id_(-1) {}
+BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
+ : Client{pdx::default_transport::ClientChannelFactory::Create(
+ endpoint_path)},
+ id_(-1) {}
+
+BufferHubBuffer::~BufferHubBuffer() {}
+
+Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
+ Status<LocalChannelHandle> status =
+ InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+ ALOGE_IF(!status,
+ "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+ status.GetErrorMessage().c_str());
+ return status;
+}
+
+int BufferHubBuffer::ImportBuffer() {
+ ATRACE_NAME("BufferHubBuffer::ImportBuffer");
+
+ Status<NativeBufferHandle<LocalHandle>> status =
+ InvokeRemoteMethod<BufferHubRPC::GetBuffer>();
+ if (!status) {
+ ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ } else if (status.get().id() < 0) {
+ ALOGE("BufferHubBuffer::ImportBuffer: Received an invalid id!");
+ return -EIO;
+ }
+
+ auto buffer_handle = status.take();
+
+ // Stash the buffer id to replace the value in id_.
+ const int new_id = buffer_handle.id();
+
+ // Import the buffer.
+ IonBuffer ion_buffer;
+ ALOGD_IF(
+ TRACE, "BufferHubBuffer::ImportBuffer: id=%d FdCount=%zu IntCount=%zu",
+ buffer_handle.id(), buffer_handle.FdCount(), buffer_handle.IntCount());
+
+ const int ret = buffer_handle.Import(&ion_buffer);
+ if (ret < 0)
+ return ret;
+
+ // If the import succeeds, replace the previous buffer and id.
+ buffer_ = std::move(ion_buffer);
+ id_ = new_id;
+ return 0;
+}
+
+int BufferHubBuffer::Poll(int timeout_ms) {
+ ATRACE_NAME("BufferHubBuffer::Poll");
+ pollfd p = {event_fd(), POLLIN, 0};
+ return poll(&p, 1, timeout_ms);
+}
+
+int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
+ void** address) {
+ return buffer_.Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBuffer::Unlock() { return buffer_.Unlock(); }
+
+int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
+ int width = static_cast<int>(size);
+ int height = 1;
+ int ret = Lock(usage(), 0, 0, width, height, addr);
+ if (ret == 0)
+ Unlock();
+ return ret;
+}
+
+int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
+ return GetBlobReadWritePointer(size, addr);
+}
+
+void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count,
+ size_t max_fds_count) const {
+ size_t numFds = static_cast<size_t>(native_handle()->numFds);
+ *fds_count = std::min(max_fds_count, numFds);
+ std::copy(native_handle()->data, native_handle()->data + *fds_count, fds);
+}
+
+BufferConsumer::BufferConsumer(LocalChannelHandle channel)
+ : BASE(std::move(channel)) {
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+ LocalChannelHandle channel) {
+ ATRACE_NAME("BufferConsumer::Import");
+ ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value());
+ return BufferConsumer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+ Status<LocalChannelHandle> status) {
+ return Import(status ? status.take()
+ : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence) {
+ return Acquire(ready_fence, nullptr, 0);
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
+ size_t meta_size_bytes) {
+ ATRACE_NAME("BufferConsumer::Acquire");
+ LocalFence fence;
+ auto return_value =
+ std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes));
+ auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>(
+ &return_value, meta_size_bytes);
+ if (status && ready_fence)
+ *ready_fence = fence.take();
+ return status ? 0 : -status.error();
+}
+
+int BufferConsumer::Release(const LocalHandle& release_fence) {
+ ATRACE_NAME("BufferConsumer::Release");
+ return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
+ BorrowedFence(release_fence.Borrow())));
+}
+
+int BufferConsumer::ReleaseAsync() {
+ ATRACE_NAME("BufferConsumer::ReleaseAsync");
+ return ReturnStatusOrError(
+ SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
+}
+
+int BufferConsumer::Discard() { return Release(LocalHandle()); }
+
+int BufferConsumer::SetIgnore(bool ignore) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore));
+}
+
+BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+ uint32_t usage, size_t metadata_size)
+ : BufferProducer(width, height, format, usage, usage, metadata_size) {}
+
+BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+ uint64_t producer_usage, uint64_t consumer_usage,
+ size_t metadata_size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u "
+ "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64
+ " metadata_size=%zu",
+ event_fd(), width, height, format, producer_usage, consumer_usage,
+ metadata_size);
+
+ // (b/37881101) Deprecate producer/consumer usage
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+ width, height, format, (producer_usage | consumer_usage), metadata_size);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+ int group_id, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t usage,
+ size_t meta_size_bytes)
+ : BufferProducer(name, user_id, group_id, width, height, format, usage,
+ usage, meta_size_bytes) {}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+ int group_id, uint32_t width, uint32_t height,
+ uint32_t format, uint64_t producer_usage,
+ uint64_t consumer_usage, size_t meta_size_bytes)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d "
+ "group_id=%d width=%u height=%u format=%u producer_usage=%" PRIx64
+ " consumer_usage=%" PRIx64 " meta_size_bytes=%zu",
+ event_fd(), name.c_str(), user_id, group_id, width, height, format,
+ producer_usage, consumer_usage, meta_size_bytes);
+
+ // (b/37881101) Deprecate producer/consumer usage
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+ name, user_id, group_id, width, height, format,
+ (producer_usage | consumer_usage), meta_size_bytes);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create/get persistent "
+ "buffer \"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(uint32_t usage, size_t size)
+ : BufferProducer(usage, usage, size) {}
+
+BufferProducer::BufferProducer(uint64_t producer_usage, uint64_t consumer_usage,
+ size_t size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: producer_usage=%" PRIx64
+ " consumer_usage=%" PRIx64 " size=%zu",
+ producer_usage, consumer_usage, size);
+ const int width = static_cast<int>(size);
+ const int height = 1;
+ const int format = HAL_PIXEL_FORMAT_BLOB;
+ const size_t meta_size_bytes = 0;
+
+ // (b/37881101) Deprecate producer/consumer usage
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+ width, height, format, (producer_usage | consumer_usage),
+ meta_size_bytes);
+ if (!status) {
+ ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+ int group_id, uint32_t usage, size_t size)
+ : BufferProducer(name, user_id, group_id, usage, usage, size) {}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+ int group_id, uint64_t producer_usage,
+ uint64_t consumer_usage, size_t size)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE,
+ "BufferProducer::BufferProducer: name=%s user_id=%d group=%d "
+ "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 " size=%zu",
+ name.c_str(), user_id, group_id, producer_usage, consumer_usage,
+ size);
+ const int width = static_cast<int>(size);
+ const int height = 1;
+ const int format = HAL_PIXEL_FORMAT_BLOB;
+ const size_t meta_size_bytes = 0;
+
+ // (b/37881101) Deprecate producer/consumer usage
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+ name, user_id, group_id, width, height, format,
+ (producer_usage | consumer_usage), meta_size_bytes);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to create persistent "
+ "buffer \"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(const std::string& name)
+ : BASE(BufferHubRPC::kClientPath) {
+ ATRACE_NAME("BufferProducer::BufferProducer");
+ ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str());
+
+ auto status = InvokeRemoteMethod<BufferHubRPC::GetPersistentBuffer>(name);
+ if (!status) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to get producer buffer by name "
+ "\"%s\": %s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer "
+ "\"%s\": %s",
+ name.c_str(), strerror(-ret));
+ Close(ret);
+ }
+}
+
+BufferProducer::BufferProducer(LocalChannelHandle channel)
+ : BASE(std::move(channel)) {
+ const int ret = ImportBuffer();
+ if (ret < 0) {
+ ALOGE(
+ "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+ strerror(-ret));
+ Close(ret);
+ }
+}
+
+int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
+ size_t meta_size_bytes) {
+ ATRACE_NAME("BufferProducer::Post");
+ return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+ BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
+}
+
+int BufferProducer::Gain(LocalHandle* release_fence) {
+ ATRACE_NAME("BufferProducer::Gain");
+ auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+ if (!status)
+ return -status.error();
+ if (release_fence)
+ *release_fence = status.take().take();
+ return 0;
+}
+
+int BufferProducer::GainAsync() {
+ ATRACE_NAME("BufferProducer::GainAsync");
+ return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+ LocalChannelHandle channel) {
+ ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
+ return BufferProducer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+ Status<LocalChannelHandle> status) {
+ return Import(status ? status.take()
+ : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferProducer::MakePersistent(const std::string& name, int user_id,
+ int group_id) {
+ ATRACE_NAME("BufferProducer::MakePersistent");
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ProducerMakePersistent>(name, user_id,
+ group_id));
+}
+
+int BufferProducer::RemovePersistence() {
+ ATRACE_NAME("BufferProducer::RemovePersistence");
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<BufferHubRPC::ProducerRemovePersistence>());
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_rpc.cpp b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
new file mode 100644
index 0000000000..9a67faa8aa
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/bufferhub_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char BufferHubRPC::kClientPath[];
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
new file mode 100644
index 0000000000..fa61c4a215
--- /dev/null
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -0,0 +1,226 @@
+#include <gtest/gtest.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <mutex>
+#include <thread>
+
+#define RETRY_EINTR(fnc_call) \
+ ([&]() -> decltype(fnc_call) { \
+ decltype(fnc_call) result; \
+ do { \
+ result = (fnc_call); \
+ } while (result == -1 && errno == EINTR); \
+ return result; \
+ })()
+
+using android::dvr::BufferProducer;
+using android::dvr::BufferConsumer;
+using android::pdx::LocalHandle;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const uint64_t kContext = 42;
+
+using LibBufferHubTest = ::testing::Test;
+
+TEST_F(LibBufferHubTest, TestBasicUsage) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ // Check that consumers can spawn other consumers.
+ std::unique_ptr<BufferConsumer> c2 =
+ BufferConsumer::Import(c->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+
+ EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+ // Both consumers should be triggered.
+ EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+ EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+ EXPECT_LT(0, RETRY_EINTR(c2->Poll(10)));
+
+ uint64_t context;
+ LocalHandle fence;
+ EXPECT_LE(0, c->Acquire(&fence, &context));
+ EXPECT_EQ(kContext, context);
+ EXPECT_GE(0, RETRY_EINTR(c->Poll(0)));
+
+ EXPECT_LE(0, c2->Acquire(&fence, &context));
+ EXPECT_EQ(kContext, context);
+ EXPECT_GE(0, RETRY_EINTR(c2->Poll(0)));
+
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+ EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+ EXPECT_EQ(0, c2->Discard());
+
+ EXPECT_LE(0, RETRY_EINTR(p->Poll(0)));
+ EXPECT_EQ(0, p->Gain(&fence));
+ EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ Metadata m = {1, 3};
+ EXPECT_EQ(0, p->Post(LocalHandle(), m));
+ EXPECT_LE(0, RETRY_EINTR(c->Poll(10)));
+
+ LocalHandle fence;
+ Metadata m2 = {};
+ EXPECT_EQ(0, c->Acquire(&fence, &m2));
+ EXPECT_EQ(m.field1, m2.field1);
+ EXPECT_EQ(m.field2, m2.field2);
+
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+ EXPECT_GE(0, RETRY_EINTR(c->Poll(10)));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
+ struct Metadata {
+ int64_t field1;
+ int64_t field2;
+ };
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ Metadata m = {1, 3};
+ EXPECT_EQ(0, p->Post(LocalHandle(), m));
+
+ LocalHandle fence;
+ int64_t sequence;
+ EXPECT_NE(0, c->Acquire(&fence, &sequence));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+
+ LocalHandle fence;
+ EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestWithNoMeta) {
+ std::unique_ptr<BufferProducer> p =
+ BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ LocalHandle fence;
+
+ EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+ EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
+ std::unique_ptr<BufferProducer> p =
+ BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ int64_t sequence = 3;
+ EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ // Record the original buffer id for later comparison.
+ const int buffer_id = p->id();
+
+ auto c = BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_NE(nullptr, c);
+
+ EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+
+ // Close the connection to the producer. This should not affect the consumer.
+ p = nullptr;
+
+ LocalHandle fence;
+ EXPECT_EQ(0, c->Acquire(&fence));
+ EXPECT_EQ(0, c->Release(LocalHandle()));
+
+ // Attempt to reconnect to the persistent buffer.
+ p = BufferProducer::Create("TestPersistentBuffer");
+ ASSERT_NE(nullptr, p);
+ EXPECT_EQ(buffer_id, p->id());
+ EXPECT_EQ(0, p->Gain(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ // Close the connection to the producer.
+ p = nullptr;
+
+ // Mismatch the params.
+ p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2,
+ kHeight, kFormat, kUsage);
+ ASSERT_EQ(nullptr, p);
+}
+
+TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) {
+ auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+ kHeight, kFormat, kUsage);
+ ASSERT_NE(nullptr, p);
+
+ LocalHandle fence;
+ auto c = BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_NE(nullptr, c);
+ EXPECT_NE(-EPIPE, c->Acquire(&fence));
+
+ // Test that removing persistence and closing the producer orphans the
+ // consumer.
+ EXPECT_EQ(0, p->RemovePersistence());
+ p = nullptr;
+
+ EXPECT_EQ(-EPIPE, c->Release(LocalHandle()));
+}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000000..be20e72a84
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -0,0 +1,313 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+#include <vector>
+
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubBuffer : public pdx::Client {
+ public:
+ using LocalHandle = pdx::LocalHandle;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ template <typename T>
+ using Status = pdx::Status<T>;
+
+ // Create a new consumer channel that is attached to the producer. Returns
+ // a file descriptor for the new channel or a negative error code.
+ Status<LocalChannelHandle> CreateConsumer();
+
+ // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
+ int Poll(int timeout_ms);
+
+ // Locks the area specified by (x, y, width, height) for a specific usage. If
+ // the usage is software then |addr| will be updated to point to the address
+ // of the buffer in virtual memory. The caller should only access/modify the
+ // pixels in the specified area. anything else is undefined behavior.
+ int Lock(int usage, int x, int y, int width, int height, void** addr);
+
+ // Must be called after Lock() when the caller has finished changing the
+ // buffer.
+ int Unlock();
+
+ // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+ // Locking and Unlocking is handled internally. There's no need to Unlock
+ // after calling this method.
+ int GetBlobReadWritePointer(size_t size, void** addr);
+
+ // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+ // Locking and Unlocking is handled internally. There's no need to Unlock
+ // after calling this method.
+ int GetBlobReadOnlyPointer(size_t size, void** addr);
+
+ // Returns a dup'd file descriptor for accessing the blob shared memory. The
+ // caller takes ownership of the file descriptor and must close it or pass on
+ // ownership. Some GPU API extensions can take file descriptors to bind shared
+ // memory gralloc buffers to GPU buffer objects.
+ LocalHandle GetBlobFd() const {
+ // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+ // vendors and this is the wrong fd, late-latching and EDS will very clearly
+ // stop working and we will need to correct this. The alternative is to use
+ // a GL context in the pose service to allocate this buffer or to use the
+ // ION API directly instead of gralloc.
+ return LocalHandle(dup(native_handle()->data[0]));
+ }
+
+ // Get up to |max_fds_count| file descriptors for accessing the blob shared
+ // memory. |fds_count| will contain the actual number of file descriptors.
+ void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const;
+
+ using Client::event_fd;
+
+ Status<int> GetEventMask(int events) {
+ if (auto* client_channel = GetChannel()) {
+ return client_channel->GetEventMask(events);
+ } else {
+ return pdx::ErrorStatus(EINVAL);
+ }
+ }
+
+ native_handle_t* native_handle() const {
+ return const_cast<native_handle_t*>(buffer_.handle());
+ }
+
+ IonBuffer* buffer() { return &buffer_; }
+ const IonBuffer* buffer() const { return &buffer_; }
+
+ int id() const { return id_; }
+
+ // The following methods return settings of the first buffer. Currently,
+ // it is only possible to create multi-buffer BufferHubBuffers with the same
+ // settings.
+ uint32_t width() const { return buffer_.width(); }
+ uint32_t height() const { return buffer_.height(); }
+ uint32_t stride() const { return buffer_.stride(); }
+ uint32_t format() const { return buffer_.format(); }
+ uint32_t usage() const { return buffer_.usage(); }
+ uint32_t layer_count() const { return buffer_.layer_count(); }
+
+ // TODO(b/37881101) Clean up producer/consumer usage.
+ uint64_t producer_usage() const { return buffer_.usage(); }
+ uint64_t consumer_usage() const { return buffer_.usage(); }
+
+ protected:
+ explicit BufferHubBuffer(LocalChannelHandle channel);
+ explicit BufferHubBuffer(const std::string& endpoint_path);
+ virtual ~BufferHubBuffer();
+
+ // Initialization helper.
+ int ImportBuffer();
+
+ private:
+ BufferHubBuffer(const BufferHubBuffer&) = delete;
+ void operator=(const BufferHubBuffer&) = delete;
+
+ // Global id for the buffer that is consistent across processes. It is meant
+ // for logging and debugging purposes only and should not be used for lookup
+ // or any other functional purpose as a security precaution.
+ int id_;
+
+ IonBuffer buffer_;
+};
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of BufferProducer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (BufferConsumers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
+ public:
+ // Imports a bufferhub producer channel, assuming ownership of its handle.
+ static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
+ static std::unique_ptr<BufferProducer> Import(
+ Status<LocalChannelHandle> status);
+
+ // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+ // |meta| are passed unaltered to the consumers. The producer must not modify
+ // the buffer until it is re-gained.
+ // This returns zero or a negative unix error code.
+ int Post(const LocalHandle& ready_fence, const void* meta,
+ size_t meta_size_bytes);
+
+ template <typename Meta,
+ typename = typename std::enable_if<std::is_void<Meta>::value>::type>
+ int Post(const LocalHandle& ready_fence) {
+ return Post(ready_fence, nullptr, 0);
+ }
+ template <typename Meta, typename = typename std::enable_if<
+ !std::is_void<Meta>::value>::type>
+ int Post(const LocalHandle& ready_fence, const Meta& meta) {
+ return Post(ready_fence, &meta, sizeof(meta));
+ }
+
+ // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+ // must be waited on before using the buffer. If it is not valid then the
+ // buffer is free for immediate use. This call will only succeed if the buffer
+ // is in the released state.
+ // This returns zero or a negative unix error code.
+ int Gain(LocalHandle* release_fence);
+
+ // Asynchronously marks a released buffer as gained. This method is similar to
+ // the synchronous version above, except that it does not wait for BufferHub
+ // to acknowledge success or failure, nor does it transfer a release fence to
+ // the client. This version may be used in situations where a release fence is
+ // not needed. Because of the asynchronous nature of the underlying message,
+ // no error is returned if this method is called when the buffer is in an
+ // incorrect state. Returns zero if sending the message succeeded, or a
+ // negative errno code otherwise.
+ int GainAsync();
+
+ // Attaches the producer to |name| so that it becomes a persistent buffer that
+ // may be retrieved by name at a later time. This may be used in cases where a
+ // shared memory buffer should persist across the life of the producer process
+ // (i.e. the buffer may be held by clients across a service restart). The
+ // buffer may be associated with a user and/or group id to restrict access to
+ // the buffer. If user_id or group_id is -1 then checks for the respective id
+ // are disabled. If user_id or group_id is 0 then the respective id of the
+ // calling process is used instead.
+ int MakePersistent(const std::string& name, int user_id, int group_id);
+
+ // Removes the persistence of the producer.
+ int RemovePersistence();
+
+ private:
+ friend BASE;
+
+ // Constructors are automatically exposed through BufferProducer::Create(...)
+ // static template methods inherited from ClientBase, which take the same
+ // arguments as the constructors.
+
+ // Constructs a buffer with the given geometry and parameters.
+ BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+ uint32_t usage, size_t metadata_size = 0);
+ BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+ uint64_t producer_usage, uint64_t consumer_usage,
+ size_t metadata_size);
+
+ // Constructs a persistent buffer with the given geometry and parameters and
+ // binds it to |name| in one shot. If a persistent buffer with the same name
+ // and settings already exists and matches the given geometry and parameters,
+ // that buffer is connected to this client instead of creating a new buffer.
+ // If the name matches but the geometry or settings do not match then
+ // construction fails and BufferProducer::Create() returns nullptr.
+ //
+ // Access to the persistent buffer may be restricted by |user_id| and/or
+ // |group_id|; these settings are established only when the buffer is first
+ // created and cannot be changed. A user or group id of -1 disables checks for
+ // that respective id. A user or group id of 0 is substituted with the
+ // effective user or group id of the calling process.
+ BufferProducer(const std::string& name, int user_id, int group_id,
+ uint32_t width, uint32_t height, uint32_t format,
+ uint32_t usage, size_t metadata_size = 0);
+ BufferProducer(const std::string& name, int user_id, int group_id,
+ uint32_t width, uint32_t height, uint32_t format,
+ uint64_t producer_usage, uint64_t consumer_usage,
+ size_t meta_size_bytes);
+
+ // Constructs a blob (flat) buffer with the given usage flags.
+ BufferProducer(uint32_t usage, size_t size);
+ BufferProducer(uint64_t producer_usage, uint64_t consumer_usage, size_t size);
+
+ // Constructs a persistent blob (flat) buffer and binds it to |name|.
+ BufferProducer(const std::string& name, int user_id, int group_id,
+ uint32_t usage, size_t size);
+ BufferProducer(const std::string& name, int user_id, int group_id,
+ uint64_t producer_usage, uint64_t consumer_usage, size_t size);
+
+ // Constructs a channel to persistent buffer by name only. The buffer must
+ // have been previously created or made persistent.
+ explicit BufferProducer(const std::string& name);
+
+ // Imports the given file handle to a producer channel, taking ownership.
+ explicit BufferProducer(LocalChannelHandle channel);
+};
+
+// This is a connection to a producer buffer, which can be located in another
+// application. When that buffer is Post()ed, this fd will be signaled and
+// Acquire allows read access. The user is responsible for making sure that
+// Acquire is called with the correct metadata structure. The only guarantee the
+// API currently provides is that an Acquire() with metadata of the wrong size
+// will fail.
+class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> {
+ public:
+ // This call assumes ownership of |fd|.
+ static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel);
+ static std::unique_ptr<BufferConsumer> Import(
+ Status<LocalChannelHandle> status);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| will be set to a fence to wait on until the buffer is ready.
+ // This call will only succeed after the fd is signalled. This call may be
+ // performed as an alternative to the Acquire() with metadata. In such cases
+ // the metadata is not read.
+ //
+ // This returns zero or negative unix error code.
+ int Acquire(LocalHandle* ready_fence);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| is set to a fence signaling that the contents of the buffer
+ // are available. This call will only succeed if the buffer is in the posted
+ // state.
+ // Returns zero on success, or a negative errno code otherwise.
+ int Acquire(LocalHandle* ready_fence, void* meta, size_t meta_size_bytes);
+
+ // Attempt to retrieve a post event from buffer hub. If successful,
+ // |ready_fence| is set to a fence to wait on until the buffer is ready. This
+ // call will only succeed after the fd is signaled. This returns zero or a
+ // negative unix error code.
+ template <typename Meta>
+ int Acquire(LocalHandle* ready_fence, Meta* meta) {
+ return Acquire(ready_fence, meta, sizeof(*meta));
+ }
+
+ // This should be called after a successful Acquire call. If the fence is
+ // valid the fence determines the buffer usage, otherwise the buffer is
+ // released immediately.
+ // This returns zero or a negative unix error code.
+ int Release(const LocalHandle& release_fence);
+
+ // Asynchronously releases a buffer. Similar to the synchronous version above,
+ // except that it does not wait for BufferHub to reply with success or error,
+ // nor does it transfer a release fence. This version may be used in
+ // situations where a release fence is not needed. Because of the asynchronous
+ // nature of the underlying message, no error is returned if this method is
+ // called when the buffer is in an incorrect state. Returns zero if sending
+ // the message succeeded, or a negative errno code otherwise.
+ int ReleaseAsync();
+
+ // May be called after or instead of Acquire to indicate that the consumer
+ // does not need to access the buffer this cycle. This returns zero or a
+ // negative unix error code.
+ int Discard();
+
+ // When set, this consumer is no longer notified when this buffer is
+ // available. The system behaves as if Discard() is immediately called
+ // whenever the buffer is posted. If ignore is set to true while a buffer is
+ // pending, it will act as if Discard() was also called.
+ // This returns zero or a negative unix error code.
+ int SetIgnore(bool ignore);
+
+ private:
+ friend BASE;
+
+ explicit BufferConsumer(LocalChannelHandle channel);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_CLIENT_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
new file mode 100644
index 0000000000..ffdc9e2e79
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -0,0 +1,241 @@
+#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
+#define ANDROID_DVR_BUFFERHUB_RPC_H_
+
+#include <cutils/native_handle.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/types.h>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FileHandleType>
+class NativeBufferHandle {
+ public:
+ NativeBufferHandle() { Clear(); }
+ NativeBufferHandle(const IonBuffer& buffer, int id)
+ : id_(id),
+ stride_(buffer.stride()),
+ width_(buffer.width()),
+ height_(buffer.height()),
+ layer_count_(buffer.layer_count()),
+ format_(buffer.format()),
+ usage_(buffer.usage()) {
+ // Populate the fd and int vectors: native_handle->data[] is an array of fds
+ // followed by an array of opaque ints.
+ const int fd_count = buffer.handle()->numFds;
+ const int int_count = buffer.handle()->numInts;
+ for (int i = 0; i < fd_count; i++) {
+ fds_.emplace_back(FileHandleType::AsDuplicate(buffer.handle()->data[i]));
+ }
+ for (int i = 0; i < int_count; i++) {
+ opaque_ints_.push_back(buffer.handle()->data[fd_count + i]);
+ }
+ }
+ NativeBufferHandle(NativeBufferHandle&& other) = default;
+ NativeBufferHandle& operator=(NativeBufferHandle&& other) = default;
+
+ // Imports the native handle into the given IonBuffer instance.
+ int Import(IonBuffer* buffer) {
+ // This is annoying, but we need to convert the vector of FileHandles into a
+ // vector of ints for the Import API.
+ std::vector<int> fd_ints;
+ for (const auto& fd : fds_)
+ fd_ints.push_back(fd.Get());
+
+ const int ret =
+ buffer->Import(fd_ints.data(), fd_ints.size(), opaque_ints_.data(),
+ opaque_ints_.size(), width_, height_, layer_count_,
+ stride_, format_, usage_);
+ if (ret < 0)
+ return ret;
+
+ // Import succeeded, release the file handles which are now owned by the
+ // IonBuffer and clear members.
+ for (auto& fd : fds_)
+ fd.Release();
+ opaque_ints_.clear();
+ Clear();
+
+ return 0;
+ }
+
+ int id() const { return id_; }
+ size_t IntCount() const { return opaque_ints_.size(); }
+ size_t FdCount() const { return fds_.size(); }
+
+ private:
+ int id_;
+ uint32_t stride_;
+ uint32_t width_;
+ uint32_t height_;
+ uint32_t layer_count_;
+ uint32_t format_;
+ uint64_t usage_;
+ std::vector<int> opaque_ints_;
+ std::vector<FileHandleType> fds_;
+
+ void Clear() {
+ id_ = -1;
+ stride_ = width_ = height_ = format_ = usage_ = 0;
+ }
+
+ PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle<FileHandleType>, id_, stride_,
+ width_, height_, layer_count_, format_, usage_,
+ opaque_ints_, fds_);
+
+ NativeBufferHandle(const NativeBufferHandle&) = delete;
+ void operator=(const NativeBufferHandle&) = delete;
+};
+
+using BorrowedNativeBufferHandle = NativeBufferHandle<pdx::BorrowedHandle>;
+using LocalNativeBufferHandle = NativeBufferHandle<pdx::LocalHandle>;
+
+template <typename FileHandleType>
+class FenceHandle {
+ public:
+ FenceHandle() = default;
+ explicit FenceHandle(int fence) : fence_{fence} {}
+ explicit FenceHandle(FileHandleType&& fence) : fence_{std::move(fence)} {}
+ FenceHandle(FenceHandle&&) = default;
+ FenceHandle& operator=(FenceHandle&&) = default;
+
+ explicit operator bool() const { return fence_.IsValid(); }
+
+ const FileHandleType& get() const { fence_; }
+ FileHandleType&& take() { return std::move(fence_); }
+
+ int get_fd() const { return fence_.Get(); }
+ void close() { fence_.Close(); }
+
+ FenceHandle<pdx::BorrowedHandle> borrow() const {
+ return FenceHandle<pdx::BorrowedHandle>(fence_.Borrow());
+ }
+
+ private:
+ FileHandleType fence_;
+
+ PDX_SERIALIZABLE_MEMBERS(FenceHandle<FileHandleType>, fence_);
+
+ FenceHandle(const FenceHandle&) = delete;
+ void operator=(const FenceHandle&) = delete;
+};
+
+using LocalFence = FenceHandle<pdx::LocalHandle>;
+using BorrowedFence = FenceHandle<pdx::BorrowedHandle>;
+
+struct QueueInfo {
+ size_t meta_size_bytes;
+ int id;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(QueueInfo, meta_size_bytes, id);
+};
+
+struct UsagePolicy {
+ uint64_t usage_set_mask;
+ uint64_t usage_clear_mask;
+ uint64_t usage_deny_set_mask;
+ uint64_t usage_deny_clear_mask;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(UsagePolicy, usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask);
+};
+
+// BufferHub Service RPC interface. Defines the endpoints, op codes, and method
+// type signatures supported by bufferhubd.
+struct BufferHubRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/buffer_hub/client";
+
+ // |BufferHubQueue| will keep track of at most this value of buffers.
+ // Attempts at runtime to increase the number of buffers past this
+ // will fail. Note that the value is in sync with |android::BufferQueue|, so
+ // that slot id can be shared between |android::dvr::BufferHubQueueProducer|
+ // and |android::BufferQueueProducer| which both implements the same
+ // interface: |android::IGraphicBufferProducer|.
+ static constexpr size_t kMaxQueueCapacity =
+ android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+ // Op codes.
+ enum {
+ kOpCreateBuffer = 0,
+ kOpCreatePersistentBuffer,
+ kOpGetPersistentBuffer,
+ kOpGetBuffer,
+ kOpNewConsumer,
+ kOpProducerMakePersistent,
+ kOpProducerRemovePersistence,
+ kOpProducerPost,
+ kOpProducerGain,
+ kOpConsumerAcquire,
+ kOpConsumerRelease,
+ kOpConsumerSetIgnore,
+ kOpCreateProducerQueue,
+ kOpCreateConsumerQueue,
+ kOpGetQueueInfo,
+ kOpProducerQueueAllocateBuffers,
+ kOpProducerQueueDetachBuffer,
+ kOpConsumerQueueImportBuffers,
+ };
+
+ // Aliases.
+ using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ using LocalHandle = pdx::LocalHandle;
+ using Void = pdx::rpc::Void;
+
+ // Methods.
+ PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer,
+ void(uint32_t width, uint32_t height, uint32_t format,
+ uint64_t usage, size_t meta_size_bytes));
+ PDX_REMOTE_METHOD(CreatePersistentBuffer, kOpCreatePersistentBuffer,
+ void(const std::string& name, int user_id, int group_id,
+ uint32_t width, uint32_t height, uint32_t format,
+ uint64_t usage, size_t meta_size_bytes));
+ PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer,
+ void(const std::string& name));
+ PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer,
+ NativeBufferHandle<LocalHandle>(Void));
+ PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void));
+ PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent,
+ void(const std::string& name, int user_id, int group_id));
+ PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence,
+ void(Void));
+ PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost,
+ void(LocalFence acquire_fence, MetaData));
+ PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void));
+ PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire,
+ std::pair<LocalFence, MetaData>(std::size_t metadata_size));
+ PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
+ void(LocalFence release_fence));
+ PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore));
+
+ // Buffer Queue Methods.
+ PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue,
+ QueueInfo(size_t meta_size_bytes,
+ const UsagePolicy& usage_policy));
+ PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue,
+ LocalChannelHandle(Void));
+ PDX_REMOTE_METHOD(GetQueueInfo, kOpGetQueueInfo, QueueInfo(Void));
+ PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers,
+ kOpProducerQueueAllocateBuffers,
+ std::vector<std::pair<LocalChannelHandle, size_t>>(
+ uint32_t width, uint32_t height, uint32_t layer_count,
+ uint32_t format, uint64_t usage, size_t buffer_count));
+ PDX_REMOTE_METHOD(ProducerQueueDetachBuffer, kOpProducerQueueDetachBuffer,
+ void(size_t slot));
+ PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
+ std::vector<std::pair<LocalChannelHandle, size_t>>(Void));
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFERHUB_RPC_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
new file mode 100644
index 0000000000..0d337f7b3b
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -0,0 +1,96 @@
+#ifndef ANDROID_DVR_ION_BUFFER_H_
+#define ANDROID_DVR_ION_BUFFER_H_
+
+#include <hardware/gralloc.h>
+#include <log/log.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+ IonBuffer();
+ IonBuffer(uint32_t width, uint32_t height, uint32_t format, uint64_t usage);
+ IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+ uint32_t stride, uint32_t format, uint64_t usage);
+ IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t stride, uint32_t format,
+ uint64_t usage);
+ ~IonBuffer();
+
+ IonBuffer(IonBuffer&& other);
+ IonBuffer& operator=(IonBuffer&& other);
+
+ // Frees the underlying native handle and leaves the instance initialized to
+ // empty.
+ void FreeHandle();
+
+ // Allocates a new native handle with the given parameters, freeing the
+ // previous native handle if necessary. Returns 0 on success or a negative
+ // errno code otherwise. If allocation fails the previous native handle is
+ // left intact.
+ int Alloc(uint32_t width, uint32_t height, uint32_t layer_count,
+ uint32_t format, uint64_t usage);
+
+ // Resets the underlying native handle and parameters, freeing the previous
+ // native handle if necessary.
+ void Reset(buffer_handle_t handle, uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t stride, uint32_t format,
+ uint64_t usage);
+
+ // Like Reset but also registers the native handle, which is necessary for
+ // native handles received over IPC. Returns 0 on success or a negative errno
+ // code otherwise. If import fails the previous native handle is left intact.
+ int Import(buffer_handle_t handle, uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t stride, uint32_t format,
+ uint64_t usage);
+
+ // Like Reset but imports a native handle from raw fd and int arrays. Returns
+ // 0 on success or a negative errno code otherwise. If import fails the
+ // previous native handle is left intact.
+ int Import(const int* fd_array, int fd_count, const int* int_array,
+ int int_count, uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t stride, uint32_t format,
+ uint64_t usage);
+
+ // Duplicates the native handle underlying |other| and then imports it. This
+ // is useful for creating multiple, independent views of the same Ion/Gralloc
+ // buffer. Returns 0 on success or a negative errno code otherwise. If
+ // duplication or import fail the previous native handle is left intact.
+ int Duplicate(const IonBuffer* other);
+
+ int Lock(uint32_t usage, int x, int y, int width, int height, void** address);
+ int LockYUV(uint32_t usage, int x, int y, int width, int height,
+ struct android_ycbcr* yuv);
+ int Unlock();
+
+ const sp<GraphicBuffer>& buffer() const { return buffer_; }
+ buffer_handle_t handle() const {
+ return buffer_.get() ? buffer_->handle : nullptr;
+ }
+ uint32_t width() const { return buffer_.get() ? buffer_->getWidth() : 0; }
+ uint32_t height() const { return buffer_.get() ? buffer_->getHeight() : 0; }
+ uint32_t layer_count() const {
+ return buffer_.get() ? buffer_->getLayerCount() : 0;
+ }
+ uint32_t stride() const { return buffer_.get() ? buffer_->getStride() : 0; }
+ uint32_t format() const {
+ return buffer_.get() ? buffer_->getPixelFormat() : 0;
+ }
+ uint64_t usage() const {
+ return buffer_.get() ? static_cast<uint64_t>(buffer_->getUsage()) : 0;
+ }
+
+ private:
+ sp<GraphicBuffer> buffer_;
+
+ IonBuffer(const IonBuffer&) = delete;
+ void operator=(const IonBuffer&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_ION_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
new file mode 100644
index 0000000000..b4ef2f50d7
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -0,0 +1,176 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_H_
+#define ANDROID_DVR_NATIVE_BUFFER_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <log/log.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/RefBase.h>
+
+#include <private/dvr/buffer_hub_client.h>
+
+namespace android {
+namespace dvr {
+
+// ANativeWindowBuffer is the abstraction Android HALs and frameworks use to
+// pass around hardware graphics buffers. The following classes implement this
+// abstraction with different DVR backing buffers, all of which provide
+// different semantics on top of ion/gralloc buffers.
+
+// An implementation of ANativeWindowBuffer backed by an IonBuffer.
+class NativeBuffer
+ : public android::ANativeObjectBase<ANativeWindowBuffer, NativeBuffer,
+ android::LightRefBase<NativeBuffer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ explicit NativeBuffer(const std::shared_ptr<IonBuffer>& buffer)
+ : BASE(), buffer_(buffer), fence_(kEmptyFence) {
+ ANativeWindowBuffer::width = buffer->width();
+ ANativeWindowBuffer::height = buffer->height();
+ ANativeWindowBuffer::stride = buffer->stride();
+ ANativeWindowBuffer::format = buffer->format();
+ ANativeWindowBuffer::usage = buffer->usage();
+ handle = buffer_->handle();
+ }
+
+ virtual ~NativeBuffer() {}
+
+ std::shared_ptr<IonBuffer> buffer() { return buffer_; }
+ int fence() const { return fence_.Get(); }
+
+ void SetFence(int fence) { fence_.Reset(fence); }
+
+ private:
+ friend class android::LightRefBase<NativeBuffer>;
+
+ std::shared_ptr<IonBuffer> buffer_;
+ pdx::LocalHandle fence_;
+
+ NativeBuffer(const NativeBuffer&) = delete;
+ void operator=(NativeBuffer&) = delete;
+};
+
+// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a
+// BufferProducer.
+class NativeBufferProducer : public android::ANativeObjectBase<
+ ANativeWindowBuffer, NativeBufferProducer,
+ android::LightRefBase<NativeBufferProducer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer,
+ EGLDisplay display, uint32_t surface_buffer_index)
+ : BASE(),
+ buffer_(buffer),
+ surface_buffer_index_(surface_buffer_index),
+ display_(display) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ handle = buffer_->native_handle();
+ }
+
+ explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer)
+ : NativeBufferProducer(buffer, nullptr, 0) {}
+
+ virtual ~NativeBufferProducer() {
+ for (EGLImageKHR egl_image : egl_images_) {
+ if (egl_image != EGL_NO_IMAGE_KHR)
+ eglDestroyImageKHR(display_, egl_image);
+ }
+ }
+
+ EGLImageKHR image_khr(int index) const { return egl_images_[index]; }
+ std::shared_ptr<BufferProducer> buffer() const { return buffer_; }
+ int release_fence() const { return release_fence_.Get(); }
+ uint32_t surface_buffer_index() const { return surface_buffer_index_; }
+
+ // Return the release fence, passing ownership to the caller.
+ pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); }
+
+ // Post the buffer consumer, closing the acquire and release fences.
+ int Post(int acquire_fence, uint64_t sequence) {
+ release_fence_.Close();
+ return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence);
+ }
+
+ // Gain the buffer producer, closing the previous release fence if valid.
+ int Gain() { return buffer_->Gain(&release_fence_); }
+
+ // Asynchronously gain the buffer, closing the previous release fence.
+ int GainAsync() {
+ release_fence_.Close();
+ return buffer_->GainAsync();
+ }
+
+ private:
+ friend class android::LightRefBase<NativeBufferProducer>;
+
+ std::shared_ptr<BufferProducer> buffer_;
+ pdx::LocalHandle release_fence_;
+ std::vector<EGLImageKHR> egl_images_;
+ uint32_t surface_buffer_index_;
+ EGLDisplay display_;
+
+ NativeBufferProducer(const NativeBufferProducer&) = delete;
+ void operator=(NativeBufferProducer&) = delete;
+};
+
+// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a
+// BufferConsumer.
+class NativeBufferConsumer : public android::ANativeObjectBase<
+ ANativeWindowBuffer, NativeBufferConsumer,
+ android::LightRefBase<NativeBufferConsumer>> {
+ public:
+ static constexpr int kEmptyFence = -1;
+
+ explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer)
+ : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) {
+ ANativeWindowBuffer::width = buffer_->width();
+ ANativeWindowBuffer::height = buffer_->height();
+ ANativeWindowBuffer::stride = buffer_->stride();
+ ANativeWindowBuffer::format = buffer_->format();
+ ANativeWindowBuffer::usage = buffer_->usage();
+ handle = buffer_->native_handle();
+ }
+
+ virtual ~NativeBufferConsumer() {}
+
+ std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+ int acquire_fence() const { return acquire_fence_.Get(); }
+ uint64_t sequence() const { return sequence_; }
+
+ // Return the acquire fence, passing ownership to the caller.
+ pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); }
+
+ // Acquire the underlying buffer consumer, closing the previous acquire fence
+ // if valid.
+ int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); }
+
+ // Release the buffer consumer, closing the acquire and release fences if
+ // valid.
+ int Release(int release_fence) {
+ acquire_fence_.Close();
+ sequence_ = 0;
+ return buffer_->Release(pdx::LocalHandle(release_fence));
+ }
+
+ private:
+ friend class android::LightRefBase<NativeBufferConsumer>;
+
+ std::shared_ptr<BufferConsumer> buffer_;
+ pdx::LocalHandle acquire_fence_;
+ uint64_t sequence_;
+
+ NativeBufferConsumer(const NativeBufferConsumer&) = delete;
+ void operator=(NativeBufferConsumer&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_NATIVE_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
new file mode 100644
index 0000000000..cbaa24a7bc
--- /dev/null
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -0,0 +1,240 @@
+#include <private/dvr/ion_buffer.h>
+
+#include <log/log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+namespace {
+
+constexpr uint32_t kDefaultGraphicBufferLayerCount = 1;
+
+} // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+IonBuffer::IonBuffer() : IonBuffer(nullptr, 0, 0, 0, 0, 0, 0) {}
+
+IonBuffer::IonBuffer(uint32_t width, uint32_t height, uint32_t format,
+ uint64_t usage)
+ : IonBuffer() {
+ Alloc(width, height, kDefaultGraphicBufferLayerCount, format, usage);
+}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+ uint32_t stride, uint32_t format, uint64_t usage)
+ : IonBuffer(handle, width, height, kDefaultGraphicBufferLayerCount, stride,
+ format, usage) {}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t stride, uint32_t format,
+ uint64_t usage)
+ : buffer_(nullptr) {
+ ALOGD_IF(TRACE,
+ "IonBuffer::IonBuffer: handle=%p width=%u height=%u layer_count=%u "
+ "stride=%u format=%u usage=%" PRIx64,
+ handle, width, height, layer_count, stride, format, usage);
+ if (handle != 0) {
+ Import(handle, width, height, layer_count, stride, format, usage);
+ }
+}
+
+IonBuffer::~IonBuffer() {
+ ALOGD_IF(TRACE,
+ "IonBuffer::~IonBuffer: handle=%p width=%u height=%u stride=%u "
+ "format=%u usage=%" PRIx64,
+ handle(), width(), height(), stride(), format(), usage());
+ FreeHandle();
+}
+
+IonBuffer::IonBuffer(IonBuffer&& other) : IonBuffer() {
+ *this = std::move(other);
+}
+
+IonBuffer& IonBuffer::operator=(IonBuffer&& other) {
+ ALOGD_IF(TRACE, "IonBuffer::operator=: handle_=%p other.handle_=%p", handle(),
+ other.handle());
+
+ if (this != &other) {
+ buffer_ = other.buffer_;
+ other.FreeHandle();
+ }
+ return *this;
+}
+
+void IonBuffer::FreeHandle() {
+ if (buffer_.get()) {
+ // GraphicBuffer unregisters and cleans up the handle if needed
+ buffer_ = nullptr;
+ }
+}
+
+int IonBuffer::Alloc(uint32_t width, uint32_t height, uint32_t layer_count,
+ uint32_t format, uint64_t usage) {
+ ALOGD_IF(TRACE,
+ "IonBuffer::Alloc: width=%u height=%u layer_count=%u format=%u "
+ "usage=%" PRIx64, width, height, layer_count, format, usage);
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(width, height, format, layer_count, usage);
+ if (buffer->initCheck() != OK) {
+ ALOGE("IonBuffer::Aloc: Failed to allocate buffer");
+ return -EINVAL;
+ } else {
+ buffer_ = buffer;
+ return 0;
+ }
+}
+
+void IonBuffer::Reset(buffer_handle_t handle, uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t stride, uint32_t format,
+ uint64_t usage) {
+ ALOGD_IF(TRACE,
+ "IonBuffer::Reset: handle=%p width=%u height=%u layer_count=%u "
+ "stride=%u format=%u usage=%" PRIx64,
+ handle, width, height, layer_count, stride, format, usage);
+ Import(handle, width, height, layer_count, stride, format, usage);
+}
+
+int IonBuffer::Import(buffer_handle_t handle, uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t stride, uint32_t format,
+ uint64_t usage) {
+ ATRACE_NAME("IonBuffer::Import1");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Import: handle=%p width=%u height=%u layer_count=%u "
+ "stride=%u format=%u usage=%" PRIx64,
+ handle, width, height, layer_count, stride, format, usage);
+ FreeHandle();
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(handle, GraphicBuffer::TAKE_UNREGISTERED_HANDLE, width,
+ height, format, layer_count, usage, stride);
+ if (buffer->initCheck() != OK) {
+ ALOGE("IonBuffer::Import: Failed to import buffer");
+ return -EINVAL;
+ } else {
+ buffer_ = buffer;
+ return 0;
+ }
+}
+
+int IonBuffer::Import(const int* fd_array, int fd_count, const int* int_array,
+ int int_count, uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t stride, uint32_t format,
+ uint64_t usage) {
+ ATRACE_NAME("IonBuffer::Import2");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Import: fd_count=%d int_count=%d width=%u height=%u "
+ "layer_count=%u stride=%u format=%u usage=%" PRIx64,
+ fd_count, int_count, width, height, layer_count, stride, format,
+ usage);
+
+ if (fd_count < 0 || int_count < 0) {
+ ALOGE("IonBuffer::Import: invalid arguments.");
+ return -EINVAL;
+ }
+
+ native_handle_t* handle = native_handle_create(fd_count, int_count);
+ if (!handle) {
+ ALOGE("IonBuffer::Import: failed to create new native handle.");
+ return -ENOMEM;
+ }
+
+ // Copy fd_array into the first part of handle->data and int_array right
+ // after it.
+ memcpy(handle->data, fd_array, sizeof(int) * fd_count);
+ memcpy(handle->data + fd_count, int_array, sizeof(int) * int_count);
+
+ const int ret =
+ Import(handle, width, height, layer_count, stride, format, usage);
+ if (ret < 0) {
+ ALOGE("IonBuffer::Import: failed to import raw native handle: %s",
+ strerror(-ret));
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+
+ return ret;
+}
+
+int IonBuffer::Duplicate(const IonBuffer* other) {
+ if (!other->handle())
+ return -EINVAL;
+
+ const int fd_count = other->handle()->numFds;
+ const int int_count = other->handle()->numInts;
+
+ if (fd_count < 0 || int_count < 0)
+ return -EINVAL;
+
+ native_handle_t* handle = native_handle_create(fd_count, int_count);
+ if (!handle) {
+ ALOGE("IonBuffer::Duplicate: Failed to create new native handle.");
+ return -ENOMEM;
+ }
+
+ // Duplicate the file descriptors from the other native handle.
+ for (int i = 0; i < fd_count; i++)
+ handle->data[i] = dup(other->handle()->data[i]);
+
+ // Copy the ints after the file descriptors.
+ memcpy(handle->data + fd_count, other->handle()->data + fd_count,
+ sizeof(int) * int_count);
+
+ const int ret =
+ Import(handle, other->width(), other->height(), other->layer_count(),
+ other->stride(), other->format(), other->usage());
+ if (ret < 0) {
+ ALOGE("IonBuffer::Duplicate: Failed to import duplicate native handle: %s",
+ strerror(-ret));
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+
+ return ret;
+}
+
+int IonBuffer::Lock(uint32_t usage, int x, int y, int width, int height,
+ void** address) {
+ ATRACE_NAME("IonBuffer::Lock");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d "
+ "address=%p",
+ handle(), usage, x, y, width, height, address);
+
+ status_t err =
+ buffer_->lock(usage, Rect(x, y, x + width, y + height), address);
+ if (err != NO_ERROR)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+int IonBuffer::LockYUV(uint32_t usage, int x, int y, int width, int height,
+ struct android_ycbcr* yuv) {
+ ATRACE_NAME("IonBuffer::LockYUV");
+ ALOGD_IF(TRACE,
+ "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d",
+ handle(), usage, x, y, width, height);
+
+ status_t err =
+ buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv);
+ if (err != NO_ERROR)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+int IonBuffer::Unlock() {
+ ATRACE_NAME("IonBuffer::Unlock");
+ ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle());
+
+ status_t err = buffer_->unlock();
+ if (err != NO_ERROR)
+ return -EINVAL;
+ else
+ return 0;
+}
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
new file mode 100644
index 0000000000..a587f95879
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -0,0 +1,59 @@
+// Copyright (C) 2016 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.
+
+sourceFiles = [
+ "buffer_hub_queue_client.cpp",
+ "buffer_hub_queue_producer.cpp",
+]
+
+includeFiles = [
+ "include",
+]
+
+headerLibraries = [
+ "libdvr_headers",
+]
+
+staticLibraries = [
+ "libbufferhub",
+ "libdvrcommon",
+ "libpdx_default_transport",
+]
+
+sharedLibraries = [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libhardware",
+ "liblog",
+ "libui",
+ "libutils",
+ "libgui",
+]
+
+cc_library {
+ name: "libbufferhubqueue",
+ cflags: [
+ "-DLOG_TAG=\"libbufferhubqueue\"",
+ "-DTRACE=0",
+ ],
+ srcs: sourceFiles,
+ export_include_dirs: includeFiles,
+ export_static_lib_headers: staticLibraries,
+ header_libs: headerLibraries,
+ static_libs: staticLibraries,
+ shared_libs: sharedLibraries,
+}
+
+subdirs = ["tests"]
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
new file mode 100644
index 0000000000..012a4e70e4
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -0,0 +1,635 @@
+#include "include/private/dvr/buffer_hub_queue_client.h"
+
+#include <inttypes.h>
+#include <log/log.h>
+#include <poll.h>
+#include <sys/epoll.h>
+
+#include <array>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+#define RETRY_EINTR(fnc_call) \
+ ([&]() -> decltype(fnc_call) { \
+ decltype(fnc_call) result; \
+ do { \
+ result = (fnc_call); \
+ } while (result == -1 && errno == EINTR); \
+ return result; \
+ })()
+
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle)
+ : Client{pdx::default_transport::ClientChannel::Create(
+ std::move(channel_handle))},
+ meta_size_(0),
+ buffers_(BufferHubQueue::kMaxQueueCapacity),
+ epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false),
+ available_buffers_(BufferHubQueue::kMaxQueueCapacity),
+ fences_(BufferHubQueue::kMaxQueueCapacity),
+ capacity_(0),
+ id_(-1) {
+ Initialize();
+}
+
+BufferHubQueue::BufferHubQueue(const std::string& endpoint_path)
+ : Client{pdx::default_transport::ClientChannelFactory::Create(
+ endpoint_path)},
+ meta_size_(0),
+ buffers_(BufferHubQueue::kMaxQueueCapacity),
+ epollhup_pending_(BufferHubQueue::kMaxQueueCapacity, false),
+ available_buffers_(BufferHubQueue::kMaxQueueCapacity),
+ fences_(BufferHubQueue::kMaxQueueCapacity),
+ capacity_(0),
+ id_(-1) {
+ Initialize();
+}
+
+void BufferHubQueue::Initialize() {
+ int ret = epoll_fd_.Create();
+ if (ret < 0) {
+ ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s",
+ strerror(-ret));
+ return;
+ }
+
+ epoll_event event = {.events = EPOLLIN | EPOLLET,
+ .data = {.u64 = static_cast<uint64_t>(
+ BufferHubQueue::kEpollQueueEventIndex)}};
+ ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
+ if (ret < 0) {
+ ALOGE("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s",
+ strerror(-ret));
+ }
+}
+
+Status<void> BufferHubQueue::ImportQueue() {
+ auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>();
+ if (!status) {
+ ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s",
+ status.GetErrorMessage().c_str());
+ return ErrorStatus(status.error());
+ } else {
+ SetupQueue(status.get().meta_size_bytes, status.get().id);
+ return {};
+ }
+}
+
+void BufferHubQueue::SetupQueue(size_t meta_size_bytes, int id) {
+ meta_size_ = meta_size_bytes;
+ id_ = id;
+ meta_buffer_tmp_.reset(meta_size_ > 0 ? new uint8_t[meta_size_] : nullptr);
+}
+
+std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
+ if (auto status = CreateConsumerQueueHandle())
+ return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
+ else
+ return nullptr;
+}
+
+std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() {
+ if (auto status = CreateConsumerQueueHandle())
+ return std::unique_ptr<ConsumerQueue>(
+ new ConsumerQueue(status.take(), true));
+ else
+ return nullptr;
+}
+
+Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle() {
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>();
+ if (!status) {
+ ALOGE(
+ "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: "
+ "%s",
+ status.GetErrorMessage().c_str());
+ return ErrorStatus(status.error());
+ }
+
+ return status;
+}
+
+bool BufferHubQueue::WaitForBuffers(int timeout) {
+ std::array<epoll_event, kMaxEvents> events;
+
+ // Loop at least once to check for hangups.
+ do {
+ ALOGD_IF(
+ TRACE,
+ "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu",
+ id(), count(), capacity());
+
+ // If there is already a buffer then just check for hangup without waiting.
+ const int ret = epoll_fd_.Wait(events.data(), events.size(),
+ count() == 0 ? timeout : 0);
+
+ if (ret == 0) {
+ ALOGI_IF(TRACE,
+ "BufferHubQueue::WaitForBuffers: No events before timeout: "
+ "queue_id=%d",
+ id());
+ return count() != 0;
+ }
+
+ if (ret < 0 && ret != -EINTR) {
+ ALOGE("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s",
+ strerror(-ret));
+ return false;
+ }
+
+ const int num_events = ret;
+
+ // A BufferQueue's epoll fd tracks N+1 events, where there are N events,
+ // one for each buffer, in the queue and one extra event for the queue
+ // client itself.
+ for (int i = 0; i < num_events; i++) {
+ int64_t index = static_cast<int64_t>(events[i].data.u64);
+
+ ALOGD_IF(TRACE,
+ "BufferHubQueue::WaitForBuffers: event %d: index=%" PRId64, i,
+ index);
+
+ if (is_buffer_event_index(index)) {
+ HandleBufferEvent(static_cast<size_t>(index), events[i].events);
+ } else if (is_queue_event_index(index)) {
+ HandleQueueEvent(events[i].events);
+ } else {
+ ALOGW("BufferHubQueue::WaitForBuffers: Unknown event index: %" PRId64,
+ index);
+ }
+ }
+ } while (count() == 0 && capacity() > 0 && !hung_up());
+
+ return count() != 0;
+}
+
+void BufferHubQueue::HandleBufferEvent(size_t slot, int poll_events) {
+ auto buffer = buffers_[slot];
+ if (!buffer) {
+ ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
+ return;
+ }
+
+ auto status = buffer->GetEventMask(poll_events);
+ if (!status) {
+ ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
+ status.GetErrorMessage().c_str());
+ return;
+ }
+
+ const int events = status.get();
+ if (events & EPOLLIN) {
+ const int ret = OnBufferReady(buffer, &fences_[slot]);
+ if (ret == 0 || ret == -EALREADY || ret == -EBUSY) {
+ // Only enqueue the buffer if it moves to or is already in the state
+ // requested in OnBufferReady(). If the buffer is busy this means that the
+ // buffer moved from released to posted when a new consumer was created
+ // before the ProducerQueue had a chance to regain it. This is a valid
+ // transition that we have to handle because edge triggered poll events
+ // latch the ready state even if it is later de-asserted -- don't enqueue
+ // or print an error log in this case.
+ if (ret != -EBUSY)
+ Enqueue(buffer, slot);
+ } else {
+ ALOGE(
+ "BufferHubQueue::HandleBufferEvent: Failed to set buffer ready, "
+ "queue_id=%d buffer_id=%d: %s",
+ id(), buffer->id(), strerror(-ret));
+ }
+ } else if (events & EPOLLHUP) {
+ // This might be caused by producer replacing an existing buffer slot, or
+ // when BufferHubQueue is shutting down. For the first case, currently the
+ // epoll FD is cleaned up when the replacement consumer client is imported,
+ // we shouldn't detach again if |epollhub_pending_[slot]| is set.
+ ALOGW(
+ "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP at slot: %zu, "
+ "buffer event fd: %d, EPOLLHUP pending: %d",
+ slot, buffer->event_fd(), int{epollhup_pending_[slot]});
+ if (epollhup_pending_[slot]) {
+ epollhup_pending_[slot] = false;
+ } else {
+ DetachBuffer(slot);
+ }
+ } else {
+ ALOGW(
+ "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll "
+ "events=%d",
+ slot, events);
+ }
+}
+
+void BufferHubQueue::HandleQueueEvent(int poll_event) {
+ auto status = GetEventMask(poll_event);
+ if (!status) {
+ ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
+ status.GetErrorMessage().c_str());
+ return;
+ }
+
+ const int events = status.get();
+ if (events & EPOLLIN) {
+ // Note that after buffer imports, if |count()| still returns 0, epoll
+ // wait will be tried again to acquire the newly imported buffer.
+ auto buffer_status = OnBufferAllocated();
+ if (!buffer_status) {
+ ALOGE("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s",
+ buffer_status.GetErrorMessage().c_str());
+ }
+ } else if (events & EPOLLHUP) {
+ ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!");
+ hung_up_ = true;
+ } else {
+ ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%x", events);
+ }
+}
+
+int BufferHubQueue::AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf,
+ size_t slot) {
+ if (is_full()) {
+ // TODO(jwcai) Move the check into Producer's AllocateBuffer and consumer's
+ // import buffer.
+ ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu",
+ capacity_);
+ return -E2BIG;
+ }
+
+ if (buffers_[slot] != nullptr) {
+ // Replace the buffer if the slot is preoccupied. This could happen when the
+ // producer side replaced the slot with a newly allocated buffer. Detach the
+ // buffer before setting up with the new one.
+ DetachBuffer(slot);
+ epollhup_pending_[slot] = true;
+ }
+
+ epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u64 = slot}};
+ const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, buf->event_fd(), &event);
+ if (ret < 0) {
+ ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s",
+ strerror(-ret));
+ return ret;
+ }
+
+ buffers_[slot] = buf;
+ capacity_++;
+ return 0;
+}
+
+int BufferHubQueue::DetachBuffer(size_t slot) {
+ auto& buf = buffers_[slot];
+ if (buf == nullptr) {
+ ALOGE("BufferHubQueue::DetachBuffer: Invalid slot: %zu", slot);
+ return -EINVAL;
+ }
+
+ const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, buf->event_fd(), nullptr);
+ if (ret < 0) {
+ ALOGE(
+ "BufferHubQueue::DetachBuffer: Failed to detach buffer from epoll set: "
+ "%s",
+ strerror(-ret));
+ return ret;
+ }
+
+ buffers_[slot] = nullptr;
+ capacity_--;
+ return 0;
+}
+
+void BufferHubQueue::Enqueue(const std::shared_ptr<BufferHubBuffer>& buf,
+ size_t slot) {
+ if (count() == capacity_) {
+ ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!");
+ return;
+ }
+
+ // Set slot buffer back to vector.
+ // TODO(jwcai) Here have to dynamically allocate BufferInfo::metadata due to
+ // the limitation of the RingBuffer we are using. Would be better to refactor
+ // that.
+ BufferInfo buffer_info(slot, meta_size_);
+ buffer_info.buffer = buf;
+ // Swap metadata loaded during onBufferReady into vector.
+ std::swap(buffer_info.metadata, meta_buffer_tmp_);
+
+ available_buffers_.Append(std::move(buffer_info));
+}
+
+Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(
+ int timeout, size_t* slot, void* meta, LocalHandle* fence) {
+ ALOGD_IF(TRACE, "Dequeue: count=%zu, timeout=%d", count(), timeout);
+
+ if (!WaitForBuffers(timeout))
+ return ErrorStatus(ETIMEDOUT);
+
+ std::shared_ptr<BufferHubBuffer> buf;
+ BufferInfo& buffer_info = available_buffers_.Front();
+
+ *fence = std::move(fences_[buffer_info.slot]);
+
+ // Report current pos as the output slot.
+ std::swap(buffer_info.slot, *slot);
+ // Swap buffer from vector to be returned later.
+ std::swap(buffer_info.buffer, buf);
+ // Swap metadata from vector into tmp so that we can write out to |meta|.
+ std::swap(buffer_info.metadata, meta_buffer_tmp_);
+
+ available_buffers_.PopFront();
+
+ if (!buf) {
+ ALOGE("BufferHubQueue::Dequeue: Buffer to be dequeued is nullptr");
+ return ErrorStatus(ENOBUFS);
+ }
+
+ if (meta) {
+ std::copy(meta_buffer_tmp_.get(), meta_buffer_tmp_.get() + meta_size_,
+ reinterpret_cast<uint8_t*>(meta));
+ }
+
+ return {std::move(buf)};
+}
+
+ProducerQueue::ProducerQueue(size_t meta_size)
+ : ProducerQueue(meta_size, 0, 0, 0, 0) {}
+
+ProducerQueue::ProducerQueue(LocalChannelHandle handle)
+ : BASE(std::move(handle)) {
+ auto status = ImportQueue();
+ if (!status) {
+ ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ }
+}
+
+ProducerQueue::ProducerQueue(size_t meta_size, uint64_t usage_set_mask,
+ uint64_t usage_clear_mask,
+ uint64_t usage_deny_set_mask,
+ uint64_t usage_deny_clear_mask)
+ : BASE(BufferHubRPC::kClientPath) {
+ auto status = InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+ meta_size, UsagePolicy{usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask});
+ if (!status) {
+ ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ return;
+ }
+
+ SetupQueue(status.get().meta_size_bytes, status.get().id);
+}
+
+int ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t format,
+ uint64_t usage, size_t* out_slot) {
+ if (out_slot == nullptr) {
+ ALOGE("ProducerQueue::AllocateBuffer: Parameter out_slot cannot be null.");
+ return -EINVAL;
+ }
+
+ if (is_full()) {
+ ALOGE("ProducerQueue::AllocateBuffer queue is at maximum capacity: %zu",
+ capacity());
+ return -E2BIG;
+ }
+
+ const size_t kBufferCount = 1U;
+ Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
+ InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
+ width, height, layer_count, format, usage, kBufferCount);
+ if (!status) {
+ ALOGE("ProducerQueue::AllocateBuffer failed to create producer buffer: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ auto buffer_handle_slots = status.take();
+ LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != kBufferCount,
+ "BufferHubRPC::ProducerQueueAllocateBuffers should "
+ "return one and only one buffer handle.");
+
+ // We only allocate one buffer at a time.
+ auto& buffer_handle = buffer_handle_slots[0].first;
+ size_t buffer_slot = buffer_handle_slots[0].second;
+ ALOGD_IF(TRACE,
+ "ProducerQueue::AllocateBuffer, new buffer, channel_handle: %d",
+ buffer_handle.value());
+
+ *out_slot = buffer_slot;
+ return AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
+ buffer_slot);
+}
+
+int ProducerQueue::AddBuffer(const std::shared_ptr<BufferProducer>& buf,
+ size_t slot) {
+ ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
+ id(), buf->id(), slot);
+ // For producer buffer, we need to enqueue the newly added buffer
+ // immediately. Producer queue starts with all buffers in available state.
+ const int ret = BufferHubQueue::AddBuffer(buf, slot);
+ if (ret < 0)
+ return ret;
+
+ Enqueue(buf, slot);
+ return 0;
+}
+
+int ProducerQueue::DetachBuffer(size_t slot) {
+ auto status =
+ InvokeRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(slot);
+ if (!status) {
+ ALOGE("ProducerQueue::DetachBuffer: Failed to detach producer buffer: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ return BufferHubQueue::DetachBuffer(slot);
+}
+
+Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue(
+ int timeout, size_t* slot, LocalHandle* release_fence) {
+ if (slot == nullptr || release_fence == nullptr) {
+ ALOGE("ProducerQueue::Dequeue: Invalid parameter: slot=%p release_fence=%p",
+ slot, release_fence);
+ return ErrorStatus(EINVAL);
+ }
+
+ auto buffer_status =
+ BufferHubQueue::Dequeue(timeout, slot, nullptr, release_fence);
+ if (!buffer_status)
+ return buffer_status.error_status();
+
+ return {std::static_pointer_cast<BufferProducer>(buffer_status.take())};
+}
+
+int ProducerQueue::OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+ LocalHandle* release_fence) {
+ ALOGD_IF(TRACE, "ProducerQueue::OnBufferReady: queue_id=%d buffer_id=%d",
+ id(), buf->id());
+ auto buffer = std::static_pointer_cast<BufferProducer>(buf);
+ return buffer->Gain(release_fence);
+}
+
+ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import)
+ : BufferHubQueue(std::move(handle)), ignore_on_import_(ignore_on_import) {
+ auto status = ImportQueue();
+ if (!status) {
+ ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s",
+ status.GetErrorMessage().c_str());
+ Close(-status.error());
+ }
+
+ auto import_status = ImportBuffers();
+ if (import_status) {
+ ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.",
+ import_status.get());
+ } else {
+ ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s",
+ import_status.GetErrorMessage().c_str());
+ }
+}
+
+Status<size_t> ConsumerQueue::ImportBuffers() {
+ auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
+ if (!status) {
+ ALOGE("ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s",
+ status.GetErrorMessage().c_str());
+ return ErrorStatus(status.error());
+ }
+
+ int ret;
+ int last_error = 0;
+ int imported_buffers = 0;
+
+ auto buffer_handle_slots = status.take();
+ for (auto& buffer_handle_slot : buffer_handle_slots) {
+ ALOGD_IF(TRACE, "ConsumerQueue::ImportBuffers: buffer_handle=%d",
+ buffer_handle_slot.first.value());
+
+ std::unique_ptr<BufferConsumer> buffer_consumer =
+ BufferConsumer::Import(std::move(buffer_handle_slot.first));
+
+ // Setup ignore state before adding buffer to the queue.
+ if (ignore_on_import_) {
+ ALOGD_IF(TRACE,
+ "ConsumerQueue::ImportBuffers: Setting buffer to ignored state: "
+ "buffer_id=%d",
+ buffer_consumer->id());
+ ret = buffer_consumer->SetIgnore(true);
+ if (ret < 0) {
+ ALOGE(
+ "ConsumerQueue::ImportBuffers: Failed to set ignored state on "
+ "imported buffer buffer_id=%d: %s",
+ buffer_consumer->id(), strerror(-ret));
+ last_error = ret;
+ }
+ }
+
+ ret = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
+ if (ret < 0) {
+ ALOGE("ConsumerQueue::ImportBuffers: Failed to add buffer: %s",
+ strerror(-ret));
+ last_error = ret;
+ continue;
+ } else {
+ imported_buffers++;
+ }
+ }
+
+ if (imported_buffers > 0)
+ return {imported_buffers};
+ else
+ return ErrorStatus(-last_error);
+}
+
+int ConsumerQueue::AddBuffer(const std::shared_ptr<BufferConsumer>& buf,
+ size_t slot) {
+ ALOGD_IF(TRACE, "ConsumerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
+ id(), buf->id(), slot);
+ const int ret = BufferHubQueue::AddBuffer(buf, slot);
+ if (ret < 0)
+ return ret;
+
+ // Check to see if the buffer is already signaled. This is necessary to catch
+ // cases where buffers are already available; epoll edge triggered mode does
+ // not fire until and edge transition when adding new buffers to the epoll
+ // set.
+ const int kTimeoutMs = 0;
+ pollfd pfd{buf->event_fd(), POLLIN, 0};
+ const int count = RETRY_EINTR(poll(&pfd, 1, kTimeoutMs));
+ if (count < 0) {
+ const int error = errno;
+ ALOGE("ConsumerQueue::AddBuffer: Failed to poll consumer buffer: %s",
+ strerror(errno));
+ return -error;
+ }
+
+ if (count == 1)
+ HandleBufferEvent(slot, pfd.revents);
+
+ return 0;
+}
+
+Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue(
+ int timeout, size_t* slot, void* meta, size_t meta_size,
+ LocalHandle* acquire_fence) {
+ if (meta_size != meta_size_) {
+ ALOGE(
+ "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer "
+ "does not match metadata size (%zu) for the queue.",
+ meta_size, meta_size_);
+ return ErrorStatus(EINVAL);
+ }
+
+ if (slot == nullptr || acquire_fence == nullptr) {
+ ALOGE(
+ "ConsumerQueue::Dequeue: Invalid parameter: slot=%p meta=%p "
+ "acquire_fence=%p",
+ slot, meta, acquire_fence);
+ return ErrorStatus(EINVAL);
+ }
+
+ auto buffer_status =
+ BufferHubQueue::Dequeue(timeout, slot, meta, acquire_fence);
+ if (!buffer_status)
+ return buffer_status.error_status();
+
+ return {std::static_pointer_cast<BufferConsumer>(buffer_status.take())};
+}
+
+int ConsumerQueue::OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+ LocalHandle* acquire_fence) {
+ ALOGD_IF(TRACE, "ConsumerQueue::OnBufferReady: queue_id=%d buffer_id=%d",
+ id(), buf->id());
+ auto buffer = std::static_pointer_cast<BufferConsumer>(buf);
+ return buffer->Acquire(acquire_fence, meta_buffer_tmp_.get(), meta_size_);
+}
+
+Status<void> ConsumerQueue::OnBufferAllocated() {
+ auto status = ImportBuffers();
+ if (!status) {
+ ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s",
+ status.GetErrorMessage().c_str());
+ return ErrorStatus(status.error());
+ } else if (status.get() == 0) {
+ ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!");
+ return ErrorStatus(ENOBUFS);
+ } else {
+ ALOGD_IF(TRACE,
+ "ConsumerQueue::OnBufferAllocated: Imported %zu consumer buffers.",
+ status.get());
+ return {};
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
new file mode 100644
index 0000000000..8582bbf3a6
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -0,0 +1,643 @@
+#include "include/private/dvr/buffer_hub_queue_producer.h"
+
+#include <dvr/dvr_api.h>
+#include <inttypes.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+/* static */
+sp<BufferHubQueueProducer> BufferHubQueueProducer::Create() {
+ sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer;
+ producer->queue_ = ProducerQueue::Create<DvrNativeBufferMetadata>();
+ return producer;
+}
+
+/* static */
+sp<BufferHubQueueProducer> BufferHubQueueProducer::Create(
+ const std::shared_ptr<ProducerQueue>& queue) {
+ if (queue->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
+ ALOGE(
+ "BufferHubQueueProducer::Create producer's metadata size is different "
+ "than the size of DvrNativeBufferMetadata");
+ return nullptr;
+ }
+
+ sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer;
+ producer->queue_ = queue;
+ return producer;
+}
+
+status_t BufferHubQueueProducer::requestBuffer(int slot,
+ sp<GraphicBuffer>* buf) {
+ ALOGD_IF(TRACE, "requestBuffer: slot=%d", slot);
+
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (connected_api_ == kNoConnectedApi) {
+ ALOGE("requestBuffer: BufferHubQueueProducer has no connected producer");
+ return NO_INIT;
+ }
+
+ if (slot < 0 || slot >= max_buffer_count_) {
+ ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot,
+ max_buffer_count_);
+ return BAD_VALUE;
+ } else if (!buffers_[slot].mBufferState.isDequeued()) {
+ ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)",
+ slot, buffers_[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if (buffers_[slot].mGraphicBuffer != nullptr) {
+ ALOGE("requestBuffer: slot %d is not empty.", slot);
+ return BAD_VALUE;
+ } else if (buffers_[slot].mBufferProducer == nullptr) {
+ ALOGE("requestBuffer: slot %d is not dequeued.", slot);
+ return BAD_VALUE;
+ }
+
+ const auto& buffer_producer = buffers_[slot].mBufferProducer;
+ sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer();
+
+ buffers_[slot].mGraphicBuffer = graphic_buffer;
+ buffers_[slot].mRequestBufferCalled = true;
+
+ *buf = graphic_buffer;
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setMaxDequeuedBufferCount(
+ int max_dequeued_buffers) {
+ ALOGD_IF(TRACE, "setMaxDequeuedBufferCount: max_dequeued_buffers=%d",
+ max_dequeued_buffers);
+
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (max_dequeued_buffers <= 0 ||
+ max_dequeued_buffers >
+ static_cast<int>(BufferHubQueue::kMaxQueueCapacity -
+ kDefaultUndequeuedBuffers)) {
+ ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]",
+ max_dequeued_buffers, BufferHubQueue::kMaxQueueCapacity);
+ return BAD_VALUE;
+ }
+
+ // The new dequeued_buffers count should not be violated by the number
+ // of currently dequeued buffers.
+ int dequeued_count = 0;
+ for (const auto& buf : buffers_) {
+ if (buf.mBufferState.isDequeued()) {
+ dequeued_count++;
+ }
+ }
+ if (dequeued_count > max_dequeued_buffers) {
+ ALOGE(
+ "setMaxDequeuedBufferCount: the requested dequeued_buffers"
+ "count (%d) exceeds the current dequeued buffer count (%d)",
+ max_dequeued_buffers, dequeued_count);
+ return BAD_VALUE;
+ }
+
+ max_dequeued_buffer_count_ = max_dequeued_buffers;
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setAsyncMode(bool async) {
+ if (async) {
+ // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer
+ // automatically and behaves differently from IGraphicBufferConsumer. Thus,
+ // android::BufferQueue's async mode (a.k.a. allocating an additional buffer
+ // to prevent dequeueBuffer from being blocking) technically does not apply
+ // here.
+ //
+ // In Daydream, non-blocking producer side dequeue is guaranteed by careful
+ // buffer consumer implementations. In another word, BufferHubQueue based
+ // dequeueBuffer should never block whether setAsyncMode(true) is set or
+ // not.
+ //
+ // See: IGraphicBufferProducer::setAsyncMode and
+ // BufferQueueProducer::setAsyncMode for more about original implementation.
+ ALOGW(
+ "BufferHubQueueProducer::setAsyncMode: BufferHubQueue should always be "
+ "asynchronous. This call makes no effact.");
+ return NO_ERROR;
+ }
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::dequeueBuffer(
+ int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* /* out_timestamps */) {
+ ALOGD_IF(TRACE, "dequeueBuffer: w=%u, h=%u, format=%d, usage=%u", width,
+ height, format, usage);
+
+ status_t ret;
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (connected_api_ == kNoConnectedApi) {
+ ALOGE("dequeueBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
+
+ const uint32_t kLayerCount = 1;
+ if (static_cast<int32_t>(queue_->capacity()) <
+ max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) {
+ // Lazy allocation. When the capacity of |queue_| has not reached
+ // |max_dequeued_buffer_count_|, allocate new buffer.
+ // TODO(jwcai) To save memory, the really reasonable thing to do is to go
+ // over existing slots and find first existing one to dequeue.
+ ret = AllocateBuffer(width, height, kLayerCount, format, usage);
+ if (ret < 0)
+ return ret;
+ }
+
+ size_t slot;
+ std::shared_ptr<BufferProducer> buffer_producer;
+
+ for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
+ LocalHandle fence;
+ auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
+
+ buffer_producer = buffer_status.take();
+ if (!buffer_producer)
+ return NO_MEMORY;
+
+ if (width == buffer_producer->width() &&
+ height == buffer_producer->height() &&
+ static_cast<uint32_t>(format) == buffer_producer->format()) {
+ // The producer queue returns a buffer producer matches the request.
+ break;
+ }
+
+ // Needs reallocation.
+ // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
+ ALOGI(
+ "dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
+ "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
+ "re-allocattion.",
+ width, height, format, slot, buffer_producer->width(),
+ buffer_producer->height(), buffer_producer->format());
+ // Mark the slot as reallocating, so that later we can set
+ // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
+ buffers_[slot].mIsReallocating = true;
+
+ // Remove the old buffer once the allocation before allocating its
+ // replacement.
+ RemoveBuffer(slot);
+
+ // Allocate a new producer buffer with new buffer configs. Note that if
+ // there are already multiple buffers in the queue, the next one returned
+ // from |queue_->Dequeue| may not be the new buffer we just reallocated.
+ // Retry up to BufferHubQueue::kMaxQueueCapacity times.
+ ret = AllocateBuffer(width, height, kLayerCount, format, usage);
+ if (ret < 0)
+ return ret;
+ }
+
+ // With the BufferHub backed solution. Buffer slot returned from
+ // |queue_->Dequeue| is guaranteed to avaiable for producer's use.
+ // It's either in free state (if the buffer has never been used before) or
+ // in queued state (if the buffer has been dequeued and queued back to
+ // BufferHubQueue).
+ // TODO(jwcai) Clean this up, make mBufferState compatible with BufferHub's
+ // model.
+ LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() &&
+ !buffers_[slot].mBufferState.isQueued()),
+ "dequeueBuffer: slot %zu is not free or queued.", slot);
+
+ buffers_[slot].mBufferState.freeQueued();
+ buffers_[slot].mBufferState.dequeue();
+ ALOGD_IF(TRACE, "dequeueBuffer: slot=%zu", slot);
+
+ // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
+ // just need to exopose that through |BufferHubQueue| once we need fence.
+ *out_fence = Fence::NO_FENCE;
+ *out_slot = slot;
+ ret = NO_ERROR;
+
+ if (buffers_[slot].mIsReallocating) {
+ ret |= BUFFER_NEEDS_REALLOCATION;
+ buffers_[slot].mIsReallocating = false;
+ }
+
+ return ret;
+}
+
+status_t BufferHubQueueProducer::detachBuffer(int /* slot */) {
+ ALOGE("BufferHubQueueProducer::detachBuffer not implemented.");
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::detachNextBuffer(
+ sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */) {
+ ALOGE("BufferHubQueueProducer::detachNextBuffer not implemented.");
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::attachBuffer(
+ int* /* out_slot */, const sp<GraphicBuffer>& /* buffer */) {
+ // With this BufferHub backed implementation, we assume (for now) all buffers
+ // are allocated and owned by the BufferHub. Thus the attempt of transfering
+ // ownership of a buffer to the buffer queue is intentionally unsupported.
+ LOG_ALWAYS_FATAL("BufferHubQueueProducer::attachBuffer not supported.");
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::queueBuffer(int slot,
+ const QueueBufferInput& input,
+ QueueBufferOutput* output) {
+ ALOGD_IF(TRACE, "queueBuffer: slot %d", slot);
+
+ if (output == nullptr) {
+ return BAD_VALUE;
+ }
+
+ int64_t timestamp;
+ bool is_auto_timestamp;
+ android_dataspace dataspace;
+ Rect crop(Rect::EMPTY_RECT);
+ int scaling_mode;
+ uint32_t transform;
+ sp<Fence> fence;
+
+ input.deflate(&timestamp, &is_auto_timestamp, &dataspace, &crop,
+ &scaling_mode, &transform, &fence);
+
+ // Check input scaling mode is valid.
+ switch (scaling_mode) {
+ case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+ case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+ case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
+ break;
+ default:
+ ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode);
+ return BAD_VALUE;
+ }
+
+ // Check input fence is valid.
+ if (fence == nullptr) {
+ ALOGE("queueBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
+
+ status_t ret;
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (connected_api_ == kNoConnectedApi) {
+ ALOGE("queueBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
+
+ if (slot < 0 || slot >= max_buffer_count_) {
+ ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot,
+ max_buffer_count_);
+ return BAD_VALUE;
+ } else if (!buffers_[slot].mBufferState.isDequeued()) {
+ ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)",
+ slot, buffers_[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if ((!buffers_[slot].mRequestBufferCalled ||
+ buffers_[slot].mGraphicBuffer == nullptr)) {
+ ALOGE(
+ "queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, "
+ "mGraphicBuffer=%p)",
+ slot, buffers_[slot].mRequestBufferCalled,
+ buffers_[slot].mGraphicBuffer.get());
+ return BAD_VALUE;
+ }
+
+ // Post the buffer producer with timestamp in the metadata.
+ const auto& buffer_producer = buffers_[slot].mBufferProducer;
+
+ // Check input crop is not out of boundary of current buffer.
+ Rect buffer_rect(buffer_producer->width(), buffer_producer->height());
+ Rect cropped_rect(Rect::EMPTY_RECT);
+ crop.intersect(buffer_rect, &cropped_rect);
+ if (cropped_rect != crop) {
+ ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot);
+ return BAD_VALUE;
+ }
+
+ LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
+
+ DvrNativeBufferMetadata meta_data = {};
+ meta_data.timestamp = timestamp;
+ meta_data.is_auto_timestamp = static_cast<int32_t>(is_auto_timestamp);
+ meta_data.dataspace = static_cast<int32_t>(dataspace);
+ meta_data.crop_left = crop.left;
+ meta_data.crop_top = crop.top;
+ meta_data.crop_right = crop.right;
+ meta_data.crop_bottom = crop.bottom;
+ meta_data.scaling_mode = static_cast<int32_t>(scaling_mode);
+ meta_data.transform = static_cast<int32_t>(transform);
+
+ buffer_producer->Post(fence_fd, &meta_data, sizeof(meta_data));
+ buffers_[slot].mBufferState.queue();
+
+ output->width = buffer_producer->width();
+ output->height = buffer_producer->height();
+ output->transformHint = 0; // default value, we don't use it yet.
+
+ // |numPendingBuffers| counts of the number of buffers that has been enqueued
+ // by the producer but not yet acquired by the consumer. Due to the nature
+ // of BufferHubQueue design, this is hard to trace from the producer's client
+ // side, but it's safe to assume it's zero.
+ output->numPendingBuffers = 0;
+
+ // Note that we are not setting nextFrameNumber here as it seems to be only
+ // used by surface flinger. See more at b/22802885, ag/791760.
+ output->nextFrameNumber = 0;
+
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::cancelBuffer(int slot,
+ const sp<Fence>& fence) {
+ ALOGD_IF(TRACE, __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (connected_api_ == kNoConnectedApi) {
+ ALOGE("cancelBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
+
+ if (slot < 0 || slot >= max_buffer_count_) {
+ ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
+ max_buffer_count_);
+ return BAD_VALUE;
+ } else if (!buffers_[slot].mBufferState.isDequeued()) {
+ ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)",
+ slot, buffers_[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if (fence == nullptr) {
+ ALOGE("cancelBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
+
+ auto buffer_producer = buffers_[slot].mBufferProducer;
+ queue_->Enqueue(buffer_producer, slot);
+ buffers_[slot].mBufferState.cancel();
+ buffers_[slot].mFence = fence;
+ ALOGD_IF(TRACE, "cancelBuffer: slot %d", slot);
+
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::query(int what, int* out_value) {
+ ALOGD_IF(TRACE, __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (out_value == nullptr) {
+ ALOGE("query: out_value was NULL");
+ return BAD_VALUE;
+ }
+
+ int value = 0;
+ switch (what) {
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ // TODO(b/36187402) This should be the maximum number of buffers that this
+ // producer queue's consumer can acquire. Set to be at least one. Need to
+ // find a way to set from the consumer side.
+ value = kDefaultUndequeuedBuffers;
+ break;
+ case NATIVE_WINDOW_BUFFER_AGE:
+ value = 0;
+ break;
+ case NATIVE_WINDOW_WIDTH:
+ value = queue_->default_width();
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ value = queue_->default_height();
+ break;
+ case NATIVE_WINDOW_FORMAT:
+ value = queue_->default_format();
+ break;
+ case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+ // BufferHubQueue is always operating in async mode, thus semantically
+ // consumer can never be running behind. See BufferQueueCore.cpp core
+ // for more information about the original meaning of this flag.
+ value = 0;
+ break;
+ case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+ // TODO(jwcai) This is currently not implement as we don't need
+ // IGraphicBufferConsumer parity.
+ value = 0;
+ break;
+ case NATIVE_WINDOW_DEFAULT_DATASPACE:
+ // TODO(jwcai) Return the default value android::BufferQueue is using as
+ // there is no way dvr::ConsumerQueue can set it.
+ value = 0; // HAL_DATASPACE_UNKNOWN
+ break;
+ case NATIVE_WINDOW_STICKY_TRANSFORM:
+ // TODO(jwcai) Return the default value android::BufferQueue is using as
+ // there is no way dvr::ConsumerQueue can set it.
+ value = 0;
+ break;
+ case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
+ // In Daydream's implementation, the consumer end (i.e. VR Compostior)
+ // knows how to handle protected buffers.
+ value = 1;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+
+ ALOGD_IF(TRACE, "query: key=%d, v=%d", what, value);
+ *out_value = value;
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::connect(
+ const sp<IProducerListener>& /* listener */, int api,
+ bool /* producer_controlled_by_app */, QueueBufferOutput* output) {
+ // Consumer interaction are actually handled by buffer hub, and we need
+ // to maintain consumer operations here. We only need to perform basic input
+ // parameter checks here.
+ ALOGD_IF(TRACE, __FUNCTION__);
+
+ if (output == nullptr) {
+ return BAD_VALUE;
+ }
+
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (connected_api_ != kNoConnectedApi) {
+ return BAD_VALUE;
+ }
+
+ switch (api) {
+ case NATIVE_WINDOW_API_EGL:
+ case NATIVE_WINDOW_API_CPU:
+ case NATIVE_WINDOW_API_MEDIA:
+ case NATIVE_WINDOW_API_CAMERA:
+ connected_api_ = api;
+
+ output->width = queue_->default_width();
+ output->height = queue_->default_height();
+
+ // default values, we don't use them yet.
+ output->transformHint = 0;
+ output->numPendingBuffers = 0;
+ output->nextFrameNumber = 0;
+ output->bufferReplaced = false;
+
+ break;
+ default:
+ ALOGE("BufferHubQueueProducer::connect: unknow API %d", api);
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::disconnect(int api, DisconnectMode /*mode*/) {
+ // Consumer interaction are actually handled by buffer hub, and we need
+ // to maintain consumer operations here. We only need to perform basic input
+ // parameter checks here.
+ ALOGD_IF(TRACE, __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mutex_);
+
+ if (kNoConnectedApi == connected_api_) {
+ return NO_INIT;
+ } else if (api != connected_api_) {
+ return BAD_VALUE;
+ }
+
+ connected_api_ = kNoConnectedApi;
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setSidebandStream(
+ const sp<NativeHandle>& stream) {
+ if (stream != nullptr) {
+ // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
+ // metadata.
+ ALOGE("SidebandStream is not currently supported.");
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
+void BufferHubQueueProducer::allocateBuffers(uint32_t /* width */,
+ uint32_t /* height */,
+ PixelFormat /* format */,
+ uint32_t /* usage */) {
+ // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
+ // of buffers permitted by the current BufferQueue configuration (aka
+ // |max_buffer_count_|).
+ ALOGE("BufferHubQueueProducer::allocateBuffers not implemented.");
+}
+
+status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) {
+ ALOGE("BufferHubQueueProducer::allowAllocation not implemented.");
+ return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setGenerationNumber(
+ uint32_t generation_number) {
+ ALOGD_IF(TRACE, __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mutex_);
+ generation_number_ = generation_number;
+ return NO_ERROR;
+}
+
+String8 BufferHubQueueProducer::getConsumerName() const {
+ // BufferHub based implementation could have one to many producer/consumer
+ // relationship, thus |getConsumerName| from the producer side does not
+ // make any sense.
+ ALOGE("BufferHubQueueProducer::getConsumerName not supported.");
+ return String8("BufferHubQueue::DummyConsumer");
+}
+
+status_t BufferHubQueueProducer::setSharedBufferMode(bool shared_buffer_mode) {
+ if (shared_buffer_mode) {
+ ALOGE(
+ "BufferHubQueueProducer::setSharedBufferMode(true) is not supported.");
+ // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow.
+ return INVALID_OPERATION;
+ }
+ // Setting to default should just work as a no-op.
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setAutoRefresh(bool auto_refresh) {
+ if (auto_refresh) {
+ ALOGE("BufferHubQueueProducer::setAutoRefresh(true) is not supported.");
+ return INVALID_OPERATION;
+ }
+ // Setting to default should just work as a no-op.
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) {
+ ALOGD_IF(TRACE, __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mutex_);
+ dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::getLastQueuedBuffer(
+ sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */,
+ float /*out_transform_matrix*/[16]) {
+ ALOGE("BufferHubQueueProducer::getLastQueuedBuffer not implemented.");
+ return INVALID_OPERATION;
+}
+
+void BufferHubQueueProducer::getFrameTimestamps(
+ FrameEventHistoryDelta* /*outDelta*/) {
+ ALOGE("BufferHubQueueProducer::getFrameTimestamps not implemented.");
+}
+
+status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const {
+ ALOGD_IF(TRACE, __FUNCTION__);
+
+ *out_id = unique_id_;
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::AllocateBuffer(uint32_t width, uint32_t height,
+ uint32_t layer_count,
+ PixelFormat format,
+ uint64_t usage) {
+ size_t slot;
+
+ if (queue_->AllocateBuffer(width, height, layer_count, format, usage, &slot) <
+ 0) {
+ ALOGE("Failed to allocate new buffer in BufferHub.");
+ return NO_MEMORY;
+ }
+
+ auto buffer_producer = queue_->GetBuffer(slot);
+
+ LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr,
+ "Failed to get buffer producer at slot: %zu", slot);
+
+ buffers_[slot].mBufferProducer = buffer_producer;
+
+ return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::RemoveBuffer(size_t slot) {
+ int ret = queue_->DetachBuffer(slot);
+ if (ret < 0) {
+ ALOGE("BufferHubQueueProducer::RemoveBuffer failed through RPC, ret=%s",
+ strerror(-ret));
+ return ret;
+ }
+
+ // Reset in memory objects related the the buffer.
+ buffers_[slot].mBufferProducer = nullptr;
+ buffers_[slot].mGraphicBuffer = nullptr;
+ buffers_[slot].mBufferState.detachProducer();
+ return NO_ERROR;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
new file mode 100644
index 0000000000..ed67f79951
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -0,0 +1,456 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
+
+#include <gui/BufferQueueDefs.h>
+
+#include <pdx/client.h>
+#include <pdx/status.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/epoll_file_descriptor.h>
+#include <private/dvr/ring_buffer.h>
+
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+class ConsumerQueue;
+
+// |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are
+// automatically re-requeued when released by the remote side.
+class BufferHubQueue : public pdx::Client {
+ public:
+ using LocalHandle = pdx::LocalHandle;
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ template <typename T>
+ using Status = pdx::Status<T>;
+
+ virtual ~BufferHubQueue() {}
+ void Initialize();
+
+ // Create a new consumer queue that is attached to the producer. Returns
+ // a new consumer queue client or nullptr on failure.
+ std::unique_ptr<ConsumerQueue> CreateConsumerQueue();
+
+ // Create a new consumer queue that is attached to the producer. This queue
+ // sets each of its imported consumer buffers to the ignored state to avoid
+ // participation in lifecycle events.
+ std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue();
+
+ // Return the default buffer width of this buffer queue.
+ size_t default_width() const { return default_width_; }
+
+ // Return the default buffer height of this buffer queue.
+ size_t default_height() const { return default_height_; }
+
+ // Return the default buffer format of this buffer queue.
+ int32_t default_format() const { return default_format_; }
+
+ // Create a new consumer in handle form for immediate transport over RPC.
+ Status<LocalChannelHandle> CreateConsumerQueueHandle();
+
+ // Return the number of buffers avaiable for dequeue.
+ size_t count() const { return available_buffers_.GetSize(); }
+
+ // Return the total number of buffers that the queue is tracking.
+ size_t capacity() const { return capacity_; }
+
+ // Return the size of metadata structure associated with this BufferBubQueue.
+ size_t metadata_size() const { return meta_size_; }
+
+ // Return whether the buffer queue is alrady full.
+ bool is_full() const { return available_buffers_.IsFull(); }
+
+ explicit operator bool() const { return epoll_fd_.IsValid(); }
+
+ std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const {
+ return buffers_[slot];
+ }
+
+ Status<int> GetEventMask(int events) {
+ if (auto* client_channel = GetChannel()) {
+ return client_channel->GetEventMask(events);
+ } else {
+ return pdx::ErrorStatus(EINVAL);
+ }
+ }
+
+ // Returns an fd that signals pending queue events using
+ // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be
+ // called to handle pending queue events.
+ int queue_fd() const { return epoll_fd_.Get(); }
+
+ // Handles any pending events, returning available buffers to the queue and
+ // reaping disconnected buffers. Returns true if successful, false if an error
+ // occurred.
+ bool HandleQueueEvents() { return WaitForBuffers(0); }
+
+ // Enqueue a buffer marks buffer to be available (|Gain|'ed for producer
+ // and |Acquire|'ed for consumer. This is only used for internal bookkeeping.
+ void Enqueue(const std::shared_ptr<BufferHubBuffer>& buf, size_t slot);
+
+ // |BufferHubQueue| will keep track of at most this value of buffers.
+ static constexpr size_t kMaxQueueCapacity =
+ android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+ // Special epoll data field indicating that the epoll event refers to the
+ // queue.
+ static constexpr int64_t kEpollQueueEventIndex = -1;
+
+ // When pass |kNoTimeout| to |Dequeue|, it will block indefinitely without a
+ // timeout.
+ static constexpr int kNoTimeOut = -1;
+
+ int id() const { return id_; }
+ bool hung_up() const { return hung_up_; }
+
+ protected:
+ BufferHubQueue(LocalChannelHandle channel);
+ BufferHubQueue(const std::string& endpoint_path);
+
+ // Imports the queue parameters by querying BufferHub for the parameters for
+ // this channel.
+ Status<void> ImportQueue();
+
+ // Sets up the queue with the given parameters.
+ void SetupQueue(size_t meta_size_bytes_, int id);
+
+ // Called by ProducerQueue::AddBuffer and ConsumerQueue::AddBuffer only. to
+ // register a buffer for epoll and internal bookkeeping.
+ int AddBuffer(const std::shared_ptr<BufferHubBuffer>& buf, size_t slot);
+
+ // Called by ProducerQueue::DetachBuffer and ConsumerQueue::DetachBuffer only.
+ // to deregister a buffer for epoll and internal bookkeeping.
+ virtual int DetachBuffer(size_t slot);
+
+ // Dequeue a buffer from the free queue, blocking until one is available. The
+ // timeout argument specifies the number of milliseconds that |Dequeue()| will
+ // block. Specifying a timeout of -1 causes |Dequeue()| to block indefinitely,
+ // while specifying a timeout equal to zero cause |Dequeue()| to return
+ // immediately, even if no buffers are available.
+ pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(int timeout,
+ size_t* slot,
+ void* meta,
+ LocalHandle* fence);
+
+ // Wait for buffers to be released and re-add them to the queue.
+ bool WaitForBuffers(int timeout);
+ void HandleBufferEvent(size_t slot, int poll_events);
+ void HandleQueueEvent(int poll_events);
+
+ virtual int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+ LocalHandle* fence) = 0;
+
+ // Called when a buffer is allocated remotely.
+ virtual Status<void> OnBufferAllocated() { return {}; }
+
+ // Data members to handle arbitrary metadata passed through BufferHub. It is
+ // fair to enforce that all buffers in the same queue share the same metadata
+ // type. |meta_size_| is used to store the size of metadata on queue creation;
+ // and |meta_buffer_tmp_| is allocated and resized to |meta_size_| on queue
+ // creation to be later used as temporary space so that we can avoid
+ // additional dynamic memory allocation in each |Enqueue| and |Dequeue| call.
+ size_t meta_size_;
+
+ // Here we intentionally choose |unique_ptr<uint8_t[]>| over vector<uint8_t>
+ // to disallow dynamic resizing for stability reasons.
+ std::unique_ptr<uint8_t[]> meta_buffer_tmp_;
+
+ private:
+ static constexpr size_t kMaxEvents = 128;
+
+ // The |u64| data field of an epoll event is interpreted as int64_t:
+ // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific
+ // element of |buffers_| as a direct index;
+ static bool is_buffer_event_index(int64_t index) {
+ return index >= 0 &&
+ index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity);
+ }
+
+ // When |index| == kEpollQueueEventIndex, it refers to the queue itself.
+ static bool is_queue_event_index(int64_t index) {
+ return index == BufferHubQueue::kEpollQueueEventIndex;
+ }
+
+ struct BufferInfo {
+ // A logical slot number that is assigned to a buffer at allocation time.
+ // The slot number remains unchanged during the entire life cycle of the
+ // buffer and should not be confused with the enqueue and dequeue order.
+ size_t slot;
+
+ // A BufferHubBuffer client.
+ std::shared_ptr<BufferHubBuffer> buffer;
+
+ // Metadata associated with the buffer.
+ std::unique_ptr<uint8_t[]> metadata;
+
+ BufferInfo() : BufferInfo(-1, 0) {}
+
+ BufferInfo(size_t slot, size_t metadata_size)
+ : slot(slot),
+ buffer(nullptr),
+ metadata(metadata_size ? new uint8_t[metadata_size] : nullptr) {}
+
+ BufferInfo(BufferInfo&& other)
+ : slot(other.slot),
+ buffer(std::move(other.buffer)),
+ metadata(std::move(other.metadata)) {}
+
+ BufferInfo& operator=(BufferInfo&& other) {
+ slot = other.slot;
+ buffer = std::move(other.buffer);
+ metadata = std::move(other.metadata);
+ return *this;
+ }
+
+ private:
+ BufferInfo(const BufferInfo&) = delete;
+ void operator=(BufferInfo&) = delete;
+ };
+
+ // Default buffer width that can be set to override the buffer width when a
+ // width and height of 0 are specified in AllocateBuffer.
+ size_t default_width_{1};
+
+ // Default buffer height that can be set to override the buffer height when a
+ // width and height of 0 are specified in AllocateBuffer.
+ size_t default_height_{1};
+
+ // Default buffer format that can be set to override the buffer format when it
+ // isn't specified in AllocateBuffer.
+ int32_t default_format_{PIXEL_FORMAT_RGBA_8888};
+
+ // Buffer queue:
+ // |buffers_| tracks all |BufferHubBuffer|s created by this |BufferHubQueue|.
+ std::vector<std::shared_ptr<BufferHubBuffer>> buffers_;
+
+ // |epollhup_pending_| tracks whether a slot of |buffers_| get detached before
+ // its corresponding EPOLLHUP event got handled. This could happen as the
+ // following sequence:
+ // 1. Producer queue's client side allocates a new buffer (at slot 1).
+ // 2. Producer queue's client side replaces an existing buffer (at slot 0).
+ // This is implemented by first detaching the buffer and then allocating a
+ // new buffer.
+ // 3. During the same epoll_wait, Consumer queue's client side gets EPOLLIN
+ // event on the queue which indicates a new buffer is available and the
+ // EPOLLHUP event for slot 0. Consumer handles these two events in order.
+ // 4. Consumer client calls BufferHubRPC::ConsumerQueueImportBuffers and both
+ // slot 0 and (the new) slot 1 buffer will be imported. During the import
+ // of the buffer at slot 1, consumer client detaches the old buffer so that
+ // the new buffer can be registered. At the same time
+ // |epollhup_pending_[slot]| is marked to indicate that buffer at this slot
+ // was detached prior to EPOLLHUP event.
+ // 5. Consumer client continues to handle the EPOLLHUP. Since
+ // |epollhup_pending_[slot]| is marked as true, it can safely ignore the
+ // event without detaching the newly allocated buffer at slot 1.
+ //
+ // In normal situations where the previously described sequence doesn't
+ // happen, an EPOLLHUP event should trigger a regular buffer detach.
+ std::vector<bool> epollhup_pending_;
+
+ // |available_buffers_| uses |dvr::RingBuffer| to implementation queue
+ // sematics. When |Dequeue|, we pop the front element from
+ // |available_buffers_|, and that buffer's reference count will decrease by
+ // one, while another reference in |buffers_| keeps the last reference to
+ // prevent the buffer from being deleted.
+ RingBuffer<BufferInfo> available_buffers_;
+
+ // Fences (acquire fence for consumer and release fence for consumer) , one
+ // for each buffer slot.
+ std::vector<LocalHandle> fences_;
+
+ // Keep track with how many buffers have been added into the queue.
+ size_t capacity_;
+
+ // Epoll fd used to wait for BufferHub events.
+ EpollFileDescriptor epoll_fd_;
+
+ // Flag indicating that the other side hung up. For ProducerQueues this
+ // triggers when BufferHub dies or explicitly closes the queue channel. For
+ // ConsumerQueues this can either mean the same or that the ProducerQueue on
+ // the other end hung up.
+ bool hung_up_{false};
+
+ // Global id for the queue that is consistent across processes.
+ int id_;
+
+ BufferHubQueue(const BufferHubQueue&) = delete;
+ void operator=(BufferHubQueue&) = delete;
+};
+
+class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> {
+ public:
+ template <typename Meta>
+ static std::unique_ptr<ProducerQueue> Create() {
+ return BASE::Create(sizeof(Meta));
+ }
+ static std::unique_ptr<ProducerQueue> Create(size_t meta_size_bytes) {
+ return BASE::Create(meta_size_bytes);
+ }
+
+ // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits
+ // in |usage_clear_mask| will be automatically masked off. Note that
+ // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but
+ // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer
+ // allocation through this producer queue shall not have any of the usage bits
+ // in |usage_deny_set_mask| set. Allocation calls violating this will be
+ // rejected. All buffer allocation through this producer queue must have all
+ // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating
+ // this will be rejected. Note that |usage_deny_set_mask| and
+ // |usage_deny_clear_mask| shall not conflict with each other. Such
+ // configuration will be treated as invalid input on creation.
+ template <typename Meta>
+ static std::unique_ptr<ProducerQueue> Create(uint32_t usage_set_mask,
+ uint32_t usage_clear_mask,
+ uint32_t usage_deny_set_mask,
+ uint32_t usage_deny_clear_mask) {
+ return BASE::Create(sizeof(Meta), usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask);
+ }
+ static std::unique_ptr<ProducerQueue> Create(size_t meta_size_bytes,
+ uint32_t usage_set_mask,
+ uint32_t usage_clear_mask,
+ uint32_t usage_deny_set_mask,
+ uint32_t usage_deny_clear_mask) {
+ return BASE::Create(meta_size_bytes, usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask);
+ }
+
+ // Import a |ProducerQueue| from a channel handle.
+ static std::unique_ptr<ProducerQueue> Import(LocalChannelHandle handle) {
+ return BASE::Create(std::move(handle));
+ }
+
+ // Get a buffer producer. Note that the method doesn't check whether the
+ // buffer slot has a valid buffer that has been allocated already. When no
+ // buffer has been imported before it returns |nullptr|; otherwise it returns
+ // a shared pointer to a |BufferProducer|.
+ std::shared_ptr<BufferProducer> GetBuffer(size_t slot) const {
+ return std::static_pointer_cast<BufferProducer>(
+ BufferHubQueue::GetBuffer(slot));
+ }
+
+ // Allocate producer buffer to populate the queue. Once allocated, a producer
+ // buffer is automatically enqueue'd into the ProducerQueue and available to
+ // use (i.e. in |Gain|'ed mode).
+ // Returns Zero on success and negative error code when buffer allocation
+ // fails.
+ int AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
+ uint32_t format, uint64_t usage, size_t* out_slot);
+
+ // Add a producer buffer to populate the queue. Once added, a producer buffer
+ // is available to use (i.e. in |Gain|'ed mode).
+ int AddBuffer(const std::shared_ptr<BufferProducer>& buf, size_t slot);
+
+ // Detach producer buffer from the queue.
+ // Returns Zero on success and negative error code when buffer detach
+ // fails.
+ int DetachBuffer(size_t slot) override;
+
+ // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
+ // and caller should call Post() once it's done writing to release the buffer
+ // to the consumer side.
+ pdx::Status<std::shared_ptr<BufferProducer>> Dequeue(
+ int timeout, size_t* slot, LocalHandle* release_fence);
+
+ private:
+ friend BASE;
+
+ // Constructors are automatically exposed through ProducerQueue::Create(...)
+ // static template methods inherited from ClientBase, which take the same
+ // arguments as the constructors.
+ explicit ProducerQueue(size_t meta_size);
+ ProducerQueue(LocalChannelHandle handle);
+ ProducerQueue(size_t meta_size, uint64_t usage_set_mask,
+ uint64_t usage_clear_mask, uint64_t usage_deny_set_mask,
+ uint64_t usage_deny_clear_mask);
+
+ int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+ LocalHandle* release_fence) override;
+};
+
+// Explicit specializations of ProducerQueue::Create for void metadata type.
+template <>
+inline std::unique_ptr<ProducerQueue> ProducerQueue::Create<void>() {
+ return ProducerQueue::Create(0);
+}
+template <>
+inline std::unique_ptr<ProducerQueue> ProducerQueue::Create<void>(
+ uint32_t usage_set_mask, uint32_t usage_clear_mask,
+ uint32_t usage_deny_set_mask, uint32_t usage_deny_clear_mask) {
+ return ProducerQueue::Create(0, usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask);
+}
+
+class ConsumerQueue : public BufferHubQueue {
+ public:
+ // Get a buffer consumer. Note that the method doesn't check whether the
+ // buffer slot has a valid buffer that has been imported already. When no
+ // buffer has been imported before it returns nullptr; otherwise returns a
+ // shared pointer to a BufferConsumer.
+ std::shared_ptr<BufferConsumer> GetBuffer(size_t slot) const {
+ return std::static_pointer_cast<BufferConsumer>(
+ BufferHubQueue::GetBuffer(slot));
+ }
+
+ // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls
+ // whether or not buffers are set to be ignored when imported. This may be
+ // used to avoid participation in the buffer lifecycle by a consumer queue
+ // that is only used to spawn other consumer queues, such as in an
+ // intermediate service.
+ static std::unique_ptr<ConsumerQueue> Import(LocalChannelHandle handle,
+ bool ignore_on_import = false) {
+ return std::unique_ptr<ConsumerQueue>(
+ new ConsumerQueue(std::move(handle), ignore_on_import));
+ }
+
+ // Import newly created buffers from the service side.
+ // Returns number of buffers successfully imported or an error.
+ Status<size_t> ImportBuffers();
+
+ // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
+ // mode, and caller should call Releasse() once it's done writing to release
+ // the buffer to the producer side. |meta| is passed along from BufferHub,
+ // The user of BufferProducer is responsible with making sure that the
+ // Dequeue() is done with the corect metadata type and size with those used
+ // when the buffer is orignally created.
+ template <typename Meta>
+ pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+ int timeout, size_t* slot, Meta* meta, LocalHandle* acquire_fence) {
+ return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
+ }
+ pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+ int timeout, size_t* slot, LocalHandle* acquire_fence) {
+ return Dequeue(timeout, slot, nullptr, 0, acquire_fence);
+ }
+
+ pdx::Status<std::shared_ptr<BufferConsumer>> Dequeue(
+ int timeout, size_t* slot, void* meta, size_t meta_size,
+ LocalHandle* acquire_fence);
+
+ private:
+ friend BufferHubQueue;
+
+ ConsumerQueue(LocalChannelHandle handle, bool ignore_on_import = false);
+
+ // Add a consumer buffer to populate the queue. Once added, a consumer buffer
+ // is NOT available to use until the producer side |Post| it. |WaitForBuffers|
+ // will catch the |Post| and |Acquire| the buffer to make it available for
+ // consumer.
+ int AddBuffer(const std::shared_ptr<BufferConsumer>& buf, size_t slot);
+
+ int OnBufferReady(const std::shared_ptr<BufferHubBuffer>& buf,
+ LocalHandle* acquire_fence) override;
+
+ Status<void> OnBufferAllocated() override;
+
+ // Flag indicating that imported (consumer) buffers should be ignored when
+ // imported to avoid participating in the buffer ownership flow.
+ bool ignore_on_import_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
new file mode 100644
index 0000000000..7890176f04
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
@@ -0,0 +1,183 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubQueueProducer : public BnGraphicBufferProducer {
+ public:
+ static constexpr int kNoConnectedApi = -1;
+
+ // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer
+ // side logic doesn't limit the number of buffer it can acquire
+ // simultaneously. We need a way for consumer logic to configure and enforce
+ // that.
+ static constexpr int kDefaultUndequeuedBuffers = 1;
+
+ // Create a BufferHubQueueProducer instance by creating a new producer queue.
+ static sp<BufferHubQueueProducer> Create();
+
+ // Create a BufferHubQueueProducer instance by importing an existing prodcuer
+ // queue.
+ static sp<BufferHubQueueProducer> Create(
+ const std::shared_ptr<ProducerQueue>& producer);
+
+ // See |IGraphicBufferProducer::requestBuffer|
+ status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
+
+ // For the BufferHub based implementation. All buffers in the queue are
+ // allowed to be dequeued from the consumer side. It call always returns
+ // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting
+ // |max_dequeued_buffers| here can be considered the same as setting queue
+ // capacity.
+ //
+ // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info
+ status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override;
+
+ // See |IGraphicBufferProducer::setAsyncMode|
+ status_t setAsyncMode(bool async) override;
+
+ // See |IGraphicBufferProducer::dequeueBuffer|
+ status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
+ uint32_t height, PixelFormat format, uint32_t usage,
+ FrameEventHistoryDelta* outTimestamps) override;
+
+ // See |IGraphicBufferProducer::detachBuffer|
+ status_t detachBuffer(int slot) override;
+
+ // See |IGraphicBufferProducer::detachNextBuffer|
+ status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer,
+ sp<Fence>* out_fence) override;
+
+ // See |IGraphicBufferProducer::attachBuffer|
+ status_t attachBuffer(int* out_slot,
+ const sp<GraphicBuffer>& buffer) override;
+
+ // See |IGraphicBufferProducer::queueBuffer|
+ status_t queueBuffer(int slot, const QueueBufferInput& input,
+ QueueBufferOutput* output) override;
+
+ // See |IGraphicBufferProducer::cancelBuffer|
+ status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
+
+ // See |IGraphicBufferProducer::query|
+ status_t query(int what, int* out_value) override;
+
+ // See |IGraphicBufferProducer::connect|
+ status_t connect(const sp<IProducerListener>& listener, int api,
+ bool producer_controlled_by_app,
+ QueueBufferOutput* output) override;
+
+ // See |IGraphicBufferProducer::disconnect|
+ status_t disconnect(int api,
+ DisconnectMode mode = DisconnectMode::Api) override;
+
+ // See |IGraphicBufferProducer::setSidebandStream|
+ status_t setSidebandStream(const sp<NativeHandle>& stream) override;
+
+ // See |IGraphicBufferProducer::allocateBuffers|
+ void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t usage) override;
+
+ // See |IGraphicBufferProducer::allowAllocation|
+ status_t allowAllocation(bool allow) override;
+
+ // See |IGraphicBufferProducer::setGenerationNumber|
+ status_t setGenerationNumber(uint32_t generation_number) override;
+
+ // See |IGraphicBufferProducer::getConsumerName|
+ String8 getConsumerName() const override;
+
+ // See |IGraphicBufferProducer::setSharedBufferMode|
+ status_t setSharedBufferMode(bool shared_buffer_mode) override;
+
+ // See |IGraphicBufferProducer::setAutoRefresh|
+ status_t setAutoRefresh(bool auto_refresh) override;
+
+ // See |IGraphicBufferProducer::setDequeueTimeout|
+ status_t setDequeueTimeout(nsecs_t timeout) override;
+
+ // See |IGraphicBufferProducer::getLastQueuedBuffer|
+ status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer,
+ sp<Fence>* out_fence,
+ float out_transform_matrix[16]) override;
+
+ // See |IGraphicBufferProducer::getFrameTimestamps|
+ void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override;
+
+ // See |IGraphicBufferProducer::getUniqueId|
+ status_t getUniqueId(uint64_t* out_id) const override;
+
+ private:
+ using LocalHandle = pdx::LocalHandle;
+
+ // Private constructor to force use of |Create|.
+ BufferHubQueueProducer() {}
+
+ static uint64_t genUniqueId() {
+ static std::atomic<uint32_t> counter{0};
+ static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+ return id | counter++;
+ }
+
+ // Allocate new buffer through BufferHub and add it into |queue_| for
+ // bookkeeping.
+ status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
+ PixelFormat format, uint64_t usage);
+
+ // Remove a buffer via BufferHubRPC.
+ status_t RemoveBuffer(size_t slot);
+
+ // Concreate implementation backed by BufferHubBuffer.
+ std::shared_ptr<ProducerQueue> queue_;
+
+ // Mutex for thread safety.
+ std::mutex mutex_;
+
+ // Connect client API, should be one of the NATIVE_WINDOW_API_* flags.
+ int connected_api_{kNoConnectedApi};
+
+ // |max_buffer_count_| sets the capacity of the underlying buffer queue.
+ int32_t max_buffer_count_{BufferHubQueue::kMaxQueueCapacity};
+
+ // |max_dequeued_buffer_count_| set the maximum number of buffers that can
+ // be dequeued at the same momment.
+ int32_t max_dequeued_buffer_count_{1};
+
+ // Sets how long dequeueBuffer or attachBuffer will block if a buffer or
+ // slot is not yet available. The timeout is stored in milliseconds.
+ int dequeue_timeout_ms_{BufferHubQueue::kNoTimeOut};
+
+ // |generation_number_| stores the current generation number of the attached
+ // producer. Any attempt to attach a buffer with a different generation
+ // number will fail.
+ // TOOD(b/38137191) Currently not used as we don't support
+ // IGraphicBufferProducer::detachBuffer.
+ uint32_t generation_number_{0};
+
+ // |buffers_| stores the buffers that have been dequeued from
+ // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets
+ // filled in with the result of |Dequeue|.
+ // TODO(jwcai) The buffer allocated to a slot will also be replaced if the
+ // requested buffer usage or geometry differs from that of the buffer
+ // allocated to a slot.
+ struct BufferHubSlot : public BufferSlot {
+ BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {}
+ // BufferSlot comes from android framework, using m prefix to comply with
+ // the name convention with the reset of data fields from BufferSlot.
+ std::shared_ptr<BufferProducer> mBufferProducer;
+ bool mIsReallocating;
+ };
+ BufferHubSlot buffers_[BufferHubQueue::kMaxQueueCapacity];
+
+ // A uniqueId used by IGraphicBufferProducer interface.
+ const uint64_t unique_id_{genUniqueId()};
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
new file mode 100644
index 0000000000..865573cafd
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -0,0 +1,48 @@
+
+
+shared_libraries = [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libgui",
+ "liblog",
+ "libhardware",
+ "libui",
+ "libutils",
+]
+
+static_libraries = [
+ "libbufferhubqueue",
+ "libbufferhub",
+ "libchrome",
+ "libdvrcommon",
+ "libpdx_default_transport",
+]
+
+cc_test {
+ srcs: ["buffer_hub_queue-test.cpp"],
+ static_libs: static_libraries,
+ shared_libs: shared_libraries,
+ cflags: [
+ "-DLOG_TAG=\"buffer_hub_queue-test\"",
+ "-DTRACE=0",
+ "-O0",
+ "-g",
+ ],
+ name: "buffer_hub_queue-test",
+ tags: ["optional"],
+}
+
+cc_test {
+ srcs: ["buffer_hub_queue_producer-test.cpp"],
+ static_libs: static_libraries,
+ shared_libs: shared_libraries,
+ cflags: [
+ "-DLOG_TAG=\"buffer_hub_queue_producer-test\"",
+ "-DTRACE=0",
+ "-O0",
+ "-g",
+ ],
+ name: "buffer_hub_queue_producer-test",
+ tags: ["optional"],
+}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
new file mode 100644
index 0000000000..fe0b12aa44
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -0,0 +1,421 @@
+#include <base/logging.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+using pdx::LocalHandle;
+
+namespace {
+
+constexpr int kBufferWidth = 100;
+constexpr int kBufferHeight = 1;
+constexpr int kBufferLayerCount = 1;
+constexpr int kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
+
+class BufferHubQueueTest : public ::testing::Test {
+ public:
+ template <typename Meta>
+ bool CreateProducerQueue(uint64_t usage_set_mask = 0,
+ uint64_t usage_clear_mask = 0,
+ uint64_t usage_deny_set_mask = 0,
+ uint64_t usage_deny_clear_mask = 0) {
+ producer_queue_ =
+ ProducerQueue::Create<Meta>(usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask, usage_deny_clear_mask);
+ return producer_queue_ != nullptr;
+ }
+
+ bool CreateConsumerQueue() {
+ if (producer_queue_) {
+ consumer_queue_ = producer_queue_->CreateConsumerQueue();
+ return consumer_queue_ != nullptr;
+ } else {
+ return false;
+ }
+ }
+
+ template <typename Meta>
+ bool CreateQueues(int usage_set_mask = 0, int usage_clear_mask = 0,
+ int usage_deny_set_mask = 0,
+ int usage_deny_clear_mask = 0) {
+ return CreateProducerQueue<Meta>(usage_set_mask, usage_clear_mask,
+ usage_deny_set_mask,
+ usage_deny_clear_mask) &&
+ CreateConsumerQueue();
+ }
+
+ void AllocateBuffer() {
+ // Create producer buffer.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+ kBufferLayerCount, kBufferFormat,
+ kBufferUsage, &slot);
+ ASSERT_EQ(ret, 0);
+ }
+
+ protected:
+ std::unique_ptr<ProducerQueue> producer_queue_;
+ std::unique_ptr<ConsumerQueue> consumer_queue_;
+};
+
+TEST_F(BufferHubQueueTest, TestDequeue) {
+ const size_t nb_dequeue_times = 16;
+
+ ASSERT_TRUE(CreateQueues<size_t>());
+
+ // Allocate only one buffer.
+ AllocateBuffer();
+
+ // But dequeue multiple times.
+ for (size_t i = 0; i < nb_dequeue_times; i++) {
+ size_t slot;
+ LocalHandle fence;
+ auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(nullptr, p1);
+ size_t mi = i;
+ ASSERT_EQ(p1->Post(LocalHandle(), &mi, sizeof(mi)), 0);
+ size_t mo;
+ auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence);
+ ASSERT_TRUE(c1_status.ok());
+ auto c1 = c1_status.take();
+ ASSERT_NE(nullptr, c1);
+ ASSERT_EQ(mi, mo);
+ c1->Release(LocalHandle());
+ }
+}
+
+TEST_F(BufferHubQueueTest, TestProducerConsumer) {
+ const size_t nb_buffer = 16;
+ size_t slot;
+ uint64_t seq;
+
+ ASSERT_TRUE(CreateQueues<uint64_t>());
+
+ for (size_t i = 0; i < nb_buffer; i++) {
+ AllocateBuffer();
+
+ // Producer queue has all the available buffers on initialize.
+ ASSERT_EQ(producer_queue_->count(), i + 1);
+ ASSERT_EQ(producer_queue_->capacity(), i + 1);
+
+ // Consumer queue has no avaiable buffer on initialize.
+ ASSERT_EQ(consumer_queue_->count(), 0U);
+ // Consumer queue does not import buffers until a dequeue is issued.
+ ASSERT_EQ(consumer_queue_->capacity(), i);
+ // Dequeue returns timeout since no buffer is ready to consumer, but
+ // this implicitly triggers buffer import and bump up |capacity|.
+ LocalHandle fence;
+ auto status = consumer_queue_->Dequeue(0, &slot, &seq, &fence);
+ ASSERT_FALSE(status.ok());
+ ASSERT_EQ(ETIMEDOUT, status.error());
+ ASSERT_EQ(consumer_queue_->capacity(), i + 1);
+ }
+
+ for (size_t i = 0; i < nb_buffer; i++) {
+ LocalHandle fence;
+ // First time, there is no buffer available to dequeue.
+ auto consumer_status = consumer_queue_->Dequeue(0, &slot, &seq, &fence);
+ ASSERT_FALSE(consumer_status.ok());
+ ASSERT_EQ(ETIMEDOUT, consumer_status.error());
+
+ // Make sure Producer buffer is Post()'ed so that it's ready to Accquire
+ // in the consumer's Dequeue() function.
+ auto producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(producer_status.ok());
+ auto producer = producer_status.take();
+ ASSERT_NE(nullptr, producer);
+
+ uint64_t seq_in = static_cast<uint64_t>(i);
+ ASSERT_EQ(producer->Post({}, &seq_in, sizeof(seq_in)), 0);
+
+ // Second time, the just |Post()|'ed buffer should be dequeued.
+ uint64_t seq_out = 0;
+ consumer_status = consumer_queue_->Dequeue(0, &slot, &seq_out, &fence);
+ ASSERT_TRUE(consumer_status.ok());
+ auto consumer = consumer_status.take();
+ ASSERT_NE(nullptr, consumer);
+ ASSERT_EQ(seq_in, seq_out);
+ }
+}
+
+TEST_F(BufferHubQueueTest, TestMultipleConsumers) {
+ ASSERT_TRUE(CreateProducerQueue<void>());
+
+ // Allocate buffers.
+ const size_t kBufferCount = 4u;
+ for (size_t i = 0; i < kBufferCount; i++) {
+ AllocateBuffer();
+ }
+ ASSERT_EQ(kBufferCount, producer_queue_->count());
+
+ // Build a silent consumer queue to test multi-consumer queue features.
+ auto silent_queue = producer_queue_->CreateSilentConsumerQueue();
+ ASSERT_NE(nullptr, silent_queue);
+
+ // Check that buffers are correctly imported on construction.
+ EXPECT_EQ(kBufferCount, silent_queue->capacity());
+
+ // Dequeue and post a buffer.
+ size_t slot;
+ LocalHandle fence;
+ auto producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(producer_status.ok());
+ auto producer_buffer = producer_status.take();
+ ASSERT_NE(nullptr, producer_buffer);
+ ASSERT_EQ(0, producer_buffer->Post<void>({}));
+
+ // Currently we expect no buffer to be available prior to calling
+ // WaitForBuffers/HandleQueueEvents.
+ // TODO(eieio): Note this behavior may change in the future.
+ EXPECT_EQ(0u, silent_queue->count());
+ EXPECT_FALSE(silent_queue->HandleQueueEvents());
+ EXPECT_EQ(0u, silent_queue->count());
+
+ // Build a new consumer queue to test multi-consumer queue features.
+ consumer_queue_ = silent_queue->CreateConsumerQueue();
+ ASSERT_NE(nullptr, consumer_queue_);
+
+ // Check that buffers are correctly imported on construction.
+ EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
+ EXPECT_EQ(1u, consumer_queue_->count());
+
+ // Reclaim released/ignored buffers.
+ producer_queue_->HandleQueueEvents();
+ ASSERT_EQ(kBufferCount - 1, producer_queue_->count());
+
+ // Post another buffer.
+ producer_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(producer_status.ok());
+ producer_buffer = producer_status.take();
+ ASSERT_NE(nullptr, producer_buffer);
+ ASSERT_EQ(0, producer_buffer->Post<void>({}));
+
+ // Verify that the consumer queue receives it.
+ EXPECT_EQ(1u, consumer_queue_->count());
+ EXPECT_TRUE(consumer_queue_->HandleQueueEvents());
+ EXPECT_EQ(2u, consumer_queue_->count());
+
+ // Dequeue and acquire/release (discard) buffers on the consumer end.
+ auto consumer_status = consumer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(consumer_status.ok());
+ auto consumer_buffer = consumer_status.take();
+ ASSERT_NE(nullptr, consumer_buffer);
+ consumer_buffer->Discard();
+
+ // Buffer should be returned to the producer queue without being handled by
+ // the silent consumer queue.
+ EXPECT_EQ(1u, consumer_queue_->count());
+ EXPECT_EQ(kBufferCount - 2, producer_queue_->count());
+ EXPECT_TRUE(producer_queue_->HandleQueueEvents());
+ EXPECT_EQ(kBufferCount - 1, producer_queue_->count());
+}
+
+struct TestMetadata {
+ char a;
+ int32_t b;
+ int64_t c;
+};
+
+TEST_F(BufferHubQueueTest, TestMetadata) {
+ ASSERT_TRUE(CreateQueues<TestMetadata>());
+ AllocateBuffer();
+
+ std::vector<TestMetadata> ms = {
+ {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}};
+
+ for (auto mi : ms) {
+ size_t slot;
+ LocalHandle fence;
+ auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(nullptr, p1);
+ ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0);
+ TestMetadata mo;
+ auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+ ASSERT_TRUE(c1_status.ok());
+ auto c1 = c1_status.take();
+ ASSERT_EQ(mi.a, mo.a);
+ ASSERT_EQ(mi.b, mo.b);
+ ASSERT_EQ(mi.c, mo.c);
+ c1->Release(LocalHandle(-1));
+ }
+}
+
+TEST_F(BufferHubQueueTest, TestMetadataMismatch) {
+ ASSERT_TRUE(CreateQueues<int64_t>());
+ AllocateBuffer();
+
+ int64_t mi = 3;
+ size_t slot;
+ LocalHandle fence;
+ auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(nullptr, p1);
+ ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0);
+
+ int32_t mo;
+ // Acquire a buffer with mismatched metadata is not OK.
+ auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+ ASSERT_FALSE(c1_status.ok());
+}
+
+TEST_F(BufferHubQueueTest, TestEnqueue) {
+ ASSERT_TRUE(CreateQueues<int64_t>());
+ AllocateBuffer();
+
+ size_t slot;
+ LocalHandle fence;
+ auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(nullptr, p1);
+
+ int64_t mo;
+ producer_queue_->Enqueue(p1, slot);
+ auto c1_status = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+ ASSERT_FALSE(c1_status.ok());
+}
+
+TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
+ ASSERT_TRUE(CreateQueues<int64_t>());
+
+ size_t s1;
+ AllocateBuffer();
+ LocalHandle fence;
+ auto p1_status = producer_queue_->Dequeue(0, &s1, &fence);
+ ASSERT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_NE(nullptr, p1);
+
+ // producer queue is exhausted
+ size_t s2;
+ auto p2_status = producer_queue_->Dequeue(0, &s2, &fence);
+ ASSERT_FALSE(p2_status.ok());
+ ASSERT_EQ(ETIMEDOUT, p2_status.error());
+
+ // dynamically add buffer.
+ AllocateBuffer();
+ ASSERT_EQ(producer_queue_->count(), 1U);
+ ASSERT_EQ(producer_queue_->capacity(), 2U);
+
+ // now we can dequeue again
+ p2_status = producer_queue_->Dequeue(0, &s2, &fence);
+ ASSERT_TRUE(p2_status.ok());
+ auto p2 = p2_status.take();
+ ASSERT_NE(nullptr, p2);
+ ASSERT_EQ(producer_queue_->count(), 0U);
+ // p1 and p2 should have different slot number
+ ASSERT_NE(s1, s2);
+
+ // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers|
+ // are called. So far consumer_queue_ should be empty.
+ ASSERT_EQ(consumer_queue_->count(), 0U);
+
+ int64_t seq = 1;
+ ASSERT_EQ(p1->Post(LocalHandle(), seq), 0);
+ size_t cs1, cs2;
+ auto c1_status = consumer_queue_->Dequeue(0, &cs1, &seq, &fence);
+ ASSERT_TRUE(c1_status.ok());
+ auto c1 = c1_status.take();
+ ASSERT_NE(nullptr, c1);
+ ASSERT_EQ(consumer_queue_->count(), 0U);
+ ASSERT_EQ(consumer_queue_->capacity(), 2U);
+ ASSERT_EQ(cs1, s1);
+
+ ASSERT_EQ(p2->Post(LocalHandle(), seq), 0);
+ auto c2_status = consumer_queue_->Dequeue(0, &cs2, &seq, &fence);
+ ASSERT_TRUE(c2_status.ok());
+ auto c2 = c2_status.take();
+ ASSERT_NE(nullptr, c2);
+ ASSERT_EQ(cs2, s2);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageSetMask) {
+ const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ ASSERT_TRUE(CreateQueues<int64_t>(set_mask, 0, 0, 0));
+
+ // When allocation, leave out |set_mask| from usage bits on purpose.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+ kBufferFormat, kBufferLayerCount,
+ kBufferUsage & ~set_mask, &slot);
+ ASSERT_EQ(0, ret);
+
+ LocalHandle fence;
+ auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_EQ(p1->usage() & set_mask, set_mask);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageClearMask) {
+ const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ ASSERT_TRUE(CreateQueues<int64_t>(0, clear_mask, 0, 0));
+
+ // When allocation, add |clear_mask| into usage bits on purpose.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+ kBufferLayerCount, kBufferFormat,
+ kBufferUsage | clear_mask, &slot);
+ ASSERT_EQ(0, ret);
+
+ LocalHandle fence;
+ auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
+ ASSERT_TRUE(p1_status.ok());
+ auto p1 = p1_status.take();
+ ASSERT_EQ(0u, p1->usage() & clear_mask);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
+ const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ ASSERT_TRUE(CreateQueues<int64_t>(0, 0, deny_set_mask, 0));
+
+ // Now that |deny_set_mask| is illegal, allocation without those bits should
+ // be able to succeed.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(
+ kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
+ kBufferUsage & ~deny_set_mask, &slot);
+ ASSERT_EQ(ret, 0);
+
+ // While allocation with those bits should fail.
+ ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+ kBufferLayerCount, kBufferFormat,
+ kBufferUsage | deny_set_mask, &slot);
+ ASSERT_EQ(ret, -EINVAL);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) {
+ const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+ ASSERT_TRUE(CreateQueues<int64_t>(0, 0, 0, deny_clear_mask));
+
+ // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are
+ // mandatory), allocation with those bits should be able to succeed.
+ size_t slot;
+ int ret = producer_queue_->AllocateBuffer(
+ kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
+ kBufferUsage | deny_clear_mask, &slot);
+ ASSERT_EQ(ret, 0);
+
+ // While allocation without those bits should fail.
+ ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+ kBufferLayerCount, kBufferFormat,
+ kBufferUsage & ~deny_clear_mask, &slot);
+ ASSERT_EQ(ret, -EINVAL);
+}
+
+} // namespace
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
new file mode 100644
index 0000000000..2b6239f499
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -0,0 +1,512 @@
+#include <private/dvr/buffer_hub_queue_producer.h>
+
+#include <base/logging.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// Default dimensions before setDefaultBufferSize is called by the consumer.
+constexpr uint32_t kDefaultWidth = 1;
+constexpr uint32_t kDefaultHeight = 1;
+
+// Default format before setDefaultBufferFormat is called by the consumer.
+constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+constexpr int kDefaultConsumerUsageBits = 0;
+
+// Default transform hint before setTransformHint is called by the consumer.
+constexpr uint32_t kDefaultTransformHint = 0;
+
+constexpr int kTestApi = NATIVE_WINDOW_API_CPU;
+constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL;
+constexpr int kTestApiInvalid = 0xDEADBEEF;
+constexpr int kTestProducerUsageBits = 0;
+constexpr bool kTestControlledByApp = true;
+
+// Builder pattern to slightly vary *almost* correct input
+// -- avoids copying and pasting
+struct QueueBufferInputBuilder {
+ IGraphicBufferProducer::QueueBufferInput build() {
+ return IGraphicBufferProducer::QueueBufferInput(
+ mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode,
+ mTransform, mFence);
+ }
+
+ QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
+ this->mTimestamp = timestamp;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) {
+ this->mIsAutoTimestamp = isAutoTimestamp;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) {
+ this->mDataSpace = dataSpace;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setCrop(Rect crop) {
+ this->mCrop = crop;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setScalingMode(int scalingMode) {
+ this->mScalingMode = scalingMode;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setTransform(uint32_t transform) {
+ this->mTransform = transform;
+ return *this;
+ }
+
+ QueueBufferInputBuilder& setFence(sp<Fence> fence) {
+ this->mFence = fence;
+ return *this;
+ }
+
+ private:
+ int64_t mTimestamp{1384888611};
+ bool mIsAutoTimestamp{false};
+ android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN};
+ Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)};
+ int mScalingMode{0};
+ uint32_t mTransform{0};
+ sp<Fence> mFence{Fence::NO_FENCE};
+};
+
+// This is a test that covers our implementation of bufferhubqueue-based
+// IGraphicBufferProducer.
+class BufferHubQueueProducerTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(),
+ testInfo->name());
+
+ mProducer = BufferHubQueueProducer::Create();
+ ASSERT_TRUE(mProducer != nullptr);
+ mSurface = new Surface(mProducer, true);
+ ASSERT_TRUE(mSurface != nullptr);
+ }
+
+ // Connect to a producer in a 'correct' fashion.
+ void ConnectProducer() {
+ IGraphicBufferProducer::QueueBufferOutput output;
+ // Can connect the first time.
+ ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi,
+ kTestControlledByApp, &output));
+ }
+
+ // Dequeue a buffer in a 'correct' fashion.
+ // Precondition: Producer is connected.
+ void DequeueBuffer(int* outSlot) {
+ sp<Fence> fence;
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence));
+ }
+
+ void DequeueBuffer(int* outSlot, sp<Fence>* outFence) {
+ ASSERT_NE(nullptr, outSlot);
+ ASSERT_NE(nullptr, outFence);
+
+ int ret = mProducer->dequeueBuffer(outSlot, outFence, kDefaultWidth,
+ kDefaultHeight, kDefaultFormat,
+ kTestProducerUsageBits, nullptr);
+ // BUFFER_NEEDS_REALLOCATION can be either on or off.
+ ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret);
+
+ // Slot number should be in boundary.
+ ASSERT_LE(0, *outSlot);
+ ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot);
+ }
+
+ // Create a generic "valid" input for queueBuffer
+ // -- uses the default buffer format, width, etc.
+ static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() {
+ return QueueBufferInputBuilder().build();
+ }
+
+ const sp<IProducerListener> kDummyListener{new DummyProducerListener};
+
+ sp<BufferHubQueueProducer> mProducer;
+ sp<Surface> mSurface;
+};
+
+TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) {
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ // NULL output returns BAD_VALUE
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+ kTestControlledByApp, nullptr));
+
+ // Invalid API returns bad value
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid,
+ kTestControlledByApp, &output));
+}
+
+TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) {
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ // Can't connect when there is already a producer connected.
+ IGraphicBufferProducer::QueueBufferOutput output;
+ EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
+ kTestControlledByApp, &output));
+}
+
+TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+}
+
+TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ // Must disconnect with same API number
+ EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther));
+ // API must not be out of range
+ EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid));
+}
+
+TEST_F(BufferHubQueueProducerTest, Query_Succeeds) {
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ int32_t value = -1;
+ EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+ EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
+
+ EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
+ EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
+
+ EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
+ EXPECT_EQ(kDefaultFormat, value);
+
+ EXPECT_EQ(NO_ERROR,
+ mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
+ EXPECT_LE(0, value);
+ EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, static_cast<size_t>(value));
+
+ EXPECT_EQ(NO_ERROR,
+ mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
+ EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue
+
+ EXPECT_EQ(NO_ERROR,
+ mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
+ EXPECT_EQ(kDefaultConsumerUsageBits, value);
+}
+
+TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) {
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ // One past the end of the last 'query' enum value. Update this if we add more
+ // enums.
+ const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1;
+
+ int value;
+ // What was out of range
+ EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value));
+ EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value));
+ EXPECT_EQ(BAD_VALUE,
+ mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value));
+
+ // Some enums from window.h are 'invalid'
+ EXPECT_EQ(BAD_VALUE,
+ mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value));
+ EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value));
+ EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value));
+ EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value));
+ EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
+
+ // Value was NULL
+ EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL));
+}
+
+TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) {
+ int slot = -1;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+ // Request the buffer (pre-requisite for queueing)
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+ // A generic "valid" input
+ IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ // Queue the buffer back into the BQ
+ ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+
+ EXPECT_EQ(kDefaultWidth, output.width);
+ EXPECT_EQ(kDefaultHeight, output.height);
+ EXPECT_EQ(kDefaultTransformHint, output.transformHint);
+
+ // BufferHubQueue delivers buffers to consumer immediately.
+ EXPECT_EQ(0u, output.numPendingBuffers);
+
+ // Note that BufferHubQueue doesn't support nextFrameNumber as it seems to
+ // be a SurfaceFlinger specific optimization.
+ EXPECT_EQ(0u, output.nextFrameNumber);
+
+ // Buffer was not in the dequeued state
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+// Test invalid slot number
+TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) {
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ // A generic "valid" input
+ IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ -1, input, &output));
+ EXPECT_EQ(BAD_VALUE,
+ mProducer->queueBuffer(/*slot*/ 0xDEADBEEF, input, &output));
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueueDefs::NUM_BUFFER_SLOTS,
+ input, &output));
+}
+
+// Slot was not in the dequeued state (all slots start out in Free state)
+TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) {
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output));
+}
+
+// Slot was enqueued without requesting a buffer
+TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) {
+ int slot = -1;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+ IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+// Test when fence was NULL
+TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) {
+ int slot = -1;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+ sp<Fence> nullFence = NULL;
+
+ IGraphicBufferProducer::QueueBufferInput input =
+ QueueBufferInputBuilder().setFence(nullFence).build();
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+// Test scaling mode was invalid
+TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) {
+ int slot = -1;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+ IGraphicBufferProducer::QueueBufferInput input =
+ QueueBufferInputBuilder().setScalingMode(-1).build();
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+
+ input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
+
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+// Test crop rect is out of bounds of the buffer dimensions
+TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) {
+ int slot = -1;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+ IGraphicBufferProducer::QueueBufferInput input =
+ QueueBufferInputBuilder()
+ .setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1))
+ .build();
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
+}
+
+TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) {
+ int slot = -1;
+ sp<Fence> fence;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
+
+ // Should be able to cancel buffer after a dequeue.
+ EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+}
+
+TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
+ return;
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ int minUndequeuedBuffers;
+ ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
+
+ const int minBuffers = 1;
+ const int maxBuffers =
+ BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
+
+ ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
+ << "async mode: " << false;
+ ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers))
+ << "bufferCount: " << minBuffers;
+
+ // Should now be able to dequeue up to minBuffers times
+ // Should now be able to dequeue up to maxBuffers times
+ int slot = -1;
+ for (int i = 0; i < minBuffers; ++i) {
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+ }
+
+ ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers));
+
+ // queue the first buffer to enable max dequeued buffer count checking
+ IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+
+ sp<Fence> fence;
+ for (int i = 0; i < maxBuffers; ++i) {
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
+ }
+
+ // Cancel a buffer, so we can decrease the buffer count
+ ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+
+ // Should now be able to decrease the max dequeued count by 1
+ ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
+}
+
+TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+
+ int minUndequeuedBuffers;
+ ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
+
+ const int minBuffers = 1;
+ const int maxBuffers =
+ BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
+
+ ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
+ << "async mode: " << false;
+ // Buffer count was out of range
+ EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
+ << "bufferCount: " << 0;
+ EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1))
+ << "bufferCount: " << maxBuffers + 1;
+
+ // Set max dequeue count to 2
+ ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2));
+ // Dequeue 2 buffers
+ int slot = -1;
+ sp<Fence> fence;
+ for (int i = 0; i < 2; i++) {
+ ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+ (mProducer->dequeueBuffer(
+ &slot, &fence, kDefaultWidth, kDefaultHeight,
+ kDefaultFormat, kTestProducerUsageBits, nullptr)))
+ << "slot: " << slot;
+ }
+
+ // Client has too many buffers dequeued
+ EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1))
+ << "bufferCount: " << minBuffers;
+}
+
+TEST_F(BufferHubQueueProducerTest,
+ DisconnectedProducerReturnsError_dequeueBuffer) {
+ int slot = -1;
+ sp<Fence> fence;
+
+ ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
+ kDefaultHeight, kDefaultFormat,
+ kTestProducerUsageBits, nullptr));
+}
+
+TEST_F(BufferHubQueueProducerTest,
+ DisconnectedProducerReturnsError_requestBuffer) {
+ int slot = -1;
+ sp<GraphicBuffer> buffer;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+
+ // Shouldn't be able to request buffer after disconnect.
+ ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
+}
+
+TEST_F(BufferHubQueueProducerTest,
+ DisconnectedProducerReturnsError_queueBuffer) {
+ int slot = -1;
+ sp<GraphicBuffer> buffer;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+ ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+ // A generic "valid" input
+ IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
+ IGraphicBufferProducer::QueueBufferOutput output;
+
+ // Shouldn't be able to queue buffer after disconnect.
+ ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
+}
+
+TEST_F(BufferHubQueueProducerTest,
+ DisconnectedProducerReturnsError_cancelBuffer) {
+ int slot = -1;
+ sp<GraphicBuffer> buffer;
+
+ ASSERT_NO_FATAL_FAILURE(ConnectProducer());
+ ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
+ ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+
+ // Shouldn't be able to cancel buffer after disconnect.
+ ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
+}
+
+} // namespace
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
new file mode 100644
index 0000000000..d90521a810
--- /dev/null
+++ b/libs/vr/libdisplay/Android.bp
@@ -0,0 +1,66 @@
+// Copyright (C) 2015 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.
+
+sourceFiles = [
+ "display_client.cpp",
+ "display_manager_client.cpp",
+ "display_protocol.cpp",
+ "vsync_client.cpp",
+]
+
+localIncludeFiles = [
+ "include",
+]
+
+sharedLibraries = [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libui",
+ "libgui",
+ "libhardware",
+ "libsync",
+ "libnativewindow",
+]
+
+staticLibraries = [
+ "libdvrcommon",
+ "libbufferhubqueue",
+ "libbufferhub",
+ "libvrsensor",
+ "libpdx_default_transport",
+]
+
+headerLibraries = [
+ "vulkan_headers",
+]
+
+cc_library {
+ tags: ["tests"],
+ srcs: sourceFiles,
+ cflags: ["-DLOG_TAG=\"libdisplay\"",
+ "-DTRACE=0",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+ ], // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ],
+ export_include_dirs: localIncludeFiles,
+ shared_libs: sharedLibraries,
+ static_libs: staticLibraries,
+ header_libs: headerLibraries,
+ export_header_lib_headers: headerLibraries,
+
+ name: "libdisplay",
+}
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
new file mode 100644
index 0000000000..935ca2ef81
--- /dev/null
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -0,0 +1,210 @@
+#include "include/private/dvr/display_client.h"
+
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/status.h>
+
+#include <mutex>
+
+#include <private/dvr/display_protocol.h>
+#include <private/dvr/native_buffer.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::IfAnyOf;
+
+namespace android {
+namespace dvr {
+namespace display {
+
+Surface::Surface(LocalChannelHandle channel_handle, int* error)
+ : BASE{pdx::default_transport::ClientChannel::Create(
+ std::move(channel_handle))} {
+ auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>();
+ if (!status) {
+ ALOGE("Surface::Surface: Failed to get surface info: %s",
+ status.GetErrorMessage().c_str());
+ Close(status.error());
+ if (error)
+ *error = status.error();
+ }
+
+ surface_id_ = status.get().surface_id;
+ z_order_ = status.get().z_order;
+ visible_ = status.get().visible;
+}
+
+Surface::Surface(const SurfaceAttributes& attributes, int* error)
+ : BASE{pdx::default_transport::ClientChannelFactory::Create(
+ DisplayProtocol::kClientPath),
+ kInfiniteTimeout} {
+ auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes);
+ if (!status) {
+ ALOGE("Surface::Surface: Failed to create display surface: %s",
+ status.GetErrorMessage().c_str());
+ Close(status.error());
+ if (error)
+ *error = status.error();
+ }
+
+ surface_id_ = status.get().surface_id;
+ z_order_ = status.get().z_order;
+ visible_ = status.get().visible;
+}
+
+Status<void> Surface::SetVisible(bool visible) {
+ return SetAttributes(
+ {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}});
+}
+
+Status<void> Surface::SetZOrder(int z_order) {
+ return SetAttributes(
+ {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}});
+}
+
+Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) {
+ auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes);
+ if (!status) {
+ ALOGE(
+ "Surface::SetAttributes: Failed to set display surface "
+ "attributes: %s",
+ status.GetErrorMessage().c_str());
+ return status.error_status();
+ }
+
+ // Set the local cached copies of the attributes we care about from the full
+ // set of attributes sent to the display service.
+ for (const auto& attribute : attributes) {
+ const auto& key = attribute.first;
+ const auto* variant = &attribute.second;
+ bool invalid_value = false;
+ switch (key) {
+ case SurfaceAttribute::Visible:
+ invalid_value =
+ !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
+ break;
+ case SurfaceAttribute::ZOrder:
+ invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
+ break;
+ }
+
+ if (invalid_value) {
+ ALOGW(
+ "Surface::SetAttributes: Failed to set display surface "
+ "attribute %d because of incompatible type: %d",
+ key, variant->index());
+ }
+ }
+
+ return {};
+}
+
+Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue() {
+ ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue.");
+ auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(0);
+ if (!status) {
+ ALOGE("Surface::CreateQueue: Failed to create queue: %s",
+ status.GetErrorMessage().c_str());
+ return status.error_status();
+ }
+
+ auto producer_queue = ProducerQueue::Import(status.take());
+ if (!producer_queue) {
+ ALOGE("Surface::CreateQueue: Failed to import producer queue!");
+ return ErrorStatus(ENOMEM);
+ }
+
+ return {std::move(producer_queue)};
+}
+
+Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
+ uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
+ uint64_t usage, size_t capacity) {
+ ALOGD_IF(TRACE,
+ "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u "
+ "usage=%" PRIx64 " capacity=%zu",
+ width, height, layer_count, format, usage, capacity);
+ auto status = CreateQueue();
+ if (!status)
+ return status.error_status();
+
+ auto producer_queue = status.take();
+
+ ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity);
+ for (size_t i = 0; i < capacity; i++) {
+ size_t slot;
+ const int ret = producer_queue->AllocateBuffer(width, height, layer_count,
+ format, usage, &slot);
+ if (ret < 0) {
+ ALOGE(
+ "Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s",
+ producer_queue->id(), strerror(-ret));
+ return ErrorStatus(ENOMEM);
+ }
+ ALOGD_IF(
+ TRACE,
+ "Surface::CreateQueue: Allocated buffer at slot=%zu of capacity=%zu",
+ slot, capacity);
+ }
+
+ return {std::move(producer_queue)};
+}
+
+DisplayClient::DisplayClient(int* error)
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DisplayProtocol::kClientPath),
+ kInfiniteTimeout) {
+ if (error)
+ *error = Client::error();
+}
+
+Status<Metrics> DisplayClient::GetDisplayMetrics() {
+ return InvokeRemoteMethod<DisplayProtocol::GetMetrics>();
+}
+
+Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface(
+ const SurfaceAttributes& attributes) {
+ int error;
+ if (auto client = Surface::Create(attributes, &error))
+ return {std::move(client)};
+ else
+ return ErrorStatus(error);
+}
+
+Status<std::unique_ptr<IonBuffer>> DisplayClient::GetNamedBuffer(
+ const std::string& name) {
+ auto status = InvokeRemoteMethod<DisplayProtocol::GetNamedBuffer>(name);
+ if (!status) {
+ ALOGE(
+ "DisplayClient::GetNamedBuffer: Failed to get named buffer: name=%s; "
+ "error=%s",
+ name.c_str(), status.GetErrorMessage().c_str());
+ return status.error_status();
+ }
+
+ auto ion_buffer = std::make_unique<IonBuffer>();
+ auto native_buffer_handle = status.take();
+ const int ret = native_buffer_handle.Import(ion_buffer.get());
+ if (ret < 0) {
+ ALOGE(
+ "DisplayClient::GetNamedBuffer: Failed to import named buffer: "
+ "name=%s; error=%s",
+ name.c_str(), strerror(-ret));
+ return ErrorStatus(-ret);
+ }
+
+ return {std::move(ion_buffer)};
+}
+
+Status<bool> DisplayClient::IsVrAppRunning() {
+ return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>();
+}
+
+} // namespace display
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
new file mode 100644
index 0000000000..82dacf707d
--- /dev/null
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -0,0 +1,78 @@
+#include "include/private/dvr/display_manager_client.h"
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_protocol.h>
+#include <utils/Log.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+namespace display {
+
+DisplayManagerClient::DisplayManagerClient()
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DisplayManagerProtocol::kClientPath)) {}
+
+DisplayManagerClient::~DisplayManagerClient() {}
+
+pdx::Status<std::vector<display::SurfaceState>>
+DisplayManagerClient::GetSurfaceState() {
+ auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>();
+ if (!status) {
+ ALOGE(
+ "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s",
+ status.GetErrorMessage().c_str());
+ }
+
+ return status;
+}
+
+pdx::Status<std::unique_ptr<IonBuffer>> DisplayManagerClient::SetupNamedBuffer(
+ const std::string& name, size_t size, uint64_t usage) {
+ auto status = InvokeRemoteMethod<DisplayManagerProtocol::SetupNamedBuffer>(
+ name, size, usage);
+ if (!status) {
+ ALOGE(
+ "DisplayManagerClient::SetupPoseBuffer: Failed to create the named "
+ "buffer %s",
+ status.GetErrorMessage().c_str());
+ return status.error_status();
+ }
+
+ auto ion_buffer = std::make_unique<IonBuffer>();
+ auto native_buffer_handle = status.take();
+ const int ret = native_buffer_handle.Import(ion_buffer.get());
+ if (ret < 0) {
+ ALOGE(
+ "DisplayClient::GetNamedBuffer: Failed to import named buffer: "
+ "name=%s; error=%s",
+ name.c_str(), strerror(-ret));
+ return ErrorStatus(-ret);
+ }
+
+ return {std::move(ion_buffer)};
+}
+
+pdx::Status<std::unique_ptr<ConsumerQueue>>
+DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) {
+ auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
+ surface_id, queue_id);
+ if (!status) {
+ ALOGE(
+ "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for "
+ "surface_id=%d queue_id=%d: %s",
+ surface_id, queue_id, status.GetErrorMessage().c_str());
+ return status.error_status();
+ }
+
+ return {ConsumerQueue::Import(status.take())};
+}
+
+} // namespace display
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp
new file mode 100644
index 0000000000..773f9a5aa3
--- /dev/null
+++ b/libs/vr/libdisplay/display_protocol.cpp
@@ -0,0 +1,13 @@
+#include "include/private/dvr/display_protocol.h"
+
+namespace android {
+namespace dvr {
+namespace display {
+
+constexpr char DisplayProtocol::kClientPath[];
+constexpr char DisplayManagerProtocol::kClientPath[];
+constexpr char VSyncProtocol::kClientPath[];
+
+} // namespace display
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg
new file mode 100644
index 0000000000..2f8a3c018c
--- /dev/null
+++ b/libs/vr/libdisplay/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdisplay/include/dvr/dvr_display_types.h b/libs/vr/libdisplay/include/dvr/dvr_display_types.h
new file mode 100644
index 0000000000..25364d8590
--- /dev/null
+++ b/libs/vr/libdisplay/include/dvr/dvr_display_types.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_DVR_DISPLAY_TYPES_H_
+#define ANDROID_DVR_DISPLAY_TYPES_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+// Define types used in pose buffer fields. These types have atomicity
+// guarantees that are useful in lock-free shared memory ring buffers.
+#ifdef __ARM_NEON
+#include <arm_neon.h>
+#else
+#ifndef __FLOAT32X4T_86
+#define __FLOAT32X4T_86
+typedef float float32x4_t __attribute__((__vector_size__(16)));
+typedef struct float32x4x4_t { float32x4_t val[4]; };
+#endif
+#endif
+
+// VrFlinger display manager surface state snapshots per surface flags
+// indicating what changed since the last snapshot.
+enum {
+ // No changes.
+ DVR_SURFACE_UPDATE_FLAGS_NONE = 0,
+ // This surface is new.
+ DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE = (1 << 0),
+ // Buffer queues added/removed.
+ DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED = (1 << 1),
+ // Visibility/z-order changed.
+ DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED = (1 << 2),
+ // Generic attributes changed.
+ DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED = (1 << 3),
+};
+
+// Surface attribute keys. VrFlinger defines keys in the negative integer space.
+// The compositor is free to use keys in the positive integer space for
+// implementation-defined purposes.
+enum {
+ // DIRECT: bool
+ // Determines whether a direct surface is created (compositor output) or an
+ // application surface. Defaults to false (application surface). May only be
+ // set to true by a process with either UID=root or UID validated with
+ // IsTrustedUid() (VrCore).
+ DVR_SURFACE_ATTRIBUTE_DIRECT = -3,
+ // Z_ORDER: int32_t
+ // Interpreted by VrFlinger only on direct surfaces to order the corresponding
+ // hardware layers. More positive values render on top of more negative
+ // values.
+ DVR_SURFACE_ATTRIBUTE_Z_ORDER = -2,
+ // VISIBLE: bool
+ // Interpreted by VrFlinger only on direct surfaces to determine whether a
+ // surface is assigned to a hardware layer or ignored.
+ DVR_SURFACE_ATTRIBUTE_VISIBLE = -1,
+ // INVALID
+ // Invalid key. No attributes should have this key.
+ DVR_SURFACE_ATTRIBUTE_INVALID = 0,
+ // FIRST_USER_KEY
+ // VrFlinger ingores any keys with this value or greater, passing them to the
+ // compositor through surface state query results.
+ DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY = 1,
+};
+
+__END_DECLS
+
+#endif // ANDROID_DVR_DISPLAY_TYPES_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
new file mode 100644
index 0000000000..7a7f670ffa
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -0,0 +1,91 @@
+#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_
+#define ANDROID_DVR_DISPLAY_CLIENT_H_
+
+#include <hardware/hwcomposer.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_protocol.h>
+
+namespace android {
+namespace dvr {
+namespace display {
+
+class Surface : public pdx::ClientBase<Surface> {
+ public:
+ // Utility named constructor. This can be removed once ClientBase::Create is
+ // refactored to return Status<T> types.
+ static pdx::Status<std::unique_ptr<Surface>> CreateSurface(
+ const SurfaceAttributes& attributes) {
+ int error;
+ pdx::Status<std::unique_ptr<Surface>> status;
+ if (auto surface = Create(attributes, &error))
+ status.SetValue(std::move(surface));
+ else
+ status.SetError(error);
+ return status;
+ }
+
+ int surface_id() const { return surface_id_; }
+ int z_order() const { return z_order_; }
+ bool visible() const { return visible_; }
+
+ pdx::Status<void> SetVisible(bool visible);
+ pdx::Status<void> SetZOrder(int z_order);
+ pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes);
+
+ // Creates an empty queue.
+ pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue();
+
+ // Creates a queue and populates it with |capacity| buffers of the specified
+ // parameters.
+ pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
+ uint32_t height,
+ uint32_t layer_count,
+ uint32_t format,
+ uint64_t usage,
+ size_t capacity);
+
+ private:
+ friend BASE;
+
+ int surface_id_ = -1;
+ int z_order_ = 0;
+ bool visible_ = false;
+
+ // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create()
+ // returns Status<T>.
+ explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr);
+ explicit Surface(pdx::LocalChannelHandle channel_handle,
+ int* error = nullptr);
+
+ Surface(const Surface&) = delete;
+ void operator=(const Surface&) = delete;
+};
+
+class DisplayClient : public pdx::ClientBase<DisplayClient> {
+ public:
+ pdx::Status<Metrics> GetDisplayMetrics();
+ pdx::Status<std::unique_ptr<IonBuffer>> GetNamedBuffer(
+ const std::string& name);
+ pdx::Status<std::unique_ptr<Surface>> CreateSurface(
+ const SurfaceAttributes& attributes);
+
+ // Temporary query for current VR status. Will be removed later.
+ pdx::Status<bool> IsVrAppRunning();
+
+ private:
+ friend BASE;
+
+ explicit DisplayClient(int* error = nullptr);
+
+ DisplayClient(const DisplayClient&) = delete;
+ void operator=(const DisplayClient&) = delete;
+};
+
+} // namespace display
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISPLAY_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
new file mode 100644
index 0000000000..fea841595a
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
+#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include <pdx/client.h>
+#include <pdx/status.h>
+#include <private/dvr/display_protocol.h>
+
+namespace android {
+namespace dvr {
+
+class IonBuffer;
+class ConsumerQueue;
+
+namespace display {
+
+class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
+ public:
+ ~DisplayManagerClient() override;
+
+ pdx::Status<std::vector<SurfaceState>> GetSurfaceState();
+ pdx::Status<std::unique_ptr<IonBuffer>> SetupNamedBuffer(
+ const std::string& name, size_t size, uint64_t usage);
+ pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id,
+ int queue_id);
+
+ using Client::event_fd;
+
+ pdx::Status<int> GetEventMask(int events) {
+ if (auto* client_channel = GetChannel())
+ return client_channel->GetEventMask(events);
+ else
+ return pdx::ErrorStatus(EINVAL);
+ }
+
+ private:
+ friend BASE;
+
+ DisplayManagerClient();
+
+ DisplayManagerClient(const DisplayManagerClient&) = delete;
+ void operator=(const DisplayManagerClient&) = delete;
+};
+
+} // namespace display
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
new file mode 100644
index 0000000000..f34d61fe49
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
@@ -0,0 +1,283 @@
+#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_
+#define ANDROID_DVR_DISPLAY_PROTOCOL_H_
+
+#include <sys/types.h>
+
+#include <array>
+#include <map>
+
+#include <dvr/dvr_display_types.h>
+
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/variant.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+// RPC protocol definitions for DVR display services (VrFlinger).
+
+namespace android {
+namespace dvr {
+namespace display {
+
+// Native display metrics.
+struct Metrics {
+ // Basic display properties.
+ uint32_t display_width;
+ uint32_t display_height;
+ uint32_t display_x_dpi;
+ uint32_t display_y_dpi;
+ uint32_t vsync_period_ns;
+
+ // HMD metrics.
+ // TODO(eieio): Determine how these fields should be populated. On phones
+ // these values are determined at runtime by VrCore based on which headset the
+ // phone is in. On dedicated hardware this needs to come from somewhere else.
+ // Perhaps these should be moved to a separate structure that is returned by a
+ // separate runtime call.
+ uint32_t distorted_width;
+ uint32_t distorted_height;
+ uint32_t hmd_ipd_mm;
+ float inter_lens_distance_m;
+ std::array<float, 4> left_fov_lrbt;
+ std::array<float, 4> right_fov_lrbt;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height,
+ display_x_dpi, display_y_dpi, vsync_period_ns,
+ distorted_width, distorted_height, hmd_ipd_mm,
+ inter_lens_distance_m, left_fov_lrbt,
+ right_fov_lrbt);
+};
+
+// Serializable base type for enum structs. Enum structs are easier to use than
+// enum classes, especially for bitmasks. This base type provides common
+// utilities for flags types.
+template <typename Integer>
+class Flags {
+ public:
+ using Base = Flags<Integer>;
+ using Type = Integer;
+
+ Flags(const Integer& value) : value_{value} {}
+ Flags(const Flags&) = default;
+ Flags& operator=(const Flags&) = default;
+
+ Integer value() const { return value_; }
+ operator Integer() const { return value_; }
+
+ bool IsSet(Integer bits) const { return (value_ & bits) == bits; }
+ bool IsClear(Integer bits) const { return (value_ & bits) == 0; }
+
+ void Set(Integer bits) { value_ |= bits; }
+ void Clear(Integer bits) { value_ &= ~bits; }
+
+ Integer operator|(Integer bits) const { return value_ | bits; }
+ Integer operator&(Integer bits) const { return value_ & bits; }
+
+ Flags& operator|=(Integer bits) {
+ value_ |= bits;
+ return *this;
+ }
+ Flags& operator&=(Integer bits) {
+ value_ &= bits;
+ return *this;
+ }
+
+ private:
+ Integer value_;
+
+ PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_);
+};
+
+// Flags indicating what changed since last update.
+struct SurfaceUpdateFlags : public Flags<uint32_t> {
+ enum : Type {
+ None = DVR_SURFACE_UPDATE_FLAGS_NONE,
+ NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE,
+ BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED,
+ VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED,
+ AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED,
+ };
+
+ SurfaceUpdateFlags() : Base{None} {}
+ using Base::Base;
+};
+
+// Surface attribute key/value types.
+using SurfaceAttributeKey = int32_t;
+using SurfaceAttributeValue =
+ pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>,
+ std::array<float, 3>, std::array<float, 4>,
+ std::array<float, 8>, std::array<float, 16>>;
+
+// Defined surface attribute keys.
+struct SurfaceAttribute : public Flags<SurfaceAttributeKey> {
+ enum : Type {
+ // Keys in the negative integer space are interpreted by VrFlinger for
+ // direct surfaces.
+ Direct = DVR_SURFACE_ATTRIBUTE_DIRECT,
+ ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
+ Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE,
+
+ // Invalid key. May be used to terminate C style lists in public API code.
+ Invalid = DVR_SURFACE_ATTRIBUTE_INVALID,
+
+ // Positive keys are interpreted by the compositor only.
+ FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY,
+ };
+
+ SurfaceAttribute() : Base{Invalid} {}
+ using Base::Base;
+};
+
+// Collection of surface attribute key/value pairs.
+using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>;
+
+struct SurfaceState {
+ int32_t surface_id;
+ int32_t process_id;
+ int32_t user_id;
+
+ SurfaceAttributes surface_attributes;
+ SurfaceUpdateFlags update_flags;
+ std::vector<int32_t> queue_ids;
+
+ // Convenience accessors.
+ bool GetVisible() const {
+ bool bool_value = false;
+ GetAttribute(SurfaceAttribute::Visible, &bool_value,
+ ValidTypes<int32_t, int64_t, bool, float>{});
+ return bool_value;
+ }
+
+ int GetZOrder() const {
+ int int_value = 0;
+ GetAttribute(SurfaceAttribute::ZOrder, &int_value,
+ ValidTypes<int32_t, int64_t, float>{});
+ return int_value;
+ }
+
+ private:
+ template <typename... Types>
+ struct ValidTypes {};
+
+ template <typename ReturnType, typename... Types>
+ bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value,
+ ValidTypes<Types...>) const {
+ auto search = surface_attributes.find(key);
+ if (search != surface_attributes.end())
+ return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value);
+ else
+ return false;
+ }
+
+ PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id,
+ surface_attributes, update_flags, queue_ids);
+};
+
+struct SurfaceInfo {
+ int surface_id;
+ bool visible;
+ int z_order;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order);
+};
+
+struct DisplayProtocol {
+ // Service path.
+ static constexpr char kClientPath[] = "system/vr/display/client";
+
+ // Op codes.
+ enum {
+ kOpGetMetrics = 0,
+ kOpGetNamedBuffer,
+ kOpIsVrAppRunning,
+ kOpCreateSurface,
+ kOpGetSurfaceInfo,
+ kOpCreateQueue,
+ kOpSetAttributes,
+ };
+
+ // Aliases.
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ using Void = pdx::rpc::Void;
+
+ // Methods.
+ PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void));
+ PDX_REMOTE_METHOD(GetNamedBuffer, kOpGetNamedBuffer,
+ LocalNativeBufferHandle(std::string name));
+ PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void));
+ PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
+ SurfaceInfo(const SurfaceAttributes& attributes));
+ PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void));
+ PDX_REMOTE_METHOD(CreateQueue, kOpCreateQueue,
+ LocalChannelHandle(size_t meta_size_bytes));
+ PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
+ void(const SurfaceAttributes& attributes));
+};
+
+struct DisplayManagerProtocol {
+ // Service path.
+ static constexpr char kClientPath[] = "system/vr/display/manager";
+
+ // Op codes.
+ enum {
+ kOpGetSurfaceState = 0,
+ kOpGetSurfaceQueue,
+ kOpSetupNamedBuffer,
+ };
+
+ // Aliases.
+ using LocalChannelHandle = pdx::LocalChannelHandle;
+ using Void = pdx::rpc::Void;
+
+ // Methods.
+ PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState,
+ std::vector<SurfaceState>(Void));
+ PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue,
+ LocalChannelHandle(int surface_id, int queue_id));
+ PDX_REMOTE_METHOD(SetupNamedBuffer, kOpSetupNamedBuffer,
+ LocalNativeBufferHandle(const std::string& name,
+ size_t size, uint64_t usage));
+};
+
+struct VSyncSchedInfo {
+ int64_t vsync_period_ns;
+ int64_t timestamp_ns;
+ uint32_t next_vsync_count;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns,
+ next_vsync_count);
+};
+
+struct VSyncProtocol {
+ // Service path.
+ static constexpr char kClientPath[] = "system/vr/display/vsync";
+
+ // Op codes.
+ enum {
+ kOpWait = 0,
+ kOpAck,
+ kOpGetLastTimestamp,
+ kOpGetSchedInfo,
+ kOpAcknowledge,
+ };
+
+ // Aliases.
+ using Void = pdx::rpc::Void;
+ using Timestamp = int64_t;
+
+ // Methods.
+ PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void));
+ PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void));
+ PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void));
+ PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void));
+};
+
+} // namespace display
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_DISPLAY_PROTOCOL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
new file mode 100644
index 0000000000..1eeb80e09d
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
@@ -0,0 +1,69 @@
+#ifndef ANDROID_DVR_VSYNC_CLIENT_H_
+#define ANDROID_DVR_VSYNC_CLIENT_H_
+
+#include <stdint.h>
+
+#include <pdx/client.h>
+
+struct dvr_vsync_client {};
+
+namespace android {
+namespace dvr {
+
+/*
+ * VSyncClient is a remote interface to the vsync service in displayd.
+ * This class is used to wait for and retrieve information about the
+ * display vsync.
+ */
+class VSyncClient : public pdx::ClientBase<VSyncClient>,
+ public dvr_vsync_client {
+ public:
+ /*
+ * Wait for the next vsync signal.
+ * The timestamp (in ns) is written into *ts when ts is non-NULL.
+ */
+ int Wait(int64_t* timestamp_ns);
+
+ /*
+ * Returns the file descriptor used to communicate with the vsync system
+ * service or -1 on error.
+ */
+ int GetFd();
+
+ /*
+ * Clears the select/poll/epoll event so that subsequent calls to
+ * these will not signal until the next vsync.
+ */
+ int Acknowledge();
+
+ /*
+ * Get the timestamp of the last vsync event in ns. This call has
+ * the same side effect on events as Acknowledge(), which saves
+ * an IPC message.
+ */
+ int GetLastTimestamp(int64_t* timestamp_ns);
+
+ /*
+ * Get vsync scheduling info.
+ * Get the estimated timestamp of the next GPU lens warp preemption event in
+ * ns. Also returns the corresponding vsync count that the next lens warp
+ * operation will target. This call has the same side effect on events as
+ * Acknowledge(), which saves an IPC message.
+ */
+ int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns,
+ uint32_t* next_vsync_count);
+
+ private:
+ friend BASE;
+
+ VSyncClient();
+ explicit VSyncClient(long timeout_ms);
+
+ VSyncClient(const VSyncClient&) = delete;
+ void operator=(const VSyncClient&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_VSYNC_CLIENT_H_
diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg
new file mode 100644
index 0000000000..2f8a3c018c
--- /dev/null
+++ b/libs/vr/libdisplay/system/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp
new file mode 100644
index 0000000000..bc6cf6cabe
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_client.cpp
@@ -0,0 +1,76 @@
+#include "include/private/dvr/vsync_client.h"
+
+#include <log/log.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/display_protocol.h>
+
+using android::dvr::display::VSyncProtocol;
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+VSyncClient::VSyncClient(long timeout_ms)
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ VSyncProtocol::kClientPath),
+ timeout_ms) {}
+
+VSyncClient::VSyncClient()
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ VSyncProtocol::kClientPath)) {}
+
+int VSyncClient::Wait(int64_t* timestamp_ns) {
+ auto status = InvokeRemoteMethod<VSyncProtocol::Wait>();
+ if (!status) {
+ ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ if (timestamp_ns != nullptr) {
+ *timestamp_ns = status.get();
+ }
+ return 0;
+}
+
+int VSyncClient::GetFd() { return event_fd(); }
+
+int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) {
+ auto status = InvokeRemoteMethod<VSyncProtocol::GetLastTimestamp>();
+ if (!status) {
+ ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+ *timestamp_ns = status.get();
+ return 0;
+}
+
+int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns,
+ uint32_t* next_vsync_count) {
+ if (!vsync_period_ns || !timestamp_ns || !next_vsync_count)
+ return -EINVAL;
+
+ auto status = InvokeRemoteMethod<VSyncProtocol::GetSchedInfo>();
+ if (!status) {
+ ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ *vsync_period_ns = status.get().vsync_period_ns;
+ *timestamp_ns = status.get().timestamp_ns;
+ *next_vsync_count = status.get().next_vsync_count;
+ return 0;
+}
+
+int VSyncClient::Acknowledge() {
+ auto status = InvokeRemoteMethod<VSyncProtocol::Acknowledge>();
+ ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
new file mode 100644
index 0000000000..fa78b1c136
--- /dev/null
+++ b/libs/vr/libdvr/Android.bp
@@ -0,0 +1,89 @@
+// Copyright (C) 2017 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_library_headers {
+ name: "libdvr_headers",
+ owner: "google",
+ export_include_dirs: ["include"],
+}
+
+cflags = [
+ "-DLOG_TAG=\"libdvr\"",
+]
+
+srcs = [
+ "dvr_api.cpp",
+ "dvr_buffer.cpp",
+ "dvr_buffer_queue.cpp",
+ "dvr_display_manager.cpp",
+ "dvr_hardware_composer_client.cpp",
+ "dvr_surface.cpp",
+ "dvr_vsync.cpp",
+]
+
+static_libs = [
+ "libbufferhub",
+ "libbufferhubqueue",
+ "libdisplay",
+ "libvrsensor",
+ "libvirtualtouchpadclient",
+ "libvr_hwc-impl",
+ "libvr_hwc-binder",
+ "libgrallocusage",
+ "libpdx_default_transport",
+]
+
+shared_libs = [
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hidl.token@1.0-utils",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libcutils",
+ "libutils",
+ "libnativewindow",
+ "libgui",
+ "libui",
+]
+
+cc_library_shared {
+ name: "libdvr",
+ owner: "google",
+ cflags: cflags,
+ header_libs: ["libdvr_headers"],
+ export_header_lib_headers: ["libdvr_headers"],
+ srcs: srcs,
+ static_libs: static_libs,
+ shared_libs: shared_libs,
+ version_script: "exported_apis.lds",
+}
+
+// Also build a static libdvr for linking into tests. The linker script
+// restricting function access in the shared lib makes it inconvenient to use in
+// test code.
+cc_library_static {
+ name: "libdvr_static",
+ owner: "google",
+ cflags: cflags,
+ header_libs: ["libdvr_headers"],
+ export_header_lib_headers: ["libdvr_headers"],
+ srcs: srcs,
+ static_libs: static_libs,
+ shared_libs: shared_libs,
+}
+
+subdirs = [
+ "tests",
+]
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
new file mode 100644
index 0000000000..2c95583d20
--- /dev/null
+++ b/libs/vr/libdvr/dvr_api.cpp
@@ -0,0 +1,47 @@
+#include "include/dvr/dvr_api.h"
+
+#include <errno.h>
+#include <utils/Log.h>
+
+// Headers from libdvr
+#include <dvr/dvr_buffer.h>
+#include <dvr/dvr_buffer_queue.h>
+#include <dvr/dvr_display_manager.h>
+#include <dvr/dvr_surface.h>
+#include <dvr/dvr_vsync.h>
+
+// Headers not yet moved into libdvr.
+// TODO(jwcai) Move these once their callers are moved into Google3.
+#include <dvr/dvr_hardware_composer_client.h>
+#include <dvr/pose_client.h>
+#include <dvr/virtual_touchpad_client.h>
+
+extern "C" {
+
+int dvrGetApi(void* api, size_t struct_size, int version) {
+ ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size,
+ version);
+ if (version == 1) {
+ if (struct_size != sizeof(DvrApi_v1)) {
+ ALOGE("dvrGetApi: Size mismatch: expected %zu; actual %zu",
+ sizeof(DvrApi_v1), struct_size);
+ return -EINVAL;
+ }
+ DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api);
+
+// Defines an API entry for V1 (no version suffix).
+#define DVR_V1_API_ENTRY(name) dvr_api->name = dvr##name
+
+#include "include/dvr/dvr_api_entries.h"
+
+// Undefine macro definitions to play nice with Google3 style rules.
+#undef DVR_V1_API_ENTRY
+
+ return 0;
+ }
+
+ ALOGE("dvrGetApi: Unknown API version=%d", version);
+ return -EINVAL;
+}
+
+} // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
new file mode 100644
index 0000000000..82469b81bf
--- /dev/null
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -0,0 +1,202 @@
+#include "include/dvr/dvr_buffer.h"
+
+#include <android/hardware_buffer.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <ui/GraphicBuffer.h>
+
+#include "dvr_internal.h"
+
+using namespace android;
+
+namespace android {
+namespace dvr {
+
+DvrBuffer* CreateDvrBufferFromIonBuffer(
+ const std::shared_ptr<IonBuffer>& ion_buffer) {
+ if (!ion_buffer)
+ return nullptr;
+ return new DvrBuffer{std::move(ion_buffer)};
+}
+
+} // namespace dvr
+} // namespace android
+
+namespace {
+
+int ConvertToAHardwareBuffer(GraphicBuffer* graphic_buffer,
+ AHardwareBuffer** hardware_buffer) {
+ if (!hardware_buffer || !graphic_buffer) {
+ return -EINVAL;
+ }
+ *hardware_buffer = reinterpret_cast<AHardwareBuffer*>(graphic_buffer);
+ AHardwareBuffer_acquire(*hardware_buffer);
+ return 0;
+}
+
+} // anonymous namespace
+
+extern "C" {
+
+void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer) {
+ if (write_buffer)
+ *write_buffer = new DvrWriteBuffer;
+}
+
+void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) {
+ delete write_buffer;
+}
+
+int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) {
+ return write_buffer && write_buffer->write_buffer;
+}
+
+int dvrWriteBufferClear(DvrWriteBuffer* write_buffer) {
+ if (!write_buffer)
+ return -EINVAL;
+
+ write_buffer->write_buffer = nullptr;
+ return 0;
+}
+
+int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) {
+ if (!write_buffer || !write_buffer->write_buffer)
+ return -EINVAL;
+
+ return write_buffer->write_buffer->id();
+}
+
+int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
+ AHardwareBuffer** hardware_buffer) {
+ if (!write_buffer || !write_buffer->write_buffer)
+ return -EINVAL;
+
+ return ConvertToAHardwareBuffer(
+ write_buffer->write_buffer->buffer()->buffer().get(), hardware_buffer);
+}
+
+int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd,
+ const void* meta, size_t meta_size_bytes) {
+ if (!write_buffer || !write_buffer->write_buffer)
+ return -EINVAL;
+
+ pdx::LocalHandle fence(ready_fence_fd);
+ int result = write_buffer->write_buffer->Post(fence, meta, meta_size_bytes);
+ return result;
+}
+
+int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd) {
+ if (!write_buffer || !write_buffer->write_buffer || !release_fence_fd)
+ return -EINVAL;
+
+ pdx::LocalHandle release_fence;
+ int result = write_buffer->write_buffer->Gain(&release_fence);
+ *release_fence_fd = release_fence.Release();
+ return result;
+}
+
+int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer) {
+ if (!write_buffer || !write_buffer->write_buffer)
+ return -EINVAL;
+
+ return write_buffer->write_buffer->GainAsync();
+}
+
+void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer) {
+ if (read_buffer)
+ *read_buffer = new DvrReadBuffer;
+}
+
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { delete read_buffer; }
+
+int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) {
+ return read_buffer && read_buffer->read_buffer;
+}
+
+int dvrReadBufferClear(DvrReadBuffer* read_buffer) {
+ if (!read_buffer)
+ return -EINVAL;
+
+ read_buffer->read_buffer = nullptr;
+ return 0;
+}
+
+int dvrReadBufferGetId(DvrReadBuffer* read_buffer) {
+ if (!read_buffer || !read_buffer->read_buffer)
+ return -EINVAL;
+
+ return read_buffer->read_buffer->id();
+}
+
+int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
+ AHardwareBuffer** hardware_buffer) {
+ if (!read_buffer || !read_buffer->read_buffer)
+ return -EINVAL;
+
+ return ConvertToAHardwareBuffer(
+ read_buffer->read_buffer->buffer()->buffer().get(), hardware_buffer);
+}
+
+int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd,
+ void* meta, size_t meta_size_bytes) {
+ if (!read_buffer || !read_buffer->read_buffer)
+ return -EINVAL;
+
+ pdx::LocalHandle ready_fence;
+ int result =
+ read_buffer->read_buffer->Acquire(&ready_fence, meta, meta_size_bytes);
+ *ready_fence_fd = ready_fence.Release();
+ return result;
+}
+
+int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd) {
+ if (!read_buffer || !read_buffer->read_buffer)
+ return -EINVAL;
+
+ pdx::LocalHandle fence(release_fence_fd);
+ int result = read_buffer->read_buffer->Release(fence);
+ return result;
+}
+
+int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer) {
+ if (!read_buffer || !read_buffer->read_buffer)
+ return -EINVAL;
+
+ return read_buffer->read_buffer->ReleaseAsync();
+}
+
+void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; }
+
+int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
+ AHardwareBuffer** hardware_buffer) {
+ if (!buffer || !buffer->buffer || !hardware_buffer) {
+ return -EINVAL;
+ }
+
+ return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(),
+ hardware_buffer);
+}
+
+const struct native_handle* dvrWriteBufferGetNativeHandle(
+ DvrWriteBuffer* write_buffer) {
+ if (!write_buffer || !write_buffer->write_buffer)
+ return nullptr;
+
+ return write_buffer->write_buffer->native_handle();
+}
+
+const struct native_handle* dvrReadBufferGetNativeHandle(
+ DvrReadBuffer* read_buffer) {
+ if (!read_buffer || !read_buffer->read_buffer)
+ return nullptr;
+
+ return read_buffer->read_buffer->native_handle();
+}
+
+const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer) {
+ if (!buffer || !buffer->buffer)
+ return nullptr;
+
+ return buffer->buffer->handle();
+}
+
+} // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
new file mode 100644
index 0000000000..f668510304
--- /dev/null
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -0,0 +1,204 @@
+#include "include/dvr/dvr_api.h"
+#include "include/dvr/dvr_buffer_queue.h"
+
+#include <android/native_window.h>
+#include <gui/Surface.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/buffer_hub_queue_producer.h>
+
+#include "dvr_internal.h"
+
+#define CHECK_PARAM(param) \
+ LOG_ALWAYS_FATAL_IF(param == nullptr, "%s: " #param "cannot be NULL.", \
+ __FUNCTION__)
+
+using namespace android;
+
+namespace android {
+namespace dvr {
+
+DvrWriteBufferQueue* CreateDvrWriteBufferQueueFromProducerQueue(
+ const std::shared_ptr<dvr::ProducerQueue>& producer_queue) {
+ return new DvrWriteBufferQueue{std::move(producer_queue)};
+}
+
+DvrReadBufferQueue* CreateDvrReadBufferQueueFromConsumerQueue(
+ const std::shared_ptr<dvr::ConsumerQueue>& consumer_queue) {
+ return new DvrReadBufferQueue{std::move(consumer_queue)};
+}
+
+dvr::ProducerQueue* GetProducerQueueFromDvrWriteBufferQueue(
+ DvrWriteBufferQueue* write_queue) {
+ return write_queue->producer_queue.get();
+}
+
+} // namespace dvr
+} // namespace android
+
+extern "C" {
+
+void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) {
+ if (write_queue != nullptr && write_queue->native_window != nullptr)
+ ANativeWindow_release(write_queue->native_window);
+
+ delete write_queue;
+}
+
+ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) {
+ if (!write_queue || !write_queue->producer_queue)
+ return -EINVAL;
+
+ return write_queue->producer_queue->capacity();
+}
+
+int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) {
+ if (!write_queue)
+ return -EINVAL;
+
+ return write_queue->producer_queue->id();
+}
+
+int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
+ ANativeWindow** out_window) {
+ if (!write_queue || !out_window)
+ return -EINVAL;
+
+ if (write_queue->producer_queue->metadata_size() !=
+ sizeof(DvrNativeBufferMetadata)) {
+ ALOGE(
+ "The size of buffer metadata (%zu) of the write queue does not match "
+ "of size of DvrNativeBufferMetadata (%zu).",
+ write_queue->producer_queue->metadata_size(),
+ sizeof(DvrNativeBufferMetadata));
+ return -EINVAL;
+ }
+
+ // Lazy creation of |native_window|.
+ if (write_queue->native_window == nullptr) {
+ sp<IGraphicBufferProducer> gbp =
+ dvr::BufferHubQueueProducer::Create(write_queue->producer_queue);
+ sp<Surface> surface = new Surface(gbp, true);
+ write_queue->native_window = static_cast<ANativeWindow*>(surface.get());
+ ANativeWindow_acquire(write_queue->native_window);
+ }
+
+ *out_window = write_queue->native_window;
+ return 0;
+}
+
+int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
+ DvrReadBufferQueue** out_read_queue) {
+ if (!write_queue || !write_queue->producer_queue || !out_read_queue)
+ return -EINVAL;
+
+ auto read_queue = std::make_unique<DvrReadBufferQueue>();
+ read_queue->consumer_queue =
+ write_queue->producer_queue->CreateConsumerQueue();
+ if (read_queue->consumer_queue == nullptr) {
+ ALOGE(
+ "dvrWriteBufferQueueCreateReadQueue: Failed to create consumer queue "
+ "from DvrWriteBufferQueue[%p].",
+ write_queue);
+ return -ENOMEM;
+ }
+
+ *out_read_queue = read_queue.release();
+ return 0;
+}
+
+int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
+ DvrWriteBuffer* write_buffer,
+ int* out_fence_fd) {
+ if (!write_queue || !write_queue->producer_queue || !write_buffer ||
+ !out_fence_fd) {
+ return -EINVAL;
+ }
+
+ size_t slot;
+ pdx::LocalHandle release_fence;
+ auto buffer_status =
+ write_queue->producer_queue->Dequeue(timeout, &slot, &release_fence);
+ if (!buffer_status) {
+ ALOGE_IF(buffer_status.error() != ETIMEDOUT,
+ "dvrWriteBufferQueueDequeue: Failed to dequeue buffer: %s",
+ buffer_status.GetErrorMessage().c_str());
+ return -buffer_status.error();
+ }
+
+ write_buffer->write_buffer = buffer_status.take();
+ *out_fence_fd = release_fence.Release();
+ return 0;
+}
+
+// ReadBufferQueue
+void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) {
+ delete read_queue;
+}
+
+ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) {
+ if (!read_queue)
+ return -EINVAL;
+
+ return read_queue->consumer_queue->capacity();
+}
+
+int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) {
+ if (!read_queue)
+ return -EINVAL;
+
+ return read_queue->consumer_queue->id();
+}
+
+int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
+ DvrReadBufferQueue** out_read_queue) {
+ if (!read_queue || !read_queue->consumer_queue || !out_read_queue)
+ return -EINVAL;
+
+ auto new_read_queue = std::make_unique<DvrReadBufferQueue>();
+ new_read_queue->consumer_queue =
+ read_queue->consumer_queue->CreateConsumerQueue();
+ if (new_read_queue->consumer_queue == nullptr) {
+ ALOGE(
+ "dvrReadBufferQueueCreateReadQueue: Failed to create consumer queue "
+ "from DvrReadBufferQueue[%p].",
+ read_queue);
+ return -ENOMEM;
+ }
+
+ *out_read_queue = new_read_queue.release();
+ return 0;
+}
+
+int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
+ DvrReadBuffer* read_buffer, int* out_fence_fd,
+ void* out_meta, size_t meta_size_bytes) {
+ if (!read_queue || !read_queue->consumer_queue || !read_buffer ||
+ !out_fence_fd || !out_meta) {
+ return -EINVAL;
+ }
+
+ if (meta_size_bytes != read_queue->consumer_queue->metadata_size()) {
+ ALOGE(
+ "dvrReadBufferQueueDequeue: Invalid metadata size, expected (%zu), "
+ "but actual (%zu).",
+ read_queue->consumer_queue->metadata_size(), meta_size_bytes);
+ return -EINVAL;
+ }
+
+ size_t slot;
+ pdx::LocalHandle acquire_fence;
+ auto buffer_status = read_queue->consumer_queue->Dequeue(
+ timeout, &slot, out_meta, meta_size_bytes, &acquire_fence);
+ if (!buffer_status) {
+ ALOGE_IF(buffer_status.error() != ETIMEDOUT,
+ "dvrReadBufferQueueDequeue: Failed to dequeue buffer: %s",
+ buffer_status.GetErrorMessage().c_str());
+ return -buffer_status.error();
+ }
+
+ read_buffer->read_buffer = buffer_status.take();
+ *out_fence_fd = acquire_fence.Release();
+ return 0;
+}
+
+} // extern "C"
diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp
new file mode 100644
index 0000000000..87636ecd91
--- /dev/null
+++ b/libs/vr/libdvr/dvr_display_manager.cpp
@@ -0,0 +1,304 @@
+#include "include/dvr/dvr_display_manager.h"
+
+#include <dvr/dvr_buffer.h>
+#include <pdx/rpc/variant.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_manager_client.h>
+
+#include "dvr_internal.h"
+
+using android::AHardwareBuffer_convertToGrallocUsageBits;
+using android::dvr::BufferConsumer;
+using android::dvr::display::DisplayManagerClient;
+using android::dvr::display::SurfaceAttributes;
+using android::dvr::display::SurfaceAttribute;
+using android::dvr::display::SurfaceState;
+using android::dvr::CreateDvrReadBufferQueueFromConsumerQueue;
+using android::pdx::rpc::EmptyVariant;
+
+namespace {
+
+// Extracts type and value from the attribute Variant and writes them into the
+// respective fields of DvrSurfaceAttribute.
+struct AttributeVisitor {
+ DvrSurfaceAttribute* attribute;
+
+ void operator()(int32_t value) {
+ attribute->value.int32_value = value;
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
+ }
+ void operator()(int64_t value) {
+ attribute->value.int64_value = value;
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64;
+ }
+ void operator()(bool value) {
+ attribute->value.bool_value = value;
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
+ }
+ void operator()(float value) {
+ attribute->value.float_value = value;
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT;
+ }
+ void operator()(const std::array<float, 2>& value) {
+ std::copy(value.cbegin(), value.cend(), attribute->value.float2_value);
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2;
+ }
+ void operator()(const std::array<float, 3>& value) {
+ std::copy(value.cbegin(), value.cend(), attribute->value.float3_value);
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3;
+ }
+ void operator()(const std::array<float, 4>& value) {
+ std::copy(value.cbegin(), value.cend(), attribute->value.float4_value);
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4;
+ }
+ void operator()(const std::array<float, 8>& value) {
+ std::copy(value.cbegin(), value.cend(), attribute->value.float8_value);
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8;
+ }
+ void operator()(const std::array<float, 16>& value) {
+ std::copy(value.cbegin(), value.cend(), attribute->value.float16_value);
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16;
+ }
+ void operator()(EmptyVariant) {
+ attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
+ }
+};
+
+size_t ConvertSurfaceAttributes(const SurfaceAttributes& surface_attributes,
+ DvrSurfaceAttribute* attributes,
+ size_t max_count) {
+ size_t count = 0;
+ for (const auto& attribute : surface_attributes) {
+ if (count >= max_count)
+ break;
+
+ // Copy the key and extract the Variant value using a visitor.
+ attributes[count].key = attribute.first;
+ attribute.second.Visit(AttributeVisitor{&attributes[count]});
+ count++;
+ }
+
+ return count;
+}
+
+} // anonymous namespace
+
+extern "C" {
+
+struct DvrDisplayManager {
+ std::unique_ptr<DisplayManagerClient> client;
+};
+
+struct DvrSurfaceState {
+ std::vector<SurfaceState> state;
+};
+
+int dvrDisplayManagerCreate(DvrDisplayManager** client_out) {
+ if (!client_out)
+ return -EINVAL;
+
+ auto client = DisplayManagerClient::Create();
+ if (!client) {
+ ALOGE("dvrDisplayManagerCreate: Failed to create display manager client!");
+ return -EIO;
+ }
+
+ *client_out = new DvrDisplayManager{std::move(client)};
+ return 0;
+}
+
+void dvrDisplayManagerDestroy(DvrDisplayManager* client) { delete client; }
+
+int dvrDisplayManagerSetupNamedBuffer(DvrDisplayManager* client,
+ const char* name, size_t size,
+ uint64_t usage, DvrBuffer** buffer_out) {
+ if (!client || !name || !buffer_out)
+ return -EINVAL;
+
+ uint64_t gralloc_usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
+
+ auto buffer_status =
+ client->client->SetupNamedBuffer(name, size, gralloc_usage);
+ if (!buffer_status) {
+ ALOGE("dvrDisplayManagerSetupPoseBuffer: Failed to setup named buffer: %s",
+ buffer_status.GetErrorMessage().c_str());
+ return -buffer_status.error();
+ }
+
+ *buffer_out = CreateDvrBufferFromIonBuffer(buffer_status.take());
+ return 0;
+}
+
+int dvrDisplayManagerGetEventFd(DvrDisplayManager* client) {
+ if (!client)
+ return -EINVAL;
+
+ return client->client->event_fd();
+}
+
+int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client,
+ int in_events, int* out_events) {
+ if (!client || !out_events)
+ return -EINVAL;
+
+ auto status = client->client->GetEventMask(in_events);
+ if (!status)
+ return -status.error();
+
+ *out_events = status.get();
+ return 0;
+}
+
+int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client,
+ DvrSurfaceState* state) {
+ if (!client || !state)
+ return -EINVAL;
+
+ auto status = client->client->GetSurfaceState();
+ if (!status)
+ return -status.error();
+
+ state->state = status.take();
+ return 0;
+}
+
+int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client,
+ int surface_id, int queue_id,
+ DvrReadBufferQueue** queue_out) {
+ if (!client || !queue_out)
+ return -EINVAL;
+
+ auto status = client->client->GetSurfaceQueue(surface_id, queue_id);
+ if (!status) {
+ ALOGE("dvrDisplayManagerGetReadBufferQueue: Failed to get queue: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ *queue_out = CreateDvrReadBufferQueueFromConsumerQueue(status.take());
+ return 0;
+}
+
+int dvrSurfaceStateCreate(DvrSurfaceState** surface_state_out) {
+ if (!surface_state_out)
+ return -EINVAL;
+
+ *surface_state_out = new DvrSurfaceState{};
+ return 0;
+}
+
+void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state) {
+ delete surface_state;
+}
+
+int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state,
+ size_t* count_out) {
+ if (!surface_state)
+ return -EINVAL;
+
+ *count_out = surface_state->state.size();
+ return 0;
+}
+
+int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ DvrSurfaceUpdateFlags* flags_out) {
+ if (!surface_state || surface_index >= surface_state->state.size())
+ return -EINVAL;
+
+ *flags_out = surface_state->state[surface_index].update_flags;
+ return 0;
+}
+
+int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state,
+ size_t surface_index, int* surface_id_out) {
+ if (!surface_state || surface_index >= surface_state->state.size())
+ return -EINVAL;
+
+ *surface_id_out = surface_state->state[surface_index].surface_id;
+ return 0;
+}
+
+int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state,
+ size_t surface_index, int* process_id_out) {
+ if (!surface_state || surface_index >= surface_state->state.size())
+ return -EINVAL;
+
+ *process_id_out = surface_state->state[surface_index].process_id;
+ return 0;
+}
+
+int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state,
+ size_t surface_index, size_t* count_out) {
+ if (!surface_state || surface_index >= surface_state->state.size())
+ return -EINVAL;
+
+ *count_out = surface_state->state[surface_index].queue_ids.size();
+ return 0;
+}
+
+ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state,
+ size_t surface_index, int* queue_ids,
+ size_t max_count) {
+ if (!surface_state || surface_index >= surface_state->state.size())
+ return -EINVAL;
+
+ size_t i;
+ const auto& state = surface_state->state[surface_index];
+ for (i = 0; i < std::min(max_count, state.queue_ids.size()); i++) {
+ queue_ids[i] = state.queue_ids[i];
+ }
+
+ return i;
+}
+
+int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state,
+ size_t surface_index, int* z_order_out) {
+ if (!surface_state || surface_index >= surface_state->state.size() ||
+ !z_order_out) {
+ return -EINVAL;
+ }
+
+ *z_order_out = surface_state->state[surface_index].GetZOrder();
+ return 0;
+}
+
+int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state,
+ size_t surface_index, bool* visible_out) {
+ if (!surface_state || surface_index >= surface_state->state.size() ||
+ !visible_out) {
+ return -EINVAL;
+ }
+
+ *visible_out = surface_state->state[surface_index].GetVisible();
+ return 0;
+}
+
+int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state,
+ size_t surface_index, size_t* count_out) {
+ if (!surface_state || surface_index >= surface_state->state.size() ||
+ !count_out) {
+ return -EINVAL;
+ }
+
+ *count_out = surface_state->state[surface_index].surface_attributes.size();
+ return 0;
+}
+
+ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ DvrSurfaceAttribute* attributes,
+ size_t max_count) {
+ if (!surface_state || surface_index >= surface_state->state.size() ||
+ !attributes) {
+ return -EINVAL;
+ }
+
+ return ConvertSurfaceAttributes(
+ surface_state->state[surface_index].surface_attributes, attributes,
+ max_count);
+}
+
+} // extern "C"
diff --git a/libs/vr/libdvr/dvr_hardware_composer_client.cpp b/libs/vr/libdvr/dvr_hardware_composer_client.cpp
new file mode 100644
index 0000000000..d3ae299cb9
--- /dev/null
+++ b/libs/vr/libdvr/dvr_hardware_composer_client.cpp
@@ -0,0 +1,237 @@
+#include "include/dvr/dvr_hardware_composer_client.h"
+
+#include <android/dvr/IVrComposer.h>
+#include <android/dvr/BnVrComposerCallback.h>
+#include <android/hardware_buffer.h>
+#include <binder/IServiceManager.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
+#include <memory>
+
+struct DvrHwcFrame {
+ android::dvr::ComposerView::Frame frame;
+};
+
+namespace {
+
+class HwcCallback : public android::dvr::BnVrComposerCallback {
+ public:
+ explicit HwcCallback(DvrHwcOnFrameCallback callback,
+ void* client_state);
+ ~HwcCallback() override;
+
+ std::unique_ptr<DvrHwcFrame> DequeueFrame();
+
+ private:
+ // android::dvr::BnVrComposerCallback:
+ android::binder::Status onNewFrame(
+ const android::dvr::ParcelableComposerFrame& frame,
+ android::dvr::ParcelableUniqueFd* fence) override;
+
+ DvrHwcOnFrameCallback callback_;
+ void* client_state_;
+
+ HwcCallback(const HwcCallback&) = delete;
+ void operator=(const HwcCallback&) = delete;
+};
+
+HwcCallback::HwcCallback(DvrHwcOnFrameCallback callback, void* client_state)
+ : callback_(callback), client_state_(client_state) {}
+
+HwcCallback::~HwcCallback() {}
+
+android::binder::Status HwcCallback::onNewFrame(
+ const android::dvr::ParcelableComposerFrame& frame,
+ android::dvr::ParcelableUniqueFd* fence) {
+ std::unique_ptr<DvrHwcFrame> dvr_frame(new DvrHwcFrame());
+ dvr_frame->frame = frame.frame();
+
+ fence->set_fence(android::base::unique_fd(callback_(client_state_,
+ dvr_frame.release())));
+ return android::binder::Status::ok();
+}
+
+} // namespace
+
+struct DvrHwcClient {
+ android::sp<android::dvr::IVrComposer> composer;
+ android::sp<HwcCallback> callback;
+};
+
+DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, void* data) {
+ std::unique_ptr<DvrHwcClient> client(new DvrHwcClient());
+
+ android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+ client->composer = android::interface_cast<android::dvr::IVrComposer>(
+ sm->getService(android::dvr::IVrComposer::SERVICE_NAME()));
+ if (!client->composer.get())
+ return nullptr;
+
+ client->callback = new HwcCallback(callback, data);
+ android::binder::Status status = client->composer->registerObserver(
+ client->callback);
+ if (!status.isOk())
+ return nullptr;
+
+ return client.release();
+}
+
+void dvrHwcClientDestroy(DvrHwcClient* client) {
+ delete client;
+}
+
+void dvrHwcFrameDestroy(DvrHwcFrame* frame) {
+ delete frame;
+}
+
+DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame) {
+ return frame->frame.display_id;
+}
+
+int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame) {
+ return frame->frame.display_width;
+}
+
+int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame) {
+ return frame->frame.display_height;
+}
+
+bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame) {
+ return frame->frame.removed;
+}
+
+size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame) {
+ return frame->frame.layers.size();
+}
+
+uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame) {
+ return static_cast<uint32_t>(frame->frame.active_config);
+}
+
+uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame) {
+ return static_cast<uint32_t>(frame->frame.color_mode);
+}
+
+void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix,
+ int32_t* out_hint) {
+ *out_hint = frame->frame.color_transform_hint;
+ memcpy(out_matrix, frame->frame.color_transform,
+ sizeof(frame->frame.color_transform));
+}
+
+uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame) {
+ return static_cast<uint32_t>(frame->frame.power_mode);
+}
+
+uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame) {
+ return static_cast<uint32_t>(frame->frame.vsync_enabled);
+}
+
+DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index) {
+ return frame->frame.layers[layer_index].id;
+}
+
+AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame,
+ size_t layer_index) {
+ AHardwareBuffer* buffer = android::AHardwareBuffer_from_GraphicBuffer(
+ frame->frame.layers[layer_index].buffer.get());
+ AHardwareBuffer_acquire(buffer);
+ return buffer;
+}
+
+int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index) {
+ return frame->frame.layers[layer_index].fence->dup();
+}
+
+DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame,
+ size_t layer_index) {
+ return DvrHwcRecti{
+ frame->frame.layers[layer_index].display_frame.left,
+ frame->frame.layers[layer_index].display_frame.top,
+ frame->frame.layers[layer_index].display_frame.right,
+ frame->frame.layers[layer_index].display_frame.bottom,
+ };
+}
+
+DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index) {
+ return DvrHwcRectf{
+ frame->frame.layers[layer_index].crop.left,
+ frame->frame.layers[layer_index].crop.top,
+ frame->frame.layers[layer_index].crop.right,
+ frame->frame.layers[layer_index].crop.bottom,
+ };
+}
+
+DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame,
+ size_t layer_index) {
+ return static_cast<DvrHwcBlendMode>(
+ frame->frame.layers[layer_index].blend_mode);
+}
+
+float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index) {
+ return frame->frame.layers[layer_index].alpha;
+}
+
+uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index) {
+ return frame->frame.layers[layer_index].type;
+}
+
+uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame,
+ size_t layer_index) {
+ return frame->frame.layers[layer_index].app_id;
+}
+
+uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index) {
+ return frame->frame.layers[layer_index].z_order;
+}
+
+void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index,
+ int32_t* out_x, int32_t* out_y) {
+ *out_x = frame->frame.layers[layer_index].cursor_x;
+ *out_y = frame->frame.layers[layer_index].cursor_y;
+}
+
+uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index) {
+ return frame->frame.layers[layer_index].transform;
+}
+
+uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index) {
+ return frame->frame.layers[layer_index].dataspace;
+}
+
+uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index) {
+ const auto& color = frame->frame.layers[layer_index].color;
+ return color.r | (static_cast<uint32_t>(color.g) << 8) |
+ (static_cast<uint32_t>(color.b) << 16) |
+ (static_cast<uint32_t>(color.a) << 24);
+}
+
+uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame,
+ size_t layer_index) {
+ return frame->frame.layers[layer_index].visible_regions.size();
+}
+
+DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame,
+ size_t layer_index, size_t index) {
+ return DvrHwcRecti{
+ frame->frame.layers[layer_index].visible_regions[index].left,
+ frame->frame.layers[layer_index].visible_regions[index].top,
+ frame->frame.layers[layer_index].visible_regions[index].right,
+ frame->frame.layers[layer_index].visible_regions[index].bottom,
+ };
+}
+
+uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame,
+ size_t layer_index) {
+ return frame->frame.layers[layer_index].damaged_regions.size();
+}
+
+DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame,
+ size_t layer_index, size_t index) {
+ return DvrHwcRecti{
+ frame->frame.layers[layer_index].damaged_regions[index].left,
+ frame->frame.layers[layer_index].damaged_regions[index].top,
+ frame->frame.layers[layer_index].damaged_regions[index].right,
+ frame->frame.layers[layer_index].damaged_regions[index].bottom,
+ };
+}
diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h
new file mode 100644
index 0000000000..89bef09871
--- /dev/null
+++ b/libs/vr/libdvr/dvr_internal.h
@@ -0,0 +1,70 @@
+#ifndef ANDROID_DVR_INTERNAL_H_
+#define ANDROID_DVR_INTERNAL_H_
+
+#include <sys/cdefs.h>
+
+#include <memory>
+
+extern "C" {
+
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
+} // extern "C"
+
+namespace android {
+namespace dvr {
+
+class BufferProducer;
+class BufferConsumer;
+class ConsumerQueue;
+class IonBuffer;
+class ProducerQueue;
+
+DvrBuffer* CreateDvrBufferFromIonBuffer(
+ const std::shared_ptr<IonBuffer>& ion_buffer);
+
+DvrReadBuffer* CreateDvrReadBufferFromBufferConsumer(
+ const std::shared_ptr<BufferConsumer>& buffer_consumer);
+DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer(
+ const std::shared_ptr<BufferProducer>& buffer_producer);
+
+DvrReadBufferQueue* CreateDvrReadBufferQueueFromConsumerQueue(
+ const std::shared_ptr<ConsumerQueue>& consumer_queue);
+DvrWriteBufferQueue* CreateDvrWriteBufferQueueFromProducerQueue(
+ const std::shared_ptr<ProducerQueue>& producer_queue);
+ProducerQueue* GetProducerQueueFromDvrWriteBufferQueue(
+ DvrWriteBufferQueue* write_queue);
+
+} // namespace dvr
+} // namespace android
+
+extern "C" {
+
+struct DvrWriteBuffer {
+ std::shared_ptr<android::dvr::BufferProducer> write_buffer;
+};
+
+struct DvrReadBuffer {
+ std::shared_ptr<android::dvr::BufferConsumer> read_buffer;
+};
+
+struct DvrBuffer {
+ std::shared_ptr<android::dvr::IonBuffer> buffer;
+};
+
+struct DvrWriteBufferQueue {
+ std::shared_ptr<android::dvr::ProducerQueue> producer_queue;
+ ANativeWindow* native_window{nullptr};
+};
+
+struct DvrReadBufferQueue {
+ std::shared_ptr<android::dvr::ConsumerQueue> consumer_queue;
+};
+
+} // extern "C"
+
+#endif // ANDROID_DVR_INTERNAL_H_
diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
new file mode 100644
index 0000000000..67e2ae88ac
--- /dev/null
+++ b/libs/vr/libdvr/dvr_surface.cpp
@@ -0,0 +1,182 @@
+#include "include/dvr/dvr_surface.h"
+
+#include <inttypes.h>
+
+#include <private/dvr/display_client.h>
+
+#include "dvr_internal.h"
+
+using android::dvr::display::DisplayClient;
+using android::dvr::display::Surface;
+using android::dvr::display::SurfaceAttributes;
+using android::dvr::display::SurfaceAttributeValue;
+using android::dvr::CreateDvrReadBufferFromBufferConsumer;
+using android::dvr::CreateDvrWriteBufferQueueFromProducerQueue;
+
+namespace {
+
+bool ConvertSurfaceAttributes(const DvrSurfaceAttribute* attributes,
+ size_t attribute_count,
+ SurfaceAttributes* surface_attributes,
+ size_t* error_index) {
+ for (size_t i = 0; i < attribute_count; i++) {
+ SurfaceAttributeValue value;
+ switch (attributes[i].value.type) {
+ case DVR_SURFACE_ATTRIBUTE_TYPE_INT32:
+ value = attributes[i].value.int32_value;
+ break;
+ case DVR_SURFACE_ATTRIBUTE_TYPE_INT64:
+ value = attributes[i].value.int64_value;
+ break;
+ case DVR_SURFACE_ATTRIBUTE_TYPE_BOOL:
+ value = attributes[i].value.bool_value;
+ break;
+ case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT:
+ value = attributes[i].value.float_value;
+ break;
+ case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2:
+ value = attributes[i].value.float2_value;
+ break;
+ case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3:
+ value = attributes[i].value.float3_value;
+ break;
+ case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4:
+ value = attributes[i].value.float4_value;
+ break;
+ case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8:
+ value = attributes[i].value.float8_value;
+ break;
+ case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16:
+ value = attributes[i].value.float16_value;
+ break;
+ default:
+ *error_index = i;
+ return false;
+ }
+
+ surface_attributes->emplace(attributes[i].key, value);
+ }
+
+ return true;
+}
+
+} // anonymous namespace
+
+extern "C" {
+
+struct DvrSurface {
+ std::unique_ptr<Surface> surface;
+};
+
+int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes,
+ size_t attribute_count, DvrSurface** out_surface) {
+ if (out_surface == nullptr) {
+ ALOGE("dvrSurfaceCreate: Invalid inputs: out_surface=%p.", out_surface);
+ return -EINVAL;
+ }
+
+ size_t error_index;
+ SurfaceAttributes surface_attributes;
+ if (!ConvertSurfaceAttributes(attributes, attribute_count,
+ &surface_attributes, &error_index)) {
+ ALOGE("dvrSurfaceCreate: Invalid surface attribute type: %" PRIu64,
+ attributes[error_index].value.type);
+ return -EINVAL;
+ }
+
+ auto status = Surface::CreateSurface(surface_attributes);
+ if (!status) {
+ ALOGE("dvrSurfaceCreate:: Failed to create display surface: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ *out_surface = new DvrSurface{status.take()};
+ return 0;
+}
+
+void dvrSurfaceDestroy(DvrSurface* surface) { delete surface; }
+
+int dvrSurfaceGetId(DvrSurface* surface) {
+ return surface->surface->surface_id();
+}
+
+int dvrSurfaceSetAttributes(DvrSurface* surface,
+ const DvrSurfaceAttribute* attributes,
+ size_t attribute_count) {
+ if (surface == nullptr || attributes == nullptr) {
+ ALOGE(
+ "dvrSurfaceSetAttributes: Invalid inputs: surface=%p attributes=%p "
+ "attribute_count=%zu",
+ surface, attributes, attribute_count);
+ return -EINVAL;
+ }
+
+ size_t error_index;
+ SurfaceAttributes surface_attributes;
+ if (!ConvertSurfaceAttributes(attributes, attribute_count,
+ &surface_attributes, &error_index)) {
+ ALOGE("dvrSurfaceSetAttributes: Invalid surface attribute type: %" PRIu64,
+ attributes[error_index].value.type);
+ return -EINVAL;
+ }
+
+ auto status = surface->surface->SetAttributes(surface_attributes);
+ if (!status) {
+ ALOGE("dvrSurfaceSetAttributes: Failed to set attributes: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ return 0;
+}
+
+int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width,
+ uint32_t height, uint32_t format,
+ uint32_t layer_count, uint64_t usage,
+ size_t capacity,
+ DvrWriteBufferQueue** out_writer) {
+ if (surface == nullptr || out_writer == nullptr) {
+ ALOGE(
+ "dvrSurfaceCreateWriteBufferQueue: Invalid inputs: surface=%p, "
+ "out_writer=%p.",
+ surface, out_writer);
+ return -EINVAL;
+ }
+
+ auto status = surface->surface->CreateQueue(width, height, layer_count,
+ format, usage, capacity);
+ if (!status) {
+ ALOGE("dvrSurfaceCreateWriteBufferQueue: Failed to create queue: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ *out_writer = CreateDvrWriteBufferQueueFromProducerQueue(status.take());
+ return 0;
+}
+
+int dvrGetNamedBuffer(const char* name, DvrBuffer** out_buffer) {
+ auto client = DisplayClient::Create();
+ if (!client) {
+ ALOGE("dvrGetNamedBuffer: Failed to create display client!");
+ return -ECOMM;
+ }
+
+ if (out_buffer == nullptr || name == nullptr) {
+ ALOGE("dvrGetNamedBuffer: Invalid inputs: name=%p, out_buffer=%p.", name,
+ out_buffer);
+ return -EINVAL;
+ }
+
+ auto status = client->GetNamedBuffer(name);
+ if (!status) {
+ ALOGE("dvrGetNamedBuffer: Failed to find named buffer name=%s: %s", name,
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+ *out_buffer = CreateDvrBufferFromIonBuffer(status.take());
+ return 0;
+}
+
+} // extern "C"
diff --git a/libs/vr/libdvr/dvr_vsync.cpp b/libs/vr/libdvr/dvr_vsync.cpp
new file mode 100644
index 0000000000..099240e53a
--- /dev/null
+++ b/libs/vr/libdvr/dvr_vsync.cpp
@@ -0,0 +1,33 @@
+#include "include/dvr/dvr_vsync.h"
+
+#include <utils/Log.h>
+
+#include <private/dvr/vsync_client.h>
+
+extern "C" {
+
+struct DvrVSyncClient {
+ std::unique_ptr<android::dvr::VSyncClient> client;
+};
+
+int dvrVSyncClientCreate(DvrVSyncClient** client_out) {
+ auto client = android::dvr::VSyncClient::Create();
+ if (!client) {
+ ALOGE("dvrVSyncClientCreate: Failed to create vsync client!");
+ return -EIO;
+ }
+
+ *client_out = new DvrVSyncClient{std::move(client)};
+ return 0;
+}
+
+void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; }
+
+int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
+ int64_t* next_timestamp_ns,
+ uint32_t* next_vsync_count) {
+ return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns,
+ next_vsync_count);
+}
+
+} // extern "C"
diff --git a/libs/vr/libdvr/exported_apis.lds b/libs/vr/libdvr/exported_apis.lds
new file mode 100644
index 0000000000..5ecb49861c
--- /dev/null
+++ b/libs/vr/libdvr/exported_apis.lds
@@ -0,0 +1,9 @@
+{
+ global:
+ # Whitelist the function to load the dvr api.
+ dvrGetApi;
+
+ local:
+ # Hide everything else.
+ *;
+};
diff --git a/libs/vr/libdvr/include/CPPLINT.cfg b/libs/vr/libdvr/include/CPPLINT.cfg
new file mode 100644
index 0000000000..2f8a3c018c
--- /dev/null
+++ b/libs/vr/libdvr/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
new file mode 100644
index 0000000000..8a203e0694
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -0,0 +1,312 @@
+#ifndef ANDROID_DVR_API_H_
+#define ANDROID_DVR_API_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include <dvr/dvr_hardware_composer_defs.h>
+
+__BEGIN_DECLS
+
+typedef struct ANativeWindow ANativeWindow;
+
+typedef struct DvrPoseAsync DvrPoseAsync;
+
+typedef uint64_t DvrSurfaceUpdateFlags;
+typedef struct DvrDisplayManager DvrDisplayManager;
+typedef struct DvrSurfaceState DvrSurfaceState;
+typedef struct DvrPose DvrPose;
+typedef struct DvrVSyncClient DvrVSyncClient;
+typedef struct DvrVirtualTouchpad DvrVirtualTouchpad;
+
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+
+typedef struct DvrSurface DvrSurface;
+typedef uint64_t DvrSurfaceAttributeType;
+typedef int32_t DvrSurfaceAttributeKey;
+
+typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue;
+typedef struct DvrSurfaceAttribute DvrSurfaceAttribute;
+
+struct native_handle;
+
+// dvr_display_manager.h
+typedef int (*DvrDisplayManagerCreatePtr)(DvrDisplayManager** client_out);
+typedef void (*DvrDisplayManagerDestroyPtr)(DvrDisplayManager* client);
+typedef int (*DvrDisplayManagerSetupNamedBufferPtr)(DvrDisplayManager* client,
+ const char* name,
+ size_t size, uint64_t usage,
+ DvrBuffer** buffer_out);
+typedef int (*DvrDisplayManagerGetEventFdPtr)(DvrDisplayManager* client);
+typedef int (*DvrDisplayManagerTranslateEpollEventMaskPtr)(
+ DvrDisplayManager* client, int in_events, int* out_events);
+typedef int (*DvrDisplayManagerGetSurfaceStatePtr)(
+ DvrDisplayManager* client, DvrSurfaceState* surface_state);
+typedef int (*DvrDisplayManagerGetReadBufferQueuePtr)(
+ DvrDisplayManager* client, int surface_id, int queue_id,
+ DvrReadBufferQueue** queue_out);
+typedef int (*DvrSurfaceStateCreatePtr)(DvrSurfaceState** surface_state);
+typedef void (*DvrSurfaceStateDestroyPtr)(DvrSurfaceState* surface_state);
+typedef int (*DvrSurfaceStateGetSurfaceCountPtr)(DvrSurfaceState* surface_state,
+ size_t* count_out);
+typedef int (*DvrSurfaceStateGetUpdateFlagsPtr)(
+ DvrSurfaceState* surface_state, size_t surface_index,
+ DvrSurfaceUpdateFlags* flags_out);
+typedef int (*DvrSurfaceStateGetSurfaceIdPtr)(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ int* surface_id_out);
+typedef int (*DvrSurfaceStateGetProcessIdPtr)(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ int* process_id_out);
+typedef int (*DvrSurfaceStateGetQueueCountPtr)(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ size_t* count_out);
+typedef ssize_t (*DvrSurfaceStateGetQueueIdsPtr)(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ int* queue_ids,
+ size_t max_count);
+typedef int (*DvrSurfaceStateGetZOrderPtr)(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ int* z_order_out);
+typedef int (*DvrSurfaceStateGetVisiblePtr)(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ bool* visible_out);
+typedef int (*DvrSurfaceStateGetAttributeCountPtr)(
+ DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out);
+typedef ssize_t (*DvrSurfaceStateGetAttributesPtr)(
+ DvrSurfaceState* surface_state, size_t surface_index,
+ DvrSurfaceAttribute* attributes, size_t max_attribute_count);
+
+// dvr_buffer.h
+typedef void (*DvrWriteBufferCreateEmptyPtr)(DvrWriteBuffer** write_buffer_out);
+typedef void (*DvrWriteBufferDestroyPtr)(DvrWriteBuffer* write_buffer);
+typedef int (*DvrWriteBufferIsValidPtr)(DvrWriteBuffer* write_buffer);
+typedef int (*DvrWriteBufferClearPtr)(DvrWriteBuffer* write_buffer);
+typedef int (*DvrWriteBufferGetIdPtr)(DvrWriteBuffer* write_buffer);
+typedef int (*DvrWriteBufferGetAHardwareBufferPtr)(
+ DvrWriteBuffer* write_buffer, AHardwareBuffer** hardware_buffer);
+typedef int (*DvrWriteBufferPostPtr)(DvrWriteBuffer* write_buffer,
+ int ready_fence_fd, const void* meta,
+ size_t meta_size_bytes);
+typedef int (*DvrWriteBufferGainPtr)(DvrWriteBuffer* write_buffer,
+ int* release_fence_fd);
+typedef int (*DvrWriteBufferGainAsyncPtr)(DvrWriteBuffer* write_buffer);
+typedef const struct native_handle* (*DvrWriteBufferGetNativeHandlePtr)(
+ DvrWriteBuffer* write_buffer);
+
+typedef void (*DvrReadBufferCreateEmptyPtr)(DvrReadBuffer** read_buffer_out);
+typedef void (*DvrReadBufferDestroyPtr)(DvrReadBuffer* read_buffer);
+typedef int (*DvrReadBufferIsValidPtr)(DvrReadBuffer* read_buffer);
+typedef int (*DvrReadBufferClearPtr)(DvrReadBuffer* read_buffer);
+typedef int (*DvrReadBufferGetIdPtr)(DvrReadBuffer* read_buffer);
+typedef int (*DvrReadBufferGetAHardwareBufferPtr)(
+ DvrReadBuffer* read_buffer, AHardwareBuffer** hardware_buffer);
+typedef int (*DvrReadBufferAcquirePtr)(DvrReadBuffer* read_buffer,
+ int* ready_fence_fd, void* meta,
+ size_t meta_size_bytes);
+typedef int (*DvrReadBufferReleasePtr)(DvrReadBuffer* read_buffer,
+ int release_fence_fd);
+typedef int (*DvrReadBufferReleaseAsyncPtr)(DvrReadBuffer* read_buffer);
+typedef const struct native_handle* (*DvrReadBufferGetNativeHandlePtr)(
+ DvrReadBuffer* read_buffer);
+
+typedef void (*DvrBufferDestroyPtr)(DvrBuffer* buffer);
+typedef int (*DvrBufferGetAHardwareBufferPtr)(
+ DvrBuffer* buffer, AHardwareBuffer** hardware_buffer);
+typedef const struct native_handle* (*DvrBufferGetNativeHandlePtr)(
+ DvrBuffer* buffer);
+
+// dvr_buffer_queue.h
+typedef void (*DvrWriteBufferQueueDestroyPtr)(DvrWriteBufferQueue* write_queue);
+typedef ssize_t (*DvrWriteBufferQueueGetCapacityPtr)(
+ DvrWriteBufferQueue* write_queue);
+typedef int (*DvrWriteBufferQueueGetIdPtr)(DvrWriteBufferQueue* write_queue);
+typedef int (*DvrWriteBufferQueueGetExternalSurfacePtr)(
+ DvrWriteBufferQueue* write_queue, ANativeWindow** out_window);
+typedef int (*DvrWriteBufferQueueCreateReadQueuePtr)(
+ DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue);
+typedef int (*DvrWriteBufferQueueDequeuePtr)(DvrWriteBufferQueue* write_queue,
+ int timeout,
+ DvrWriteBuffer* out_buffer,
+ int* out_fence_fd);
+typedef void (*DvrReadBufferQueueDestroyPtr)(DvrReadBufferQueue* read_queue);
+typedef ssize_t (*DvrReadBufferQueueGetCapacityPtr)(
+ DvrReadBufferQueue* read_queue);
+typedef int (*DvrReadBufferQueueGetIdPtr)(DvrReadBufferQueue* read_queue);
+typedef int (*DvrReadBufferQueueCreateReadQueuePtr)(
+ DvrReadBufferQueue* read_queue, DvrReadBufferQueue** out_read_queue);
+typedef int (*DvrReadBufferQueueDequeuePtr)(DvrReadBufferQueue* read_queue,
+ int timeout,
+ DvrReadBuffer* out_buffer,
+ int* out_fence_fd, void* out_meta,
+ size_t meta_size_bytes);
+
+// dvr_surface.h
+typedef int (*DvrGetNamedBufferPtr)(const char* name, DvrBuffer** out_buffer);
+typedef int (*DvrSurfaceCreatePtr)(const DvrSurfaceAttribute* attributes,
+ size_t attribute_count,
+ DvrSurface** surface_out);
+typedef void (*DvrSurfaceDestroyPtr)(DvrSurface* surface);
+typedef int (*DvrSurfaceGetIdPtr)(DvrSurface* surface);
+typedef int (*DvrSurfaceSetAttributesPtr)(DvrSurface* surface,
+ const DvrSurfaceAttribute* attributes,
+ size_t attribute_count);
+typedef int (*DvrSurfaceCreateWriteBufferQueuePtr)(
+ DvrSurface* surface, uint32_t width, uint32_t height, uint32_t format,
+ uint32_t layer_count, uint64_t usage, size_t capacity,
+ DvrWriteBufferQueue** queue_out);
+
+// vsync_client_api.h
+typedef int (*DvrVSyncClientCreatePtr)(DvrVSyncClient** client_out);
+typedef void (*DvrVSyncClientDestroyPtr)(DvrVSyncClient* client);
+typedef int (*DvrVSyncClientGetSchedInfoPtr)(DvrVSyncClient* client,
+ int64_t* vsync_period_ns,
+ int64_t* next_timestamp_ns,
+ uint32_t* next_vsync_count);
+
+// pose_client.h
+typedef DvrPose* (*DvrPoseCreatePtr)(void);
+typedef void (*DvrPoseDestroyPtr)(DvrPose* client);
+typedef int (*DvrPoseGetPtr)(DvrPose* client, uint32_t vsync_count,
+ DvrPoseAsync* out_pose);
+typedef uint32_t (*DvrPoseGetVsyncCountPtr)(DvrPose* client);
+typedef int (*DvrPoseGetControllerPtr)(DvrPose* client, int32_t controller_id,
+ uint32_t vsync_count,
+ DvrPoseAsync* out_pose);
+
+// virtual_touchpad_client.h
+typedef DvrVirtualTouchpad* (*DvrVirtualTouchpadCreatePtr)(void);
+typedef void (*DvrVirtualTouchpadDestroyPtr)(DvrVirtualTouchpad* client);
+typedef int (*DvrVirtualTouchpadAttachPtr)(DvrVirtualTouchpad* client);
+typedef int (*DvrVirtualTouchpadDetachPtr)(DvrVirtualTouchpad* client);
+typedef int (*DvrVirtualTouchpadTouchPtr)(DvrVirtualTouchpad* client,
+ int touchpad, float x, float y,
+ float pressure);
+typedef int (*DvrVirtualTouchpadButtonStatePtr)(DvrVirtualTouchpad* client,
+ int touchpad, int buttons);
+
+// dvr_hardware_composer_client.h
+typedef struct DvrHwcClient DvrHwcClient;
+typedef struct DvrHwcFrame DvrHwcFrame;
+typedef int (*DvrHwcOnFrameCallback)(void* client_state, DvrHwcFrame* frame);
+typedef DvrHwcClient* (*DvrHwcClientCreatePtr)(DvrHwcOnFrameCallback callback,
+ void* client_state);
+typedef void (*DvrHwcClientDestroyPtr)(DvrHwcClient* client);
+typedef void (*DvrHwcFrameDestroyPtr)(DvrHwcFrame* frame);
+typedef DvrHwcDisplay (*DvrHwcFrameGetDisplayIdPtr)(DvrHwcFrame* frame);
+typedef int32_t (*DvrHwcFrameGetDisplayWidthPtr)(DvrHwcFrame* frame);
+typedef int32_t (*DvrHwcFrameGetDisplayHeightPtr)(DvrHwcFrame* frame);
+typedef bool (*DvrHwcFrameGetDisplayRemovedPtr)(DvrHwcFrame* frame);
+typedef size_t (*DvrHwcFrameGetLayerCountPtr)(DvrHwcFrame* frame);
+typedef DvrHwcLayer (*DvrHwcFrameGetLayerIdPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef uint32_t (*DvrHwcFrameGetActiveConfigPtr)(DvrHwcFrame* frame);
+typedef uint32_t (*DvrHwcFrameGetColorModePtr)(DvrHwcFrame* frame);
+typedef void (*DvrHwcFrameGetColorTransformPtr)(DvrHwcFrame* frame,
+ float* out_matrix,
+ int32_t* out_hint);
+typedef uint32_t (*DvrHwcFrameGetPowerModePtr)(DvrHwcFrame* frame);
+typedef uint32_t (*DvrHwcFrameGetVsyncEnabledPtr)(DvrHwcFrame* frame);
+typedef AHardwareBuffer* (*DvrHwcFrameGetLayerBufferPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef int (*DvrHwcFrameGetLayerFencePtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef DvrHwcRecti (*DvrHwcFrameGetLayerDisplayFramePtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef DvrHwcRectf (*DvrHwcFrameGetLayerCropPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef DvrHwcBlendMode (*DvrHwcFrameGetLayerBlendModePtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef float (*DvrHwcFrameGetLayerAlphaPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef uint32_t (*DvrHwcFrameGetLayerTypePtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef uint32_t (*DvrHwcFrameGetLayerApplicationIdPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef uint32_t (*DvrHwcFrameGetLayerZOrderPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+
+typedef void (*DvrHwcFrameGetLayerCursorPtr)(DvrHwcFrame* frame,
+ size_t layer_index, int32_t* out_x,
+ int32_t* out_y);
+
+typedef uint32_t (*DvrHwcFrameGetLayerTransformPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+
+typedef uint32_t (*DvrHwcFrameGetLayerDataspacePtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+
+typedef uint32_t (*DvrHwcFrameGetLayerColorPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+
+typedef uint32_t (*DvrHwcFrameGetLayerNumVisibleRegionsPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef DvrHwcRecti (*DvrHwcFrameGetLayerVisibleRegionPtr)(DvrHwcFrame* frame,
+ size_t layer_index,
+ size_t index);
+
+typedef uint32_t (*DvrHwcFrameGetLayerNumDamagedRegionsPtr)(DvrHwcFrame* frame,
+ size_t layer_index);
+typedef DvrHwcRecti (*DvrHwcFrameGetLayerDamagedRegionPtr)(DvrHwcFrame* frame,
+ size_t layer_index,
+ size_t index);
+
+// The buffer metadata that an Android Surface (a.k.a. ANativeWindow)
+// will populate. A DvrWriteBufferQueue must be created with this metadata iff
+// ANativeWindow access is needed. Please do not remove, modify, or reorder
+// existing data members. If new fields need to be added, please take extra care
+// to make sure that new data field is padded properly the size of the struct
+// stays same.
+struct DvrNativeBufferMetadata {
+ // Timestamp of the frame.
+ int64_t timestamp;
+
+ // Whether the buffer is using auto timestamp.
+ int32_t is_auto_timestamp;
+
+ // Must be one of the HAL_DATASPACE_XXX value defined in system/graphics.h
+ int32_t dataspace;
+
+ // Crop extracted from an ACrop or android::Crop object.
+ int32_t crop_left;
+ int32_t crop_top;
+ int32_t crop_right;
+ int32_t crop_bottom;
+
+ // Must be one of the NATIVE_WINDOW_SCALING_MODE_XXX value defined in
+ // system/window.h.
+ int32_t scaling_mode;
+
+ // Must be one of the ANATIVEWINDOW_TRANSFORM_XXX value defined in
+ // android/native_window.h
+ int32_t transform;
+
+ // Reserved bytes for so that the struct is forward compatible.
+ int32_t reserved[16];
+};
+
+struct DvrApi_v1 {
+// Defines an API entry for V1 (no version suffix).
+#define DVR_V1_API_ENTRY(name) Dvr##name##Ptr name
+
+// Include file with API entries.
+#include "dvr_api_entries.h"
+
+// Undefine macro definitions to play nice with Google3 style rules.
+#undef DVR_V1_API_ENTRY
+};
+
+int dvrGetApi(void* api, size_t struct_size, int version);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_API_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
new file mode 100644
index 0000000000..09568fd9dc
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -0,0 +1,135 @@
+// dvr_api_entries.h
+//
+// Defines the DVR platform library API entries.
+//
+// Do not include this header directly.
+
+#ifndef DVR_V1_API_ENTRY
+#error Do not include this header directly.
+#endif
+
+// Display manager client
+DVR_V1_API_ENTRY(DisplayManagerCreate);
+DVR_V1_API_ENTRY(DisplayManagerDestroy);
+DVR_V1_API_ENTRY(DisplayManagerSetupNamedBuffer);
+DVR_V1_API_ENTRY(DisplayManagerGetEventFd);
+DVR_V1_API_ENTRY(DisplayManagerTranslateEpollEventMask);
+DVR_V1_API_ENTRY(DisplayManagerGetSurfaceState);
+DVR_V1_API_ENTRY(DisplayManagerGetReadBufferQueue);
+DVR_V1_API_ENTRY(SurfaceStateCreate);
+DVR_V1_API_ENTRY(SurfaceStateDestroy);
+DVR_V1_API_ENTRY(SurfaceStateGetSurfaceCount);
+DVR_V1_API_ENTRY(SurfaceStateGetUpdateFlags);
+DVR_V1_API_ENTRY(SurfaceStateGetSurfaceId);
+DVR_V1_API_ENTRY(SurfaceStateGetProcessId);
+DVR_V1_API_ENTRY(SurfaceStateGetQueueCount);
+DVR_V1_API_ENTRY(SurfaceStateGetQueueIds);
+DVR_V1_API_ENTRY(SurfaceStateGetZOrder);
+DVR_V1_API_ENTRY(SurfaceStateGetVisible);
+DVR_V1_API_ENTRY(SurfaceStateGetAttributeCount);
+DVR_V1_API_ENTRY(SurfaceStateGetAttributes);
+
+// Write buffer
+DVR_V1_API_ENTRY(WriteBufferCreateEmpty);
+DVR_V1_API_ENTRY(WriteBufferDestroy);
+DVR_V1_API_ENTRY(WriteBufferIsValid);
+DVR_V1_API_ENTRY(WriteBufferClear);
+DVR_V1_API_ENTRY(WriteBufferGetId);
+DVR_V1_API_ENTRY(WriteBufferGetAHardwareBuffer);
+DVR_V1_API_ENTRY(WriteBufferPost);
+DVR_V1_API_ENTRY(WriteBufferGain);
+DVR_V1_API_ENTRY(WriteBufferGainAsync);
+DVR_V1_API_ENTRY(WriteBufferGetNativeHandle);
+
+// Read buffer
+DVR_V1_API_ENTRY(ReadBufferCreateEmpty);
+DVR_V1_API_ENTRY(ReadBufferDestroy);
+DVR_V1_API_ENTRY(ReadBufferIsValid);
+DVR_V1_API_ENTRY(ReadBufferClear);
+DVR_V1_API_ENTRY(ReadBufferGetId);
+DVR_V1_API_ENTRY(ReadBufferGetAHardwareBuffer);
+DVR_V1_API_ENTRY(ReadBufferAcquire);
+DVR_V1_API_ENTRY(ReadBufferRelease);
+DVR_V1_API_ENTRY(ReadBufferReleaseAsync);
+DVR_V1_API_ENTRY(ReadBufferGetNativeHandle);
+
+// Buffer
+DVR_V1_API_ENTRY(BufferDestroy);
+DVR_V1_API_ENTRY(BufferGetAHardwareBuffer);
+DVR_V1_API_ENTRY(BufferGetNativeHandle);
+
+// Write buffer queue
+DVR_V1_API_ENTRY(WriteBufferQueueDestroy);
+DVR_V1_API_ENTRY(WriteBufferQueueGetCapacity);
+DVR_V1_API_ENTRY(WriteBufferQueueGetId);
+DVR_V1_API_ENTRY(WriteBufferQueueGetExternalSurface);
+DVR_V1_API_ENTRY(WriteBufferQueueCreateReadQueue);
+DVR_V1_API_ENTRY(WriteBufferQueueDequeue);
+
+// Read buffer queue
+DVR_V1_API_ENTRY(ReadBufferQueueDestroy);
+DVR_V1_API_ENTRY(ReadBufferQueueGetCapacity);
+DVR_V1_API_ENTRY(ReadBufferQueueGetId);
+DVR_V1_API_ENTRY(ReadBufferQueueCreateReadQueue);
+DVR_V1_API_ENTRY(ReadBufferQueueDequeue);
+
+// V-Sync client
+DVR_V1_API_ENTRY(VSyncClientCreate);
+DVR_V1_API_ENTRY(VSyncClientDestroy);
+DVR_V1_API_ENTRY(VSyncClientGetSchedInfo);
+
+// Display surface
+DVR_V1_API_ENTRY(SurfaceCreate);
+DVR_V1_API_ENTRY(SurfaceDestroy);
+DVR_V1_API_ENTRY(SurfaceGetId);
+DVR_V1_API_ENTRY(SurfaceSetAttributes);
+DVR_V1_API_ENTRY(SurfaceCreateWriteBufferQueue);
+DVR_V1_API_ENTRY(GetNamedBuffer);
+
+// Pose client
+DVR_V1_API_ENTRY(PoseCreate);
+DVR_V1_API_ENTRY(PoseDestroy);
+DVR_V1_API_ENTRY(PoseGet);
+DVR_V1_API_ENTRY(PoseGetVsyncCount);
+DVR_V1_API_ENTRY(PoseGetController);
+
+// Virtual touchpad client
+DVR_V1_API_ENTRY(VirtualTouchpadCreate);
+DVR_V1_API_ENTRY(VirtualTouchpadDestroy);
+DVR_V1_API_ENTRY(VirtualTouchpadAttach);
+DVR_V1_API_ENTRY(VirtualTouchpadDetach);
+DVR_V1_API_ENTRY(VirtualTouchpadTouch);
+DVR_V1_API_ENTRY(VirtualTouchpadButtonState);
+
+// VR HWComposer client
+DVR_V1_API_ENTRY(HwcClientCreate);
+DVR_V1_API_ENTRY(HwcClientDestroy);
+DVR_V1_API_ENTRY(HwcFrameDestroy);
+DVR_V1_API_ENTRY(HwcFrameGetDisplayId);
+DVR_V1_API_ENTRY(HwcFrameGetDisplayWidth);
+DVR_V1_API_ENTRY(HwcFrameGetDisplayHeight);
+DVR_V1_API_ENTRY(HwcFrameGetDisplayRemoved);
+DVR_V1_API_ENTRY(HwcFrameGetActiveConfig);
+DVR_V1_API_ENTRY(HwcFrameGetColorMode);
+DVR_V1_API_ENTRY(HwcFrameGetColorTransform);
+DVR_V1_API_ENTRY(HwcFrameGetPowerMode);
+DVR_V1_API_ENTRY(HwcFrameGetVsyncEnabled);
+DVR_V1_API_ENTRY(HwcFrameGetLayerCount);
+DVR_V1_API_ENTRY(HwcFrameGetLayerId);
+DVR_V1_API_ENTRY(HwcFrameGetLayerBuffer);
+DVR_V1_API_ENTRY(HwcFrameGetLayerFence);
+DVR_V1_API_ENTRY(HwcFrameGetLayerDisplayFrame);
+DVR_V1_API_ENTRY(HwcFrameGetLayerCrop);
+DVR_V1_API_ENTRY(HwcFrameGetLayerBlendMode);
+DVR_V1_API_ENTRY(HwcFrameGetLayerAlpha);
+DVR_V1_API_ENTRY(HwcFrameGetLayerType);
+DVR_V1_API_ENTRY(HwcFrameGetLayerApplicationId);
+DVR_V1_API_ENTRY(HwcFrameGetLayerZOrder);
+DVR_V1_API_ENTRY(HwcFrameGetLayerCursor);
+DVR_V1_API_ENTRY(HwcFrameGetLayerTransform);
+DVR_V1_API_ENTRY(HwcFrameGetLayerDataspace);
+DVR_V1_API_ENTRY(HwcFrameGetLayerColor);
+DVR_V1_API_ENTRY(HwcFrameGetLayerNumVisibleRegions);
+DVR_V1_API_ENTRY(HwcFrameGetLayerVisibleRegion);
+DVR_V1_API_ENTRY(HwcFrameGetLayerNumDamagedRegions);
+DVR_V1_API_ENTRY(HwcFrameGetLayerDamagedRegion);
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer.h b/libs/vr/libdvr/include/dvr/dvr_buffer.h
new file mode 100644
index 0000000000..af55698b74
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer.h
@@ -0,0 +1,104 @@
+#ifndef ANDROID_DVR_BUFFER_H_
+#define ANDROID_DVR_BUFFER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <memory>
+
+__BEGIN_DECLS
+
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrBuffer DvrBuffer;
+typedef struct AHardwareBuffer AHardwareBuffer;
+struct native_handle;
+
+// Creates an empty write buffer that may be filled with an acutal buffer by
+// other functions.
+void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer);
+
+// Destroys the write buffer.
+void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer);
+
+// Returns 1 if the given write buffer object contains a buffer, 0 otherwise.
+int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer);
+
+// Clears the contents of the buffer object. After a call to this function
+// dvrWriteBufferIsValid on the same buffer object returns 0.
+int dvrWriteBufferClear(DvrWriteBuffer* write_buffer);
+
+// Returns the global BufferHub id of this buffer.
+int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer);
+
+// Returns an AHardwareBuffer for the underlying buffer.
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
+ AHardwareBuffer** hardware_buffer);
+
+// Posts the buffer, notifying any connected read buffers. Takes ownership of
+// |ready_fence_fd|.
+int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd,
+ const void* meta, size_t meta_size_bytes);
+
+// Gains a buffer that has been released by all connected read buffers.
+int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd);
+int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer);
+
+// TODO(eieio): Switch to return int and take an out parameter for the native
+// handle.
+const struct native_handle* dvrWriteBufferGetNativeHandle(
+ DvrWriteBuffer* write_buffer);
+
+// Creates an empty read buffer that may be filled with and actual buffer by
+// other functions.
+void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer);
+
+// Destroys the read buffer.
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer);
+
+// Returns 1 if the given write buffer object contains a buffer, 0 otherwise.
+int dvrReadBufferIsValid(DvrReadBuffer* read_buffer);
+
+// Clears the contents of the buffer object. After a call to this function
+// dvrReadBufferIsValid on the same buffer object returns 0.
+int dvrReadBufferClear(DvrReadBuffer* read_buffer);
+
+// Returns the global BufferHub id of this buffer.
+int dvrReadBufferGetId(DvrReadBuffer* read_buffer);
+
+// Returns an AHardwareBuffer for the underlying buffer.
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
+ AHardwareBuffer** hardware_buffer);
+
+// Acquires the read buffer after it has been posted by the write buffer it is
+// connected to.
+int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd,
+ void* meta, size_t meta_size_bytes);
+
+// Releases the read buffer, notifying the write buffer it is connected to.
+// Takes ownership of |release_fence_fd|.
+int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd);
+int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer);
+
+// TODO(eieio): Switch to return int and take an out parameter for the native
+// handle.
+const struct native_handle* dvrReadBufferGetNativeHandle(
+ DvrReadBuffer* read_buffer);
+
+// Destroys the buffer.
+void dvrBufferDestroy(DvrBuffer* buffer);
+
+// Gets an AHardwareBuffer from the buffer.
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
+ AHardwareBuffer** hardware_buffer);
+
+// TODO(eieio): Switch to return int and take an out parameter for the native
+// handle.
+const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_BUFFER_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
new file mode 100644
index 0000000000..dd669dc30f
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
@@ -0,0 +1,44 @@
+#ifndef ANDROID_DVR_BUFFER_QUEUE_H_
+#define ANDROID_DVR_BUFFER_QUEUE_H_
+
+#include <sys/cdefs.h>
+
+#include <dvr/dvr_buffer.h>
+
+__BEGIN_DECLS
+
+typedef struct ANativeWindow ANativeWindow;
+
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
+// WriteBufferQueue
+void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue);
+ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue);
+int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue);
+
+// Returns ANativeWindow. Can be casted to a Java Surface using
+// ANativeWindow_toSurface NDK API. Note that this method does not acquire an
+// additional reference to the ANativeWindow returned, don't call
+// ANativeWindow_release on it.
+int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
+ ANativeWindow** out_window);
+
+int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
+ DvrReadBufferQueue** out_read_queue);
+int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
+ DvrWriteBuffer* out_buffer, int* out_fence_fd);
+
+// ReadeBufferQueue
+void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue);
+ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue);
+int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue);
+int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
+ DvrReadBufferQueue** out_read_queue);
+int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
+ DvrReadBuffer* out_buffer, int* out_fence_fd,
+ void* out_meta, size_t meta_size_bytes);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_BUFFER_QUEUE_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_deleter.h b/libs/vr/libdvr/include/dvr/dvr_deleter.h
new file mode 100644
index 0000000000..943384f802
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_deleter.h
@@ -0,0 +1,86 @@
+#ifndef ANDROID_DVR_DELETER_H_
+#define ANDROID_DVR_DELETER_H_
+
+#include <sys/cdefs.h>
+
+#include <memory>
+
+// Header-only C++ helper to delete opaque DVR objects.
+
+__BEGIN_DECLS
+
+// Use forward declarations to avoid dependency on other headers.
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrDisplayManager DvrDisplayManager;
+typedef struct DvrSurfaceState DvrSurfaceState;
+typedef struct DvrSurface DvrSurface;
+typedef struct DvrHwcClient DvrHwcClient;
+typedef struct DvrHwcFrame DvrHwcFrame;
+typedef struct DvrVSyncClient DvrVSyncClient;
+
+void dvrBufferDestroy(DvrBuffer* buffer);
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer);
+void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer);
+void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue);
+void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue);
+void dvrDisplayManagerDestroy(DvrDisplayManager* client);
+void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state);
+void dvrSurfaceDestroy(DvrSurface* surface);
+void dvrHwcClientDestroy(DvrHwcClient* client);
+void dvrHwcFrameDestroy(DvrHwcFrame* frame);
+void dvrVSyncClientDestroy(DvrVSyncClient* client);
+
+__END_DECLS
+
+// Avoid errors if this header is included in C code.
+#if defined(__cplusplus)
+
+namespace android {
+namespace dvr {
+
+// Universal DVR object deleter. May be passed to smart pointer types to handle
+// deletion of DVR API objects.
+struct DvrObjectDeleter {
+ void operator()(DvrBuffer* p) { dvrBufferDestroy(p); }
+ void operator()(DvrReadBuffer* p) { dvrReadBufferDestroy(p); }
+ void operator()(DvrWriteBuffer* p) { dvrWriteBufferDestroy(p); }
+ void operator()(DvrReadBufferQueue* p) { dvrReadBufferQueueDestroy(p); }
+ void operator()(DvrWriteBufferQueue* p) { dvrWriteBufferQueueDestroy(p); }
+ void operator()(DvrDisplayManager* p) { dvrDisplayManagerDestroy(p); }
+ void operator()(DvrSurfaceState* p) { dvrSurfaceStateDestroy(p); }
+ void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); }
+ void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); }
+ void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); }
+ void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(p); }
+};
+
+// Helper to define unique pointers for DVR object types.
+template <typename T>
+using MakeUniqueDvrPointer = std::unique_ptr<T, DvrObjectDeleter>;
+
+// Unique pointer types for DVR objects.
+using UniqueDvrBuffer = MakeUniqueDvrPointer<DvrBuffer>;
+using UniqueDvrReadBuffer = MakeUniqueDvrPointer<DvrReadBuffer>;
+using UniqueDvrWriteBuffer = MakeUniqueDvrPointer<DvrWriteBuffer>;
+using UniqueDvrReadBufferQueue = MakeUniqueDvrPointer<DvrReadBufferQueue>;
+using UniqueDvrWriteBufferQueue = MakeUniqueDvrPointer<DvrWriteBufferQueue>;
+using UniqueDvrDisplayManager = MakeUniqueDvrPointer<DvrDisplayManager>;
+using UniqueDvrSurfaceState = MakeUniqueDvrPointer<DvrSurfaceState>;
+using UniqueDvrSurface = MakeUniqueDvrPointer<DvrSurface>;
+using UniqueDvrHwcClient = MakeUniqueDvrPointer<DvrHwcClient>;
+using UniqueDvrHwcFrame = MakeUniqueDvrPointer<DvrHwcFrame>;
+using UniqueDvrVSyncClient = MakeUniqueDvrPointer<DvrVSyncClient>;
+
+// TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into
+// the relevant constructors.
+
+} // namespace dvr
+} // namespace android
+
+#endif // defined(__cplusplus)
+
+#endif // ANDROID_DVR_DELETER_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_display_manager.h b/libs/vr/libdvr/include/dvr/dvr_display_manager.h
new file mode 100644
index 0000000000..d5aef8b18b
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_display_manager.h
@@ -0,0 +1,140 @@
+#ifndef DVR_DISPLAY_MANAGER_CLIENT_H_
+#define DVR_DISPLAY_MANAGER_CLIENT_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <dvr/dvr_display_types.h>
+#include <dvr/dvr_surface.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrDisplayManager DvrDisplayManager;
+typedef struct DvrSurfaceState DvrSurfaceState;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
+typedef uint64_t DvrSurfaceUpdateFlags;
+
+// Attempts to connect to the display manager service.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerCreate(DvrDisplayManager** client_out);
+
+// Destroys the display manager client object.
+void dvrDisplayManagerDestroy(DvrDisplayManager* client);
+
+// Sets up a named buffer for shared memory data transfer between display
+// clients and the display manager.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerSetupNamedBuffer(DvrDisplayManager* client,
+ const char* name, size_t size,
+ uint64_t usage, DvrBuffer** buffer_out);
+
+// Returns an fd used to signal when surface updates occur. Note that depending
+// on the underlying transport, only a subset of the real event bits may be
+// supported. Use dvrDisplayManagerClientTranslateEpollEventMask to get the real
+// event flags.
+// @return the fd on success. Otherwise returns a negative error value.
+int dvrDisplayManagerGetEventFd(DvrDisplayManager* client);
+
+// @param in_events pass in the epoll revents that were initially returned by
+// poll/epoll.
+// @param on success, this value will be overwritten with the true poll/epoll
+// values.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client,
+ int in_events, int* out_events);
+
+// Queries the display manager service for the current state of the display
+// surfaces and stores the results in the given surface state object.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client,
+ DvrSurfaceState* surface_state);
+
+// Gets a read buffer queue from the surface |surface_id| named |queue_id|. Each
+// call returns a different read buffer queue connected to the same write buffer
+// queue. Callers should cache these instead of requesting new ones when
+// possible.
+int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client,
+ int surface_id, int queue_id,
+ DvrReadBufferQueue** queue_out);
+
+// Creates a new surface state object. This object may be used to receive the
+// results of a surface state query. More than one state object may be created
+// to keep multiple snapshots, if desired.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateCreate(DvrSurfaceState** surface_state);
+
+// Destorys the surface state object.
+void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state);
+
+// Writes the number of surfaces described in the state object into |count_out|.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state,
+ size_t* count_out);
+
+// Returns the update flags for the surface at |surface_index| in the state
+// object. The flags may be used to determine what changes, if any, occured to
+// the surface since the last state update.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ DvrSurfaceUpdateFlags* flags_out);
+
+// Returns the unique identifier of surface at |surface_index| in the state
+// object. The identifier may be used to distinguish between surfaces.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state,
+ size_t surface_index, int* surface_id_out);
+
+// Returns the process id of surface at |surface_index| in the state object.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state,
+ size_t surface_index, int* process_id_out);
+
+// Writes the number of queues in the surface at |surface_index| in the state
+// object into |count_out|.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state,
+ size_t surface_index, size_t* count_out);
+
+// Returns up to |max_count| queue ids for the queues belonging to the surface
+// at |surface_index| in the state object.
+// @return The number of queue ids written on success. Otherwise returns a
+// negative error value.
+ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state,
+ size_t surface_index, int* queue_ids,
+ size_t max_count);
+
+// Writes the z-order of the surface at |surface_index| in surface state object
+// into |z_order_out|.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state,
+ size_t surface_index, int* z_order_out);
+
+// Writes the visible state of the surface at |surface_index| in the surface
+// state object into |visible_out|.
+// @return 0 on success. Otherwise it returns a negative error value.
+int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state,
+ size_t surface_index, bool* visible_out);
+
+// Writes the number of attributes on the surface at |surface_index| in the
+// state object into |count_out|.
+// @return 0 on success. Otherwise it returns a negative error value.
+int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state,
+ size_t surface_index, size_t* count_out);
+
+// Writes the list of attribute key/value pairs for the surface at
+// |surface_index| in the surface state object into |attributes|.
+// @return The number of attributes written on success. Otherwise returns a
+// negative error value.
+ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state,
+ size_t surface_index,
+ DvrSurfaceAttribute* attributes,
+ size_t max_count);
+
+__END_DECLS
+
+#endif // DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h
new file mode 100644
index 0000000000..7ee7f9ede7
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h
@@ -0,0 +1,107 @@
+#ifndef ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H
+#define ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H
+
+#include <dvr/dvr_hardware_composer_defs.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct AHardwareBuffer AHardwareBuffer;
+typedef struct DvrHwcClient DvrHwcClient;
+typedef struct DvrHwcFrame DvrHwcFrame;
+
+// Called when a new frame has arrived.
+//
+// @param client_state Pointer to client state passed in |dvrHwcCreateClient()|.
+// @param frame New frame. Owned by the client.
+// @return fence FD for the release of the last frame.
+typedef int(*DvrHwcOnFrameCallback)(void* client_state, DvrHwcFrame* frame);
+
+// @param callback Called when a new frame is available.
+// @param client_state Pointer to client state passed back in the callback.
+DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback,
+ void* client_state);
+
+void dvrHwcClientDestroy(DvrHwcClient* client);
+
+// Called to free the frame information.
+void dvrHwcFrameDestroy(DvrHwcFrame* frame);
+
+DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame);
+
+int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame);
+
+int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame);
+
+// @return True if the display has been removed. In this case the current frame
+// does not contain any valid layers to display. It is a signal to clean up any
+// display related state.
+bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame);
+
+// @return Number of layers in the frame.
+size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame);
+
+uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame);
+uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame);
+void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix,
+ int32_t* out_hint);
+uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame);
+uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame);
+
+DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index);
+
+// Return the graphic buffer associated with the layer at |layer_index| in
+// |frame|.
+//
+// @return Graphic buffer. Caller owns the buffer and is responsible for freeing
+// it. (see AHardwareBuffer_release())
+AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame,
+ size_t layer_index);
+
+// Returns the fence FD for the layer at index |layer_index| in |frame|.
+//
+// @return Fence FD. Caller owns the FD and is responsible for closing it.
+int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index);
+
+DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame,
+ size_t layer_index);
+
+DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index);
+
+DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame,
+ size_t layer_index);
+
+float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame,
+ size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index);
+
+void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index,
+ int32_t* out_x, int32_t* out_y);
+
+uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index);
+
+uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame,
+ size_t layer_index);
+DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame,
+ size_t layer_index, size_t index);
+
+uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame,
+ size_t layer_index);
+DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame,
+ size_t layer_index, size_t index);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H
diff --git a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h
new file mode 100644
index 0000000000..36c30f9be5
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_defs.h
@@ -0,0 +1,50 @@
+#ifndef ANDROID_VR_HARDWARE_COMPOSER_DEFS_H
+#define ANDROID_VR_HARDWARE_COMPOSER_DEFS_H
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// NOTE: These definitions must match the ones in
+// //hardware/libhardware/include/hardware/hwcomposer2.h. They are used by the
+// client side which does not have access to hwc2 headers.
+enum DvrHwcBlendMode {
+ DVR_HWC_BLEND_MODE_INVALID = 0,
+ DVR_HWC_BLEND_MODE_NONE = 1,
+ DVR_HWC_BLEND_MODE_PREMULTIPLIED = 2,
+ DVR_HWC_BLEND_MODE_COVERAGE = 3,
+};
+
+enum DvrHwcComposition {
+ DVR_HWC_COMPOSITION_INVALID = 0,
+ DVR_HWC_COMPOSITION_CLIENT = 1,
+ DVR_HWC_COMPOSITION_DEVICE = 2,
+ DVR_HWC_COMPOSITION_SOLID_COLOR = 3,
+ DVR_HWC_COMPOSITION_CURSOR = 4,
+ DVR_HWC_COMPOSITION_SIDEBAND = 5,
+};
+
+typedef uint64_t DvrHwcDisplay;
+typedef uint64_t DvrHwcLayer;
+
+struct DvrHwcRecti {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+};
+
+struct DvrHwcRectf {
+ float left;
+ float top;
+ float right;
+ float bottom;
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_HARDWARE_COMPOSER_DEFS_H
diff --git a/libs/vr/libdvr/include/dvr/dvr_surface.h b/libs/vr/libdvr/include/dvr/dvr_surface.h
new file mode 100644
index 0000000000..361488e671
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_surface.h
@@ -0,0 +1,91 @@
+#ifndef ANDROID_DVR_SURFACE_H_
+#define ANDROID_DVR_SURFACE_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+#include <dvr/dvr_buffer.h>
+#include <dvr/dvr_buffer_queue.h>
+#include <dvr/dvr_display_types.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrBuffer DvrBuffer;
+typedef struct DvrSurface DvrSurface;
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+
+// Attribute types. The values are one-hot encoded to support singluar types or
+// masks of supported types.
+enum {
+ DVR_SURFACE_ATTRIBUTE_TYPE_NONE = 0,
+ DVR_SURFACE_ATTRIBUTE_TYPE_INT32 = (1 << 0),
+ DVR_SURFACE_ATTRIBUTE_TYPE_INT64 = (1 << 1),
+ DVR_SURFACE_ATTRIBUTE_TYPE_BOOL = (1 << 2),
+ DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT = (1 << 3),
+ DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2 = (1 << 4),
+ DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3 = (1 << 5),
+ DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4 = (1 << 6),
+ DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8 = (1 << 7),
+ DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16 = (1 << 8),
+};
+
+typedef uint64_t DvrSurfaceAttributeType;
+typedef int32_t DvrSurfaceAttributeKey;
+
+typedef struct DvrSurfaceAttributeValue {
+ DvrSurfaceAttributeType type;
+ union {
+ int32_t int32_value;
+ int64_t int64_value;
+ bool bool_value;
+ float float_value;
+ float float2_value[2];
+ float float3_value[3];
+ float float4_value[4];
+ float float8_value[8];
+ float float16_value[16];
+ };
+} DvrSurfaceAttributeValue;
+
+typedef struct DvrSurfaceAttribute {
+ DvrSurfaceAttributeKey key;
+ DvrSurfaceAttributeValue value;
+} DvrSurfaceAttribute;
+
+// Creates a new display surface with the given attributes.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes,
+ size_t attribute_count, DvrSurface** surface_out);
+
+// Destroys the display surface.
+void dvrSurfaceDestroy(DvrSurface* surface);
+
+// Gets the DisplayService global id for this surface.
+int dvrSurfaceGetId(DvrSurface* surface);
+
+// Sets attributes on the given display surface.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceSetAttributes(DvrSurface* surface,
+ const DvrSurfaceAttribute* attributes,
+ size_t attribute_count);
+
+// Creates a new write-side buffer queue on the given surface. Direct surfaces
+// may only have one queue, the latest call replacing any prior queue. Replaced
+// queues are still referenced and should be destryoed using the queue destroy
+// API.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width,
+ uint32_t height, uint32_t format,
+ uint32_t layer_count, uint64_t usage,
+ size_t capacity,
+ DvrWriteBufferQueue** queue_out);
+
+// Get a named buffer from the display service.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrGetNamedBuffer(const char* name, DvrBuffer** out_buffer);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_SURFACE_H_
diff --git a/libs/vr/libdvr/include/dvr/dvr_vsync.h b/libs/vr/libdvr/include/dvr/dvr_vsync.h
new file mode 100644
index 0000000000..1eea3d9133
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_vsync.h
@@ -0,0 +1,26 @@
+#ifndef ANDROID_DVR_VSYNC_H_
+#define ANDROID_DVR_VSYNC_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+typedef struct DvrVSyncClient DvrVSyncClient;
+
+// Creates a new client to the system vsync service.
+int dvrVSyncClientCreate(DvrVSyncClient** client_out);
+
+// Destroys the vsync client.
+void dvrVSyncClientDestroy(DvrVSyncClient* client);
+
+// Get the estimated timestamp of the next GPU lens warp preemption event in/
+// ns. Also returns the corresponding vsync count that the next lens warp
+// operation will target.
+int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
+ int64_t* next_timestamp_ns,
+ uint32_t* next_vsync_count);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_VSYNC_H_
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
new file mode 100644
index 0000000000..ef746e207a
--- /dev/null
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2017 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.
+
+shared_libraries = [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libgui",
+ "liblog",
+ "libhardware",
+ "libui",
+ "libutils",
+ "libnativewindow",
+]
+
+static_libraries = [
+ "libdvr_static",
+ "libbufferhubqueue",
+ "libbufferhub",
+ "libchrome",
+ "libdvrcommon",
+ "libdisplay",
+ "libpdx_default_transport",
+]
+
+cc_test {
+ srcs: [
+ "dvr_buffer_queue-test.cpp",
+ "dvr_display_manager-test.cpp",
+ "dvr_named_buffer-test.cpp",
+ ],
+
+ static_libs: static_libraries,
+ shared_libs: shared_libraries,
+ cflags: [
+ "-DLOG_TAG=\"dvr_api-test\"",
+ "-DTRACE=0",
+ "-O0",
+ "-g",
+ ],
+ name: "dvr_api-test",
+}
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
new file mode 100644
index 0000000000..474e9684c2
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -0,0 +1,217 @@
+#include <dvr/dvr_api.h>
+#include <dvr/dvr_buffer_queue.h>
+#include <gui/Surface.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+#include "../dvr_internal.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+static constexpr int kBufferWidth = 100;
+static constexpr int kBufferHeight = 1;
+static constexpr int kLayerCount = 1;
+static constexpr int kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+static constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
+static constexpr size_t kQueueCapacity = 3;
+
+typedef uint64_t TestMeta;
+
+class DvrBufferQueueTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ write_queue_ = CreateDvrWriteBufferQueueFromProducerQueue(
+ ProducerQueue::Create<TestMeta>(0, 0, 0, 0));
+ ASSERT_NE(nullptr, write_queue_);
+ }
+
+ void TearDown() override {
+ if (write_queue_ != nullptr) {
+ dvrWriteBufferQueueDestroy(write_queue_);
+ write_queue_ = nullptr;
+ }
+ }
+
+ void AllocateBuffers(size_t buffer_count) {
+ size_t out_slot;
+ for (size_t i = 0; i < buffer_count; i++) {
+ int ret = GetProducerQueueFromDvrWriteBufferQueue(write_queue_)
+ ->AllocateBuffer(kBufferWidth, kBufferHeight, kLayerCount,
+ kBufferFormat, kBufferUsage, &out_slot);
+ ASSERT_EQ(0, ret);
+ }
+ }
+
+ DvrWriteBufferQueue* write_queue_{nullptr};
+};
+
+TEST_F(DvrBufferQueueTest, TestWrite_QueueDestroy) {
+ dvrWriteBufferQueueDestroy(write_queue_);
+ write_queue_ = nullptr;
+}
+
+TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) {
+ AllocateBuffers(kQueueCapacity);
+ size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_);
+
+ ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity);
+ ASSERT_EQ(kQueueCapacity, capacity);
+}
+
+TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) {
+ DvrReadBufferQueue* read_queue = nullptr;
+ int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
+
+ ASSERT_EQ(0, ret);
+ ASSERT_NE(nullptr, read_queue);
+
+ dvrReadBufferQueueDestroy(read_queue);
+}
+
+TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) {
+ DvrReadBufferQueue* read_queue1 = nullptr;
+ DvrReadBufferQueue* read_queue2 = nullptr;
+ int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1);
+
+ ASSERT_EQ(0, ret);
+ ASSERT_NE(nullptr, read_queue1);
+
+ ret = dvrReadBufferQueueCreateReadQueue(read_queue1, &read_queue2);
+ ASSERT_EQ(0, ret);
+ ASSERT_NE(nullptr, read_queue2);
+ ASSERT_NE(read_queue1, read_queue2);
+
+ dvrReadBufferQueueDestroy(read_queue1);
+ dvrReadBufferQueueDestroy(read_queue2);
+}
+
+TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) {
+ AllocateBuffers(3);
+
+ DvrReadBuffer* read_buffer = nullptr;
+ DvrWriteBuffer* write_buffer = nullptr;
+
+ EXPECT_FALSE(dvrReadBufferIsValid(read_buffer));
+ EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
+
+ dvrReadBufferCreateEmpty(&read_buffer);
+ ASSERT_NE(nullptr, read_buffer);
+
+ dvrWriteBufferCreateEmpty(&write_buffer);
+ ASSERT_NE(nullptr, write_buffer);
+
+ EXPECT_FALSE(dvrReadBufferIsValid(read_buffer));
+ EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
+
+ DvrReadBufferQueue* read_queue = nullptr;
+
+ ASSERT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
+
+ const int kTimeoutMs = 0;
+ int fence_fd = -1;
+ ASSERT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, kTimeoutMs,
+ write_buffer, &fence_fd));
+ EXPECT_EQ(-1, fence_fd);
+ EXPECT_TRUE(dvrWriteBufferIsValid(write_buffer));
+
+ ASSERT_EQ(0, dvrWriteBufferClear(write_buffer));
+ EXPECT_FALSE(dvrWriteBufferIsValid(write_buffer));
+}
+
+TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) {
+ static constexpr int kTimeout = 0;
+ DvrReadBufferQueue* read_queue = nullptr;
+ DvrReadBuffer* rb = nullptr;
+ DvrWriteBuffer* wb = nullptr;
+ int fence_fd = -1;
+
+ int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
+
+ ASSERT_EQ(0, ret);
+ ASSERT_NE(nullptr, read_queue);
+
+ dvrWriteBufferCreateEmpty(&wb);
+ ASSERT_NE(nullptr, wb);
+
+ dvrReadBufferCreateEmpty(&rb);
+ ASSERT_NE(nullptr, rb);
+
+ AllocateBuffers(kQueueCapacity);
+
+ // Gain buffer for writing.
+ ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb, &fence_fd);
+ ASSERT_EQ(0, ret);
+ ASSERT_TRUE(dvrWriteBufferIsValid(wb));
+ ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d",
+ wb, fence_fd);
+ pdx::LocalHandle release_fence(fence_fd);
+
+ // Post buffer to the read_queue.
+ TestMeta seq = 42U;
+ ret = dvrWriteBufferPost(wb, /* fence */ -1, &seq, sizeof(seq));
+ ASSERT_EQ(0, ret);
+ dvrWriteBufferDestroy(wb);
+ wb = nullptr;
+
+ // Acquire buffer for reading.
+ TestMeta acquired_seq = 0U;
+ ret = dvrReadBufferQueueDequeue(read_queue, kTimeout, rb, &fence_fd,
+ &acquired_seq, sizeof(acquired_seq));
+ ASSERT_EQ(0, ret);
+ ASSERT_TRUE(dvrReadBufferIsValid(rb));
+ ASSERT_EQ(seq, acquired_seq);
+ ALOGD_IF(TRACE,
+ "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb,
+ fence_fd);
+ pdx::LocalHandle acquire_fence(fence_fd);
+
+ // Release buffer to the write_queue.
+ ret = dvrReadBufferRelease(rb, -1);
+ ASSERT_EQ(0, ret);
+ dvrReadBufferDestroy(rb);
+ rb = nullptr;
+
+ // TODO(b/34387835) Currently buffer allocation has to happen after all queues
+ // are initialized.
+ size_t capacity = dvrReadBufferQueueGetCapacity(read_queue);
+
+ ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity);
+ ASSERT_EQ(kQueueCapacity, capacity);
+
+ dvrReadBufferQueueDestroy(read_queue);
+}
+
+TEST_F(DvrBufferQueueTest, TestGetExternalSurface) {
+ ANativeWindow* window = nullptr;
+
+ // The |write_queue_| doesn't have proper metadata (must be
+ // DvrNativeBufferMetadata) configured during creation.
+ int ret = dvrWriteBufferQueueGetExternalSurface(write_queue_, &window);
+ ASSERT_EQ(-EINVAL, ret);
+ ASSERT_EQ(nullptr, window);
+
+ // A write queue with DvrNativeBufferMetadata should work fine.
+ std::unique_ptr<DvrWriteBufferQueue, decltype(&dvrWriteBufferQueueDestroy)>
+ write_queue(
+ CreateDvrWriteBufferQueueFromProducerQueue(
+ ProducerQueue::Create<DvrNativeBufferMetadata>(0, 0, 0, 0)),
+ dvrWriteBufferQueueDestroy);
+ ASSERT_NE(nullptr, write_queue.get());
+
+ ret = dvrWriteBufferQueueGetExternalSurface(write_queue.get(), &window);
+ ASSERT_EQ(0, ret);
+ ASSERT_NE(nullptr, window);
+
+ sp<Surface> surface = static_cast<Surface*>(window);
+ ASSERT_TRUE(Surface::isValid(surface));
+}
+
+} // namespace
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
new file mode 100644
index 0000000000..a2414d65d8
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -0,0 +1,579 @@
+#include <base/logging.h>
+#include <gtest/gtest.h>
+#include <poll.h>
+
+#include <android/hardware_buffer.h>
+
+#include <algorithm>
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <dvr/dvr_deleter.h>
+#include <dvr/dvr_display_manager.h>
+#include <dvr/dvr_surface.h>
+
+#include <pdx/status.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+DvrSurfaceAttribute GetAttribute(DvrSurfaceAttributeKey key, bool value) {
+ DvrSurfaceAttribute attribute;
+ attribute.key = key;
+ attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
+ attribute.value.bool_value = value;
+ return attribute;
+}
+
+DvrSurfaceAttribute GetAttribute(DvrSurfaceAttributeKey key, int32_t value) {
+ DvrSurfaceAttribute attribute;
+ attribute.key = key;
+ attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
+ attribute.value.bool_value = value;
+ return attribute;
+}
+
+Status<UniqueDvrSurface> CreateApplicationSurface(bool visible = false,
+ int32_t z_order = 0) {
+ DvrSurface* surface = nullptr;
+ DvrSurfaceAttribute attributes[] = {
+ GetAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order),
+ GetAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)};
+
+ const int ret = dvrSurfaceCreate(
+ attributes, std::extent<decltype(attributes)>::value, &surface);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else
+ return {UniqueDvrSurface(surface)};
+}
+
+Status<UniqueDvrWriteBufferQueue> CreateSurfaceQueue(
+ const UniqueDvrSurface& surface, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity) {
+ DvrWriteBufferQueue* queue;
+ const int ret =
+ dvrSurfaceCreateWriteBufferQueue(surface.get(), width, height, format,
+ layer_count, usage, capacity, &queue);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else
+ return {UniqueDvrWriteBufferQueue(queue)};
+}
+
+class TestDisplayManager {
+ public:
+ TestDisplayManager(UniqueDvrDisplayManager display_manager,
+ UniqueDvrSurfaceState surface_state)
+ : display_manager_(std::move(display_manager)),
+ surface_state_(std::move(surface_state)) {
+ const int fd = dvrDisplayManagerGetEventFd(display_manager_.get());
+ LOG_IF(INFO, fd < 0) << "Failed to get event fd: " << strerror(-fd);
+ display_manager_event_fd_ = fd;
+ }
+
+ Status<UniqueDvrReadBufferQueue> GetReadBufferQueue(int surface_id,
+ int queue_id) {
+ DvrReadBufferQueue* queue;
+ const int ret = dvrDisplayManagerGetReadBufferQueue(
+ display_manager_.get(), surface_id, queue_id, &queue);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else
+ return {UniqueDvrReadBufferQueue(queue)};
+ }
+
+ Status<void> UpdateSurfaceState() {
+ const int ret = dvrDisplayManagerGetSurfaceState(display_manager_.get(),
+ surface_state_.get());
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else
+ return {};
+ }
+
+ Status<void> WaitForUpdate() {
+ if (display_manager_event_fd_ < 0)
+ return ErrorStatus(-display_manager_event_fd_);
+
+ const int kTimeoutMs = 10000; // 10s
+ pollfd pfd = {display_manager_event_fd_, POLLIN, 0};
+ const int count = poll(&pfd, 1, kTimeoutMs);
+ if (count < 0)
+ return ErrorStatus(errno);
+ else if (count == 0)
+ return ErrorStatus(ETIMEDOUT);
+
+ int events;
+ const int ret = dvrDisplayManagerTranslateEpollEventMask(
+ display_manager_.get(), pfd.revents, &events);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else if (events & POLLIN)
+ return UpdateSurfaceState();
+ else
+ return ErrorStatus(EPROTO);
+ }
+
+ Status<size_t> GetSurfaceCount() {
+ size_t count = 0;
+ const int ret =
+ dvrSurfaceStateGetSurfaceCount(surface_state_.get(), &count);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else
+ return {count};
+ }
+
+ Status<DvrSurfaceUpdateFlags> GetUpdateFlags(size_t surface_index) {
+ DvrSurfaceUpdateFlags update_flags;
+ const int ret = dvrSurfaceStateGetUpdateFlags(surface_state_.get(),
+ surface_index, &update_flags);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else
+ return {update_flags};
+ }
+
+ Status<int> GetSurfaceId(size_t surface_index) {
+ int surface_id;
+ const int ret = dvrSurfaceStateGetSurfaceId(surface_state_.get(),
+ surface_index, &surface_id);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else
+ return {surface_id};
+ }
+
+ Status<int> GetProcessId(size_t surface_index) {
+ int process_id;
+ const int ret = dvrSurfaceStateGetProcessId(surface_state_.get(),
+ surface_index, &process_id);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+ else
+ return {process_id};
+ }
+
+ Status<std::vector<DvrSurfaceAttribute>> GetAttributes(size_t surface_index) {
+ std::vector<DvrSurfaceAttribute> attributes;
+ size_t count = 0;
+ const int ret = dvrSurfaceStateGetAttributeCount(surface_state_.get(),
+ surface_index, &count);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+
+ attributes.resize(count);
+ const ssize_t return_count = dvrSurfaceStateGetAttributes(
+ surface_state_.get(), surface_index, attributes.data(), count);
+ if (return_count < 0)
+ return ErrorStatus(-return_count);
+
+ attributes.resize(return_count);
+ return {std::move(attributes)};
+ }
+
+ Status<std::vector<int>> GetQueueIds(size_t surface_index) {
+ std::vector<int> queue_ids;
+ size_t count = 0;
+ const int ret = dvrSurfaceStateGetQueueCount(surface_state_.get(),
+ surface_index, &count);
+ if (ret < 0)
+ return ErrorStatus(-ret);
+
+ if (count > 0) {
+ queue_ids.resize(count);
+ const ssize_t return_count = dvrSurfaceStateGetQueueIds(
+ surface_state_.get(), surface_index, queue_ids.data(), count);
+ if (return_count < 0)
+ return ErrorStatus(-return_count);
+
+ queue_ids.resize(return_count);
+ }
+
+ return {std::move(queue_ids)};
+ }
+
+ private:
+ UniqueDvrDisplayManager display_manager_;
+ UniqueDvrSurfaceState surface_state_;
+
+ // Owned by object in display_manager_, do not explicitly close.
+ int display_manager_event_fd_;
+
+ TestDisplayManager(const TestDisplayManager&) = delete;
+ void operator=(const TestDisplayManager&) = delete;
+};
+
+class DvrDisplayManagerTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ int ret;
+ DvrDisplayManager* display_manager;
+ DvrSurfaceState* surface_state;
+
+ ret = dvrDisplayManagerCreate(&display_manager);
+ ASSERT_EQ(0, ret) << "Failed to create display manager client";
+ ASSERT_NE(nullptr, display_manager);
+
+ ret = dvrSurfaceStateCreate(&surface_state);
+ ASSERT_EQ(0, ret) << "Failed to create surface state object";
+ ASSERT_NE(nullptr, surface_state);
+
+ manager_.reset(
+ new TestDisplayManager(UniqueDvrDisplayManager(display_manager),
+ UniqueDvrSurfaceState(surface_state)));
+ }
+ void TearDown() override {}
+
+ std::unique_ptr<TestDisplayManager> manager_;
+};
+
+// TODO(eieio): Consider moving these somewhere more central because they are
+// broadly useful.
+
+template <typename T>
+testing::AssertionResult StatusOk(const char* status_expression,
+ const Status<T>& status) {
+ if (!status.ok()) {
+ return testing::AssertionFailure()
+ << "(" << status_expression
+ << ") expected to indicate success but actually contains error ("
+ << status.error() << ")";
+ } else {
+ return testing::AssertionSuccess();
+ }
+}
+
+template <typename T>
+testing::AssertionResult StatusError(const char* status_expression,
+ const Status<T>& status) {
+ if (status.ok()) {
+ return testing::AssertionFailure()
+ << "(" << status_expression
+ << ") expected to indicate error but instead indicates success.";
+ } else {
+ return testing::AssertionSuccess();
+ }
+}
+
+template <typename T>
+testing::AssertionResult StatusHasError(const char* status_expression,
+ const char* /*error_code_expression*/,
+ const Status<T>& status,
+ int error_code) {
+ if (status.ok()) {
+ return StatusError(status_expression, status);
+ } else if (status.error() != error_code) {
+ return testing::AssertionFailure()
+ << "(" << status_expression << ") expected to indicate error ("
+ << error_code << ") but actually indicates error (" << status.error()
+ << ")";
+ } else {
+ return testing::AssertionSuccess();
+ }
+}
+
+template <typename T, typename U>
+testing::AssertionResult StatusHasValue(const char* status_expression,
+ const char* /*value_expression*/,
+ const Status<T>& status,
+ const U& value) {
+ if (!status.ok()) {
+ return StatusOk(status_expression, status);
+ } else if (status.get() != value) {
+ return testing::AssertionFailure()
+ << "(" << status_expression << ") expected to contain value ("
+ << testing::PrintToString(value) << ") but actually contains value ("
+ << testing::PrintToString(status.get()) << ")";
+ } else {
+ return testing::AssertionSuccess();
+ }
+}
+
+template <typename T, typename Op>
+testing::AssertionResult StatusPred(const char* status_expression,
+ const char* pred_expression,
+ const Status<T>& status, Op pred) {
+ if (!status.ok()) {
+ return StatusOk(status_expression, status);
+ } else if (!pred(status.get())) {
+ return testing::AssertionFailure()
+ << status_expression << " value ("
+ << testing::PrintToString(status.get())
+ << ") failed to pass predicate " << pred_expression;
+ } else {
+ return testing::AssertionSuccess();
+ }
+}
+
+#define ASSERT_STATUS_OK(status) ASSERT_PRED_FORMAT1(StatusOk, status)
+#define ASSERT_STATUS_ERROR(status) ASSERT_PRED_FORMAT1(StatusError, status)
+
+#define ASSERT_STATUS_ERROR_VALUE(value, status) \
+ ASSERT_PRED_FORMAT2(StatusHasError, status, value)
+
+#define ASSERT_STATUS_EQ(value, status) \
+ ASSERT_PRED_FORMAT2(StatusHasValue, status, value)
+
+#define EXPECT_STATUS_OK(status) EXPECT_PRED_FORMAT1(StatusOk, status)
+#define EXPECT_STATUS_ERROR(status) EXPECT_PRED_FORMAT1(StatusError, status)
+
+#define EXPECT_STATUS_ERROR_VALUE(value, status) \
+ EXPECT_PRED_FORMAT2(StatusHasError, status, value)
+
+#define EXPECT_STATUS_EQ(value, status) \
+ EXPECT_PRED_FORMAT2(StatusHasValue, status, value)
+
+#define EXPECT_STATUS_PRED(pred, status) \
+ EXPECT_PRED_FORMAT2(StatusPred, status, pred)
+
+#if 0
+// Verify utility predicate/macro functionality. This section is commented out
+// because it is designed to fail in some cases to validate the helpers.
+TEST_F(DvrDisplayManagerTest, ExpectVoid) {
+ Status<void> status_error{ErrorStatus{EINVAL}};
+ Status<void> status_ok{};
+
+ EXPECT_STATUS_ERROR(status_error);
+ EXPECT_STATUS_ERROR(status_ok);
+ EXPECT_STATUS_OK(status_error);
+ EXPECT_STATUS_OK(status_ok);
+
+ EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
+ EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
+ EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
+ EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
+}
+
+TEST_F(DvrDisplayManagerTest, ExpectInt) {
+ Status<int> status_error{ErrorStatus{EINVAL}};
+ Status<int> status_ok{10};
+
+ EXPECT_STATUS_ERROR(status_error);
+ EXPECT_STATUS_ERROR(status_ok);
+ EXPECT_STATUS_OK(status_error);
+ EXPECT_STATUS_OK(status_ok);
+
+ EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
+ EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
+ EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
+ EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
+
+ EXPECT_STATUS_EQ(10, status_error);
+ EXPECT_STATUS_EQ(20, status_error);
+ EXPECT_STATUS_EQ(10, status_ok);
+ EXPECT_STATUS_EQ(20, status_ok);
+
+ auto pred1 = [](const auto& value) { return value < 15; };
+ auto pred2 = [](const auto& value) { return value > 5; };
+ auto pred3 = [](const auto& value) { return value > 15; };
+ auto pred4 = [](const auto& value) { return value < 5; };
+
+ EXPECT_STATUS_PRED(pred1, status_error);
+ EXPECT_STATUS_PRED(pred2, status_error);
+ EXPECT_STATUS_PRED(pred3, status_error);
+ EXPECT_STATUS_PRED(pred4, status_error);
+ EXPECT_STATUS_PRED(pred1, status_ok);
+ EXPECT_STATUS_PRED(pred2, status_ok);
+ EXPECT_STATUS_PRED(pred3, status_ok);
+ EXPECT_STATUS_PRED(pred4, status_ok);
+}
+#endif
+
+TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
+ // Get surface state and verify there are no surfaces.
+ ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
+ ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
+
+ // Get flags for invalid surface index.
+ EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetUpdateFlags(0));
+
+ // Create an application surface.
+ auto surface_status = CreateApplicationSurface();
+ ASSERT_STATUS_OK(surface_status);
+ UniqueDvrSurface surface = surface_status.take();
+ ASSERT_NE(nullptr, surface.get());
+
+ const int surface_id = dvrSurfaceGetId(surface.get());
+ ASSERT_GE(surface_id, 0);
+
+ // Now there should be one new surface.
+ ASSERT_STATUS_OK(manager_->WaitForUpdate());
+ EXPECT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+ // Verify the new surface flag is set.
+ auto check_flags = [](const auto& value) {
+ return value & DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE;
+ };
+ EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+ // Verify the surface id matches.
+ EXPECT_STATUS_EQ(surface_id, manager_->GetSurfaceId(0));
+
+ // Verify the owning process of the surface.
+ EXPECT_STATUS_EQ(getpid(), manager_->GetProcessId(0));
+
+ surface.reset();
+
+ ASSERT_STATUS_OK(manager_->WaitForUpdate());
+ EXPECT_STATUS_EQ(0u, manager_->GetSurfaceCount());
+}
+
+TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
+ // Get surface state and verify there are no surfaces.
+ ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
+ ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
+
+ // Get attributes for an invalid surface index.
+ EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetAttributes(0));
+
+ const bool kInitialVisibility = true;
+ const int32_t kInitialZOrder = 10;
+ auto surface_status =
+ CreateApplicationSurface(kInitialVisibility, kInitialZOrder);
+ ASSERT_STATUS_OK(surface_status);
+ auto surface = surface_status.take();
+ ASSERT_NE(nullptr, surface.get());
+
+ ASSERT_STATUS_OK(manager_->WaitForUpdate());
+ ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+ // Check the initial attribute values.
+ auto attribute_status = manager_->GetAttributes(0);
+ ASSERT_STATUS_OK(attribute_status);
+ auto attributes = attribute_status.take();
+ EXPECT_GE(attributes.size(), 2u);
+
+ const std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
+ DVR_SURFACE_ATTRIBUTE_VISIBLE};
+
+ // Collect all the keys in attributes that match the expected keys.
+ std::set<int32_t> actual_keys;
+ std::for_each(attributes.begin(), attributes.end(),
+ [&expected_keys, &actual_keys](const auto& attribute) {
+ if (expected_keys.find(attribute.key) != expected_keys.end())
+ actual_keys.emplace(attribute.key);
+ });
+
+ // If the sets match then attributes contained at least the expected keys,
+ // even if other keys were also present.
+ EXPECT_EQ(expected_keys, actual_keys);
+}
+
+TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
+ // Create an application surface.
+ auto surface_status = CreateApplicationSurface();
+ ASSERT_STATUS_OK(surface_status);
+ UniqueDvrSurface surface = surface_status.take();
+ ASSERT_NE(nullptr, surface.get());
+
+ const int surface_id = dvrSurfaceGetId(surface.get());
+ ASSERT_GE(surface_id, 0);
+ // Get surface state and verify there is one surface.
+ ASSERT_STATUS_OK(manager_->WaitForUpdate());
+ ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+ // Verify there are no queues for the surface recorded in the state snapshot.
+ EXPECT_STATUS_EQ(std::vector<int>{}, manager_->GetQueueIds(0));
+
+ // Create a new queue in the surface.
+ auto write_queue_status = CreateSurfaceQueue(
+ surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 1,
+ AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1);
+ ASSERT_STATUS_OK(write_queue_status);
+ UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
+ ASSERT_NE(nullptr, write_queue.get());
+
+ const int queue_id = dvrWriteBufferQueueGetId(write_queue.get());
+ ASSERT_GE(queue_id, 0);
+
+ // Update surface state.
+ ASSERT_STATUS_OK(manager_->WaitForUpdate());
+ ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+ // Verify the buffers changed flag is set.
+ auto check_flags = [](const auto& value) {
+ return value & DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED;
+ };
+ EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+ auto queue_ids_status = manager_->GetQueueIds(0);
+ ASSERT_STATUS_OK(queue_ids_status);
+
+ auto queue_ids = queue_ids_status.take();
+ ASSERT_EQ(1u, queue_ids.size());
+ EXPECT_EQ(queue_id, queue_ids[0]);
+
+ auto read_queue_status = manager_->GetReadBufferQueue(surface_id, queue_id);
+ ASSERT_STATUS_OK(read_queue_status);
+ UniqueDvrReadBufferQueue read_queue = read_queue_status.take();
+ ASSERT_NE(nullptr, read_queue.get());
+ EXPECT_EQ(queue_id, dvrReadBufferQueueGetId(read_queue.get()));
+
+ write_queue.reset();
+
+ // Verify that destroying the queue generates a surface update event.
+ ASSERT_STATUS_OK(manager_->WaitForUpdate());
+ ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+ // Verify that the buffers changed flag is set.
+ EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+ // Verify that the queue ids reflect the change.
+ queue_ids_status = manager_->GetQueueIds(0);
+ ASSERT_STATUS_OK(queue_ids_status);
+
+ queue_ids = queue_ids_status.take();
+ ASSERT_EQ(0u, queue_ids.size());
+}
+
+TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
+ // Create an application surface.
+ auto surface_status = CreateApplicationSurface();
+ ASSERT_STATUS_OK(surface_status);
+ UniqueDvrSurface surface = surface_status.take();
+ ASSERT_NE(nullptr, surface.get());
+
+ // Get surface state and verify there is one surface.
+ ASSERT_STATUS_OK(manager_->WaitForUpdate());
+ ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
+
+ // Create a new queue in the surface.
+ const uint32_t kLayerCount = 3;
+ auto write_queue_status = CreateSurfaceQueue(
+ surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kLayerCount,
+ AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1);
+ ASSERT_STATUS_OK(write_queue_status);
+ UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
+ ASSERT_NE(nullptr, write_queue.get());
+
+ DvrWriteBuffer* buffer = nullptr;
+ dvrWriteBufferCreateEmpty(&buffer);
+ int fence_fd = -1;
+ int error =
+ dvrWriteBufferQueueDequeue(write_queue.get(), 1000, buffer, &fence_fd);
+ ASSERT_EQ(0, error);
+
+ AHardwareBuffer* hardware_buffer = nullptr;
+ error = dvrWriteBufferGetAHardwareBuffer(buffer, &hardware_buffer);
+ ASSERT_EQ(0, error);
+
+ AHardwareBuffer_Desc desc = {};
+ AHardwareBuffer_describe(hardware_buffer, &desc);
+ ASSERT_EQ(kLayerCount, desc.layers);
+
+ AHardwareBuffer_release(hardware_buffer);
+ dvrWriteBufferDestroy(buffer);
+}
+
+} // namespace
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
new file mode 100644
index 0000000000..e65f6d5b01
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
@@ -0,0 +1,160 @@
+#include <android/hardware_buffer.h>
+#include <dvr/dvr_buffer.h>
+#include <dvr/dvr_display_manager.h>
+#include <dvr/dvr_surface.h>
+#include <system/graphics.h>
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class DvrNamedBufferTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ const int ret = dvrDisplayManagerCreate(&client_);
+ ASSERT_EQ(0, ret);
+ ASSERT_NE(nullptr, client_);
+ }
+
+ void TearDown() override {
+ dvrDisplayManagerDestroy(client_);
+ client_ = nullptr;
+ }
+
+ DvrDisplayManager* client_ = nullptr;
+};
+
+TEST_F(DvrNamedBufferTest, TestNamedBuffersSameName) {
+ const char* buffer_name = "same_name";
+ DvrBuffer* buffer1 = nullptr;
+ int ret1 =
+ dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, 0, &buffer1);
+ ASSERT_EQ(0, ret1);
+ ASSERT_NE(nullptr, buffer1);
+
+ DvrBuffer* buffer2 = nullptr;
+ int ret2 =
+ dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, 0, &buffer2);
+ ASSERT_EQ(0, ret1);
+ ASSERT_NE(nullptr, buffer2);
+
+ AHardwareBuffer* hardware_buffer1 = nullptr;
+ int e1 = dvrBufferGetAHardwareBuffer(buffer1, &hardware_buffer1);
+ ASSERT_EQ(0, e1);
+ ASSERT_NE(nullptr, hardware_buffer1);
+
+ AHardwareBuffer* hardware_buffer2 = nullptr;
+ int e2 = dvrBufferGetAHardwareBuffer(buffer2, &hardware_buffer2);
+ ASSERT_EQ(0, e2);
+ ASSERT_NE(nullptr, hardware_buffer2);
+
+ AHardwareBuffer_Desc desc1 = {};
+ AHardwareBuffer_describe(hardware_buffer1, &desc1);
+ AHardwareBuffer_Desc desc2 = {};
+ AHardwareBuffer_describe(hardware_buffer2, &desc2);
+ ASSERT_EQ(desc1.width, 10u);
+ ASSERT_EQ(desc1.height, 1u);
+ ASSERT_EQ(desc1.layers, 1u);
+ ASSERT_EQ(desc1.format, HAL_PIXEL_FORMAT_BLOB);
+ ASSERT_EQ(desc1.usage, 0u);
+ ASSERT_EQ(desc2.width, 10u);
+ ASSERT_EQ(desc2.height, 1u);
+ ASSERT_EQ(desc2.layers, 1u);
+ ASSERT_EQ(desc2.format, HAL_PIXEL_FORMAT_BLOB);
+ ASSERT_EQ(desc2.usage, 0u);
+
+ dvrBufferDestroy(buffer1);
+ dvrBufferDestroy(buffer2);
+
+ DvrBuffer* buffer3 = nullptr;
+ int e3 = dvrGetNamedBuffer(buffer_name, &buffer3);
+ ASSERT_NE(nullptr, buffer3);
+ ASSERT_EQ(0, e3);
+
+ AHardwareBuffer* hardware_buffer3 = nullptr;
+ int e4 = dvrBufferGetAHardwareBuffer(buffer3, &hardware_buffer3);
+ ASSERT_EQ(0, e4);
+ ASSERT_NE(nullptr, hardware_buffer3);
+
+ AHardwareBuffer_Desc desc3 = {};
+ AHardwareBuffer_describe(hardware_buffer3, &desc3);
+ ASSERT_EQ(desc3.width, 10u);
+ ASSERT_EQ(desc3.height, 1u);
+ ASSERT_EQ(desc3.layers, 1u);
+ ASSERT_EQ(desc3.format, HAL_PIXEL_FORMAT_BLOB);
+ ASSERT_EQ(desc3.usage, 0u);
+
+ dvrBufferDestroy(buffer3);
+
+ AHardwareBuffer_release(hardware_buffer1);
+ AHardwareBuffer_release(hardware_buffer2);
+ AHardwareBuffer_release(hardware_buffer3);
+}
+
+TEST_F(DvrNamedBufferTest, TestMultipleNamedBuffers) {
+ const char* buffer_name1 = "test1";
+ const char* buffer_name2 = "test2";
+ DvrBuffer* setup_buffer1 = nullptr;
+ int ret1 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name1, 10, 0,
+ &setup_buffer1);
+ ASSERT_EQ(0, ret1);
+ ASSERT_NE(nullptr, setup_buffer1);
+ dvrBufferDestroy(setup_buffer1);
+
+ DvrBuffer* setup_buffer2 = nullptr;
+ int ret2 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name2, 10, 0,
+ &setup_buffer2);
+ ASSERT_EQ(0, ret2);
+ ASSERT_NE(nullptr, setup_buffer2);
+ dvrBufferDestroy(setup_buffer2);
+
+ DvrBuffer* buffer1 = nullptr;
+ int e1 = dvrGetNamedBuffer(buffer_name1, &buffer1);
+ ASSERT_NE(nullptr, buffer1);
+ ASSERT_EQ(0, e1);
+ dvrBufferDestroy(buffer1);
+
+ DvrBuffer* buffer2 = nullptr;
+ int e2 = dvrGetNamedBuffer(buffer_name2, &buffer2);
+ ASSERT_NE(nullptr, buffer2);
+ ASSERT_EQ(0, e2);
+ dvrBufferDestroy(buffer2);
+}
+
+TEST_F(DvrNamedBufferTest, TestNamedBufferUsage) {
+ const char* buffer_name = "buffer_usage";
+
+ // Set usage to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. We use this because
+ // internally AHARDWAREBUFFER_USAGE_VIDEO_ENCODE is converted to
+ // GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER, and these two values are different.
+ // If all is good, when we get the AHardwareBuffer, it should be converted
+ // back to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE.
+ const uint64_t usage = AHARDWAREBUFFER_USAGE_VIDEO_ENCODE;
+
+ DvrBuffer* setup_buffer = nullptr;
+ int e1 = dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, usage,
+ &setup_buffer);
+ ASSERT_NE(nullptr, setup_buffer);
+ ASSERT_EQ(0, e1);
+
+ AHardwareBuffer* hardware_buffer = nullptr;
+ int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
+ ASSERT_EQ(0, e2);
+ ASSERT_NE(nullptr, hardware_buffer);
+
+ AHardwareBuffer_Desc desc = {};
+ AHardwareBuffer_describe(hardware_buffer, &desc);
+ ASSERT_EQ(usage, desc.usage);
+
+ dvrBufferDestroy(setup_buffer);
+ AHardwareBuffer_release(hardware_buffer);
+}
+
+} // namespace
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
new file mode 100644
index 0000000000..527cdbdd2a
--- /dev/null
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -0,0 +1,71 @@
+// Copyright (C) 2016 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.
+
+localIncludeFiles = [
+ "include",
+]
+
+sharedLibraries = [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libEGL",
+ "libGLESv2",
+ "libui",
+ "libgui",
+ "libhardware",
+]
+
+staticLibraries = ["libpdx_default_transport"]
+
+headerLibraries = [
+ "libeigen",
+]
+
+cc_library {
+ local_include_dirs: localIncludeFiles,
+
+ cflags: [
+ "-DLOG_TAG=\"libdvrcommon\"",
+ "-DTRACE=0",
+ ],
+ export_include_dirs: localIncludeFiles,
+
+ header_libs: headerLibraries,
+ export_header_lib_headers: headerLibraries,
+
+ name: "libdvrcommon",
+}
+
+testFiles = [
+ "tests/numeric_test.cpp",
+ "tests/pose_test.cpp",
+]
+
+cc_test {
+ name: "libdvrcommon_test",
+ tags: ["optional"],
+
+ srcs: testFiles,
+
+ shared_libs: sharedLibraries,
+
+ static_libs: [
+ "libgmock_main",
+ "libgmock",
+ "libgtest",
+ "libdvrcommon",
+ ] + staticLibraries,
+}
diff --git a/libs/vr/libdvrcommon/include/private/dvr/benchmark.h b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
new file mode 100644
index 0000000000..7eeab16030
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
@@ -0,0 +1,86 @@
+#ifndef ANDROID_DVR_BENCHMARK_H_
+#define ANDROID_DVR_BENCHMARK_H_
+
+#include <stdio.h>
+#include <time.h>
+
+#include <cutils/trace.h>
+
+#include <private/dvr/clock_ns.h>
+
+// Set benchmark traces, using Android systrace.
+//
+// The simplest one-parameter version of btrace automatically sets the
+// timestamp with the system clock. The other versions can optionally set the
+// timestamp manually, or pass additional data to be written to the log line.
+//
+// Example:
+// Btrace("Start execution");
+// ... code to benchmark ...
+// Btrace("End execution");
+//
+// Use compute_benchmarks.py
+// with the trace path "Start execution,End execution",
+// to report the elapsed time between the two calls.
+//
+// Btrace will either output to standard atrace, or to a file if specified.
+// The versions BtraceData also allow an int64_t to be included in the trace.
+
+// Btrace without data payload.
+static inline void Btrace(const char* name, int64_t nanoseconds_monotonic);
+static inline void Btrace(const char* name);
+static inline void Btrace(FILE* file, const char* name,
+ int64_t nanoseconds_monotonic);
+static inline void Btrace(FILE* file, const char* name);
+
+// Btrace with data payload.
+static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic,
+ int64_t data);
+static inline void BtraceData(const char* name, int64_t data);
+static inline void BtraceData(FILE* file, const char* name,
+ int64_t nanoseconds_monotonic, int64_t data);
+static inline void BtraceData(FILE* file, const char* name, int64_t data);
+
+static inline void Btrace(const char* name, int64_t nanoseconds_monotonic) {
+ const int kLogMessageLength = 256;
+ char log_message[kLogMessageLength];
+ snprintf(log_message, kLogMessageLength, "#btrace#%s", name);
+ atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic);
+}
+
+static inline void Btrace(const char* name) {
+ Btrace(name, android::dvr::GetSystemClockNs());
+}
+
+static inline void Btrace(FILE* file, const char* name,
+ int64_t nanoseconds_monotonic) {
+ fprintf(file, "#btrace#%s|%" PRId64 "\n", name, nanoseconds_monotonic);
+}
+
+static inline void Btrace(FILE* file, const char* name) {
+ Btrace(file, name, android::dvr::GetSystemClockNs());
+}
+
+static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic,
+ int64_t data) {
+ const int kLogMessageLength = 256;
+ char log_message[kLogMessageLength];
+ snprintf(log_message, kLogMessageLength, "#btrace#%s|%" PRId64, name, data);
+ atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic);
+}
+
+static inline void BtraceData(const char* name, int64_t data) {
+ BtraceData(name, android::dvr::GetSystemClockNs(), data);
+}
+
+static inline void BtraceData(FILE* file, const char* name,
+ int64_t nanoseconds_monotonic, int64_t data) {
+ fprintf(file, "#btrace#%s|%" PRId64 "|%" PRId64 "\n", name, data,
+ nanoseconds_monotonic);
+}
+
+static inline void BtraceData(FILE* file, const char* name, int64_t data) {
+ BtraceData(file, name, android::dvr::GetSystemClockNs(), data);
+}
+
+#endif // ANDROID_DVR_BENCHMARK_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h
new file mode 100644
index 0000000000..8e777edf6d
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/clock_ns.h
@@ -0,0 +1,84 @@
+#ifndef ANDROID_DVR_CLOCK_NS_H_
+#define ANDROID_DVR_CLOCK_NS_H_
+
+#include <stdint.h>
+#include <time.h>
+
+namespace android {
+namespace dvr {
+
+constexpr int64_t kNanosPerSecond = 1000000000ll;
+
+// Returns the standard Dream OS monotonic system time that corresponds with all
+// timestamps found in Dream OS APIs.
+static inline timespec GetSystemClock() {
+ timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return t;
+}
+
+static inline timespec GetSystemClockRaw() {
+ timespec t;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &t);
+ return t;
+}
+
+static inline int64_t GetSystemClockNs() {
+ timespec t = GetSystemClock();
+ int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec;
+ return ns;
+}
+
+static inline int64_t GetSystemClockRawNs() {
+ timespec t = GetSystemClockRaw();
+ int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec;
+ return ns;
+}
+
+static inline double NsToSec(int64_t nanoseconds) {
+ return nanoseconds / static_cast<double>(kNanosPerSecond);
+}
+
+static inline double GetSystemClockSec() { return NsToSec(GetSystemClockNs()); }
+
+static inline double GetSystemClockMs() { return GetSystemClockSec() * 1000.0; }
+
+// Converts a nanosecond timestamp to a timespec. Based on the kernel function
+// of the same name.
+static inline timespec NsToTimespec(int64_t ns) {
+ timespec t;
+ int32_t remainder;
+
+ t.tv_sec = ns / kNanosPerSecond;
+ remainder = ns % kNanosPerSecond;
+ if (remainder < 0) {
+ t.tv_nsec--;
+ remainder += kNanosPerSecond;
+ }
+ t.tv_nsec = remainder;
+
+ return t;
+}
+
+// Timestamp comparison functions that handle wrapping values correctly.
+static inline bool TimestampLT(int64_t a, int64_t b) {
+ return static_cast<int64_t>(static_cast<uint64_t>(a) -
+ static_cast<uint64_t>(b)) < 0;
+}
+static inline bool TimestampLE(int64_t a, int64_t b) {
+ return static_cast<int64_t>(static_cast<uint64_t>(a) -
+ static_cast<uint64_t>(b)) <= 0;
+}
+static inline bool TimestampGT(int64_t a, int64_t b) {
+ return static_cast<int64_t>(static_cast<uint64_t>(a) -
+ static_cast<uint64_t>(b)) > 0;
+}
+static inline bool TimestampGE(int64_t a, int64_t b) {
+ return static_cast<int64_t>(static_cast<uint64_t>(a) -
+ static_cast<uint64_t>(b)) >= 0;
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_CLOCK_NS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/debug.h b/libs/vr/libdvrcommon/include/private/dvr/debug.h
new file mode 100644
index 0000000000..c31a385ffb
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/debug.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_DEBUG_H_
+#define ANDROID_DVR_DEBUG_H_
+
+#include <GLES3/gl3.h>
+#include <math.h>
+
+#include <log/log.h>
+
+#ifndef NDEBUG
+#define CHECK_GL() \
+ do { \
+ const GLenum err = glGetError(); \
+ if (err != GL_NO_ERROR) { \
+ ALOGE("OpenGL error %d", err); \
+ } \
+ } while (0)
+
+#define CHECK_GL_FBO() \
+ do { \
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); \
+ switch (status) { \
+ case GL_FRAMEBUFFER_COMPLETE: \
+ break; \
+ case GL_FRAMEBUFFER_UNSUPPORTED: \
+ ALOGE("GL_FRAMEBUFFER_UNSUPPORTED"); \
+ break; \
+ default: \
+ ALOGE("FBO user error: %d", status); \
+ break; \
+ } \
+ } while (0)
+#else
+#define CHECK_GL()
+#define CHECK_GL_FBO()
+#endif
+
+#endif // ANDROID_DVR_DEBUG_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/eigen.h b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
new file mode 100644
index 0000000000..defaf583c4
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
@@ -0,0 +1,57 @@
+#ifndef ANDROID_DVR_EIGEN_H_
+#define ANDROID_DVR_EIGEN_H_
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+namespace Eigen {
+
+// Eigen doesn't take advantage of C++ template typedefs, but we can
+template <class T, int N>
+using Vector = Matrix<T, N, 1>;
+
+template <class T>
+using Vector2 = Vector<T, 2>;
+
+template <class T>
+using Vector3 = Vector<T, 3>;
+
+template <class T>
+using Vector4 = Vector<T, 4>;
+
+template <class T, int N>
+using RowVector = Matrix<T, 1, N>;
+
+template <class T>
+using RowVector2 = RowVector<T, 2>;
+
+template <class T>
+using RowVector3 = RowVector<T, 3>;
+
+template <class T>
+using RowVector4 = RowVector<T, 4>;
+
+// In Eigen, the type you should be using for transformation matrices is the
+// `Transform` class, instead of a raw `Matrix`.
+// The `Projective` option means this will not make any assumptions about the
+// last row of the object, making this suitable for use as general OpenGL
+// projection matrices (which is the most common use-case). The one caveat
+// is that in order to apply this transformation to non-homogeneous vectors
+// (e.g., vec3), you must use the `.linear()` method to get the affine part of
+// the matrix.
+//
+// Example:
+// mat4 transform;
+// vec3 position;
+// vec3 transformed = transform.linear() * position;
+//
+// Note, the use of N-1 is because the parameter passed to Eigen is the ambient
+// dimension of the transformation, not the size of the matrix iself.
+// However graphics programmers sometimes get upset when they see a 3 next
+// to a matrix when they expect a 4, so I'm hoping this will avoid that.
+template <class T, int N>
+using AffineMatrix = Transform<T, N-1, Projective>;
+
+} // namespace Eigen
+
+#endif // ANDROID_DVR_EIGEN_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
new file mode 100644
index 0000000000..099a409005
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
@@ -0,0 +1,64 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+
+namespace android {
+namespace dvr {
+
+class EpollFileDescriptor {
+ public:
+ static const int CTL_ADD = EPOLL_CTL_ADD;
+ static const int CTL_MOD = EPOLL_CTL_MOD;
+ static const int CTL_DEL = EPOLL_CTL_DEL;
+
+ EpollFileDescriptor() : fd_(-1) {}
+
+ // Constructs an EpollFileDescriptor from an integer file descriptor and
+ // takes ownership.
+ explicit EpollFileDescriptor(int fd) : fd_(fd) {}
+
+ bool IsValid() const { return fd_.get() >= 0; }
+
+ int Create() {
+ if (IsValid()) {
+ ALOGW("epoll fd has already been created.");
+ return -EALREADY;
+ }
+
+ fd_.reset(epoll_create(64));
+
+ if (fd_.get() < 0)
+ return -errno;
+ else
+ return 0;
+ }
+
+ int Control(int op, int target_fd, epoll_event* ev) {
+ if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
+ return -errno;
+ else
+ return 0;
+ }
+
+ int Wait(epoll_event* events, int maxevents, int timeout) {
+ int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
+
+ if (ret < 0)
+ return -errno;
+ else
+ return ret;
+ }
+
+ int Get() const { return fd_.get(); }
+
+ private:
+ base::unique_fd fd_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
new file mode 100644
index 0000000000..d0ee69c770
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
@@ -0,0 +1,95 @@
+#ifndef ANDROID_DVR_FIELD_OF_VIEW_H_
+#define ANDROID_DVR_FIELD_OF_VIEW_H_
+
+#include <cmath>
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a generalized, asymmetric field of view with four half angles.
+// Each half angle denotes the angle between the corresponding frustum plane.
+// Together with a near and far plane, a FieldOfView forms the frustum of an
+// off-axis perspective projection.
+class FieldOfView {
+ public:
+ // The default constructor sets an angle of 0 (in any unit) for all four
+ // half-angles.
+ FieldOfView() : left_(0.0f), right_(0.0f), bottom_(0.0f), top_(0.0f) {}
+
+ // Constructs a FieldOfView from four angles.
+ FieldOfView(float left, float right, float bottom, float top)
+ : left_(left), right_(right), bottom_(bottom), top_(top) {}
+
+ explicit FieldOfView(const float* fov)
+ : FieldOfView(fov[0], fov[1], fov[2], fov[3]) {}
+
+ // Accessors for all four half-angles.
+ float GetLeft() const { return left_; }
+ float GetRight() const { return right_; }
+ float GetBottom() const { return bottom_; }
+ float GetTop() const { return top_; }
+
+ // Setters for all four half-angles.
+ void SetLeft(float left) { left_ = left; }
+ void SetRight(float right) { right_ = right; }
+ void SetBottom(float bottom) { bottom_ = bottom; }
+ void SetTop(float top) { top_ = top; }
+
+ Eigen::AffineMatrix<float, 4> GetProjectionMatrix(float z_near,
+ float z_far) const {
+ float x_left = -std::tan(left_) * z_near;
+ float x_right = std::tan(right_) * z_near;
+ float y_bottom = -std::tan(bottom_) * z_near;
+ float y_top = std::tan(top_) * z_near;
+
+ float zero = 0.0f;
+ if (x_left == x_right || y_bottom == y_top || z_near == z_far ||
+ z_near <= zero || z_far <= zero) {
+ return Eigen::AffineMatrix<float, 4>::Identity();
+ }
+
+ float x = (2 * z_near) / (x_right - x_left);
+ float y = (2 * z_near) / (y_top - y_bottom);
+ float a = (x_right + x_left) / (x_right - x_left);
+ float b = (y_top + y_bottom) / (y_top - y_bottom);
+ float c = (z_near + z_far) / (z_near - z_far);
+ float d = (2 * z_near * z_far) / (z_near - z_far);
+
+ // Note: Eigen matrix initialization syntax is always 'column-major'
+ // even if the storage is row-major. Or in other words, just write the
+ // matrix like you'd see in a math textbook.
+ Eigen::AffineMatrix<float, 4> result;
+ result.matrix() << x, 0, a, 0,
+ 0, y, b, 0,
+ 0, 0, c, d,
+ 0, 0, -1, 0;
+ return result;
+ }
+
+ static FieldOfView FromProjectionMatrix(
+ const Eigen::AffineMatrix<float, 4>& m) {
+ // Compute tangents.
+ float tan_vert_fov = 1.0f / m(1, 1);
+ float tan_horz_fov = 1.0f / m(0, 0);
+ float t = (m(1, 2) + 1.0f) * tan_vert_fov;
+ float b = (m(1, 2) - 1.0f) * tan_vert_fov;
+ float l = (m(0, 2) - 1.0f) * tan_horz_fov;
+ float r = (m(0, 2) + 1.0f) * tan_horz_fov;
+
+ return FieldOfView(std::atan(-l), std::atan(r), std::atan(-b),
+ std::atan(t));
+ }
+
+ private:
+ float left_;
+ float right_;
+ float bottom_;
+ float top_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_FIELD_OF_VIEW_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
new file mode 100644
index 0000000000..12ef622aaa
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
@@ -0,0 +1,64 @@
+#ifndef ANDROID_DVR_LOG_HELPERS_H_
+#define ANDROID_DVR_LOG_HELPERS_H_
+
+#include <iomanip>
+#include <ostream>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+
+namespace android {
+namespace dvr {
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+ const Eigen::Vector<T, 2>& vec) {
+ return out << "vec2(" << vec.x() << ',' << vec.y() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+ const Eigen::Vector<T, 3>& vec) {
+ return out << "vec3(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+ const Eigen::Vector<T, 4>& vec) {
+ return out << "vec4(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ','
+ << vec.w() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+ const Eigen::AffineMatrix<T, 4>& mat) {
+ out << std::setfill(' ') << std::setprecision(4) << std::fixed
+ << std::showpos;
+ out << "\nmat4[";
+ out << std::setw(10) << mat(0, 0) << " " << std::setw(10) << mat(0, 1) << " "
+ << std::setw(10) << mat(0, 2) << " " << std::setw(10) << mat(0, 3);
+ out << "]\n [";
+ out << std::setw(10) << mat(1, 0) << " " << std::setw(10) << mat(1, 1) << " "
+ << std::setw(10) << mat(1, 2) << " " << std::setw(10) << mat(1, 3);
+ out << "]\n [";
+ out << std::setw(10) << mat(2, 0) << " " << std::setw(10) << mat(2, 1) << " "
+ << std::setw(10) << mat(2, 2) << " " << std::setw(10) << mat(2, 3);
+ out << "]\n [";
+ out << std::setw(10) << mat(3, 0) << " " << std::setw(10) << mat(3, 1) << " "
+ << std::setw(10) << mat(3, 2) << " " << std::setw(10) << mat(3, 3);
+ out << "]\n";
+
+ return out;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const FieldOfView& fov) {
+ return out << "fov(" << (fov.GetLeft() * 180.0f / M_PI) << ','
+ << (fov.GetRight() * 180.0f / M_PI) << ','
+ << (fov.GetBottom() * 180.0f / M_PI) << ','
+ << (fov.GetTop() * 180.0f / M_PI) << ')';
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LOG_HELPERS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h
new file mode 100644
index 0000000000..aef7146e22
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h
@@ -0,0 +1,26 @@
+#ifndef ANDROID_DVR_MATRIX_HELPERS_H_
+#define ANDROID_DVR_MATRIX_HELPERS_H_
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// A helper function for creating a mat4 directly.
+inline mat4 MakeMat4(float m00, float m01, float m02, float m03, float m10,
+ float m11, float m12, float m13, float m20, float m21,
+ float m22, float m23, float m30, float m31, float m32,
+ float m33) {
+ Eigen::Matrix4f matrix;
+
+ matrix << m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30,
+ m31, m32, m33;
+
+ return mat4(matrix);
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LOG_HELPERS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/numeric.h b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
new file mode 100644
index 0000000000..45458939c7
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
@@ -0,0 +1,175 @@
+#ifndef ANDROID_DVR_NUMERIC_H_
+#define ANDROID_DVR_NUMERIC_H_
+
+#include <cmath>
+#include <limits>
+#include <random>
+#include <type_traits>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FT>
+static inline FT ToDeg(FT f) {
+ return f * static_cast<FT>(180.0 / M_PI);
+}
+
+template <typename FT>
+static inline FT ToRad(FT f) {
+ return f * static_cast<FT>(M_PI / 180.0);
+}
+
+// Adjusts `x` to the periodic range `[lo, hi]` (to normalize angle values
+// for example).
+template <typename T>
+T NormalizePeriodicRange(T x, T lo, T hi) {
+ T range_size = hi - lo;
+
+ while (x < lo) {
+ x += range_size;
+ }
+
+ while (x > hi) {
+ x -= range_size;
+ }
+
+ return x;
+}
+
+// Normalizes a measurement in radians.
+// @param x the angle to be normalized
+// @param centre the point around which to normalize the range
+// @return the value of x, normalized to the range [centre - 180, centre + 180]
+template <typename T>
+T NormalizeDegrees(T x, T centre = static_cast<T>(180.0)) {
+ return NormalizePeriodicRange(x, centre - static_cast<T>(180.0),
+ centre + static_cast<T>(180.0));
+}
+
+// Normalizes a measurement in radians.
+// @param x the angle to be normalized
+// @param centre the point around which to normalize the range
+// @return the value of x, normalized to the range
+// [centre - M_PI, centre + M_PI]
+// @remark the centre parameter is to make it possible to specify a different
+// periodic range. This is useful if you are planning on comparing two
+// angles close to 0 or M_PI, so that one might not accidentally end
+// up on the other side of the range
+template <typename T>
+T NormalizeRadians(T x, T centre = static_cast<T>(M_PI)) {
+ return NormalizePeriodicRange(x, centre - static_cast<T>(M_PI),
+ centre + static_cast<T>(M_PI));
+}
+
+static inline vec2i Round(const vec2& v) {
+ return vec2i(roundf(v.x()), roundf(v.y()));
+}
+
+static inline vec2i Scale(const vec2i& v, float scale) {
+ return vec2i(roundf(static_cast<float>(v.x()) * scale),
+ roundf(static_cast<float>(v.y()) * scale));
+}
+
+// Re-maps `x` from `[lba,uba]` to `[lbb,ubb]`.
+template <typename T>
+T ConvertRange(T x, T lba, T uba, T lbb, T ubb) {
+ return (((x - lba) * (ubb - lbb)) / (uba - lba)) + lbb;
+}
+
+template <typename R1, typename R2>
+static inline vec2 MapPoint(const vec2& pt, const R1& from, const R2& to) {
+ vec2 normalized((pt - vec2(from.p1)).array() / vec2(from.GetSize()).array());
+ return (normalized * vec2(to.GetSize())) + vec2(to.p1);
+}
+
+template <typename T>
+inline bool IsZero(const T& v,
+ const T& tol = std::numeric_limits<T>::epsilon()) {
+ return std::abs(v) <= tol;
+}
+
+template <typename T>
+inline bool IsEqual(const T& a, const T& b,
+ const T& tol = std::numeric_limits<T>::epsilon()) {
+ return std::abs(b - a) <= tol;
+}
+
+template <typename T>
+T Square(const T& x) {
+ return x * x;
+}
+
+template <typename T>
+T RandomInRange(T lo, T hi,
+ typename
+ std::enable_if<std::is_floating_point<T>::value>::type* = 0) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_real_distribution<T> distro(lo, hi);
+ return distro(gen);
+}
+
+template <typename T>
+T RandomInRange(T lo, T hi,
+ typename
+ std::enable_if<std::is_integral<T>::value>::type* = 0) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<T> distro(lo, hi);
+ return distro(gen);
+}
+
+template <typename Derived1, typename Derived2>
+Derived1 RandomInRange(
+ const Eigen::MatrixBase<Derived1>& lo,
+ const Eigen::MatrixBase<Derived2>& hi) {
+ EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived1, Derived2);
+
+ Derived1 result = Eigen::MatrixBase<Derived1>::Zero();
+
+ for (int row = 0; row < result.rows(); ++row) {
+ for (int col = 0; col < result.cols(); ++col) {
+ result(row, col) = RandomInRange(lo(row, col), hi(row, col));
+ }
+ }
+
+ return result;
+}
+
+template <typename T>
+T RandomRange(T x) {
+ return RandomInRange(-x, x);
+}
+
+template <typename T>
+T Clamp(T x, T lo, T hi) {
+ return std::min(std::max(x, lo), hi);
+}
+
+inline mat3 ScaleMatrix(const vec2& scale_xy) {
+ return mat3(Eigen::Scaling(scale_xy[0], scale_xy[1], 1.0f));
+}
+
+inline mat3 TranslationMatrix(const vec2& translation) {
+ return mat3(Eigen::Translation2f(translation));
+}
+
+inline mat4 TranslationMatrix(const vec3& translation) {
+ return mat4(Eigen::Translation3f(translation));
+}
+
+inline vec2 TransformPoint(const mat3& m, const vec2& p) {
+ return m.linear() * p + m.translation();
+}
+
+inline vec2 TransformVector(const mat3& m, const vec2& p) {
+ return m.linear() * p;
+}
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_NUMERIC_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/ortho.h b/libs/vr/libdvrcommon/include/private/dvr/ortho.h
new file mode 100644
index 0000000000..fc0bce3792
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/ortho.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_ORTHO_H_
+#define ANDROID_DVR_ORTHO_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+template <class T>
+Eigen::AffineMatrix<T, 4> OrthoMatrix(T left, T right, T bottom, T top,
+ T znear, T zfar) {
+ Eigen::AffineMatrix<T, 4> result;
+ const T t2 = static_cast<T>(2);
+ const T a = t2 / (right - left);
+ const T b = t2 / (top - bottom);
+ const T c = t2 / (zfar - znear);
+ const T xoff = -(right + left) / (right - left);
+ const T yoff = -(top + bottom) / (top - bottom);
+ const T zoff = -(zfar + znear) / (zfar - znear);
+ const T t1 = static_cast<T>(1);
+ result.matrix() << a, 0, 0, xoff,
+ 0, b, 0, yoff,
+ 0, 0, c, zoff,
+ 0, 0, 0, t1;
+ return result;
+}
+
+} // namespace android
+} // namespace dvr
+
+#endif // ANDROID_DVR_ORTHO_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/pose.h b/libs/vr/libdvrcommon/include/private/dvr/pose.h
new file mode 100644
index 0000000000..97944e8928
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/pose.h
@@ -0,0 +1,118 @@
+#ifndef ANDROID_DVR_POSE_H_
+#define ANDROID_DVR_POSE_H_
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a 3D pose (rotation and position).
+//
+// @tparam T Data type for storing the position coordinate and rotation
+// quaternion.
+template <typename T>
+class Pose {
+ public:
+ // Creates identity pose.
+ Pose()
+ : rotation_(Eigen::Quaternion<T>::Identity()),
+ position_(Eigen::Vector3<T>::Zero()) {}
+
+ // Initializes a pose with given rotation and position.
+ //
+ // rotation Initial rotation.
+ // position Initial position.
+ Pose(Eigen::Quaternion<T> rotation, Eigen::Vector3<T> position)
+ : rotation_(rotation), position_(position) {}
+
+ void Invert() {
+ rotation_ = rotation_.inverse();
+ position_ = rotation_ * -position_;
+ }
+
+ Pose Inverse() const {
+ Pose result(*this);
+ result.Invert();
+ return result;
+ }
+
+ // Compute the composition of this pose with another, storing the result
+ // in the current object
+ void ComposeInPlace(const Pose& other) {
+ position_ = position_ + rotation_ * other.position_;
+ rotation_ = rotation_ * other.rotation_;
+ }
+
+ // Computes the composition of this pose with another, and returns the result
+ Pose Compose(const Pose& other) const {
+ Pose result(*this);
+ result.ComposeInPlace(other);
+ return result;
+ }
+
+ Eigen::Vector3<T> TransformPoint(const Eigen::Vector3<T>& v) const {
+ return rotation_ * v + position_;
+ }
+
+ Eigen::Vector3<T> Transform(const Eigen::Vector3<T>& v) const {
+ return rotation_ * v;
+ }
+
+ Pose& operator*=(const Pose& other) {
+ ComposeInPlace(other);
+ return *this;
+ }
+
+ Pose operator*(const Pose& other) const { return Compose(other); }
+
+ // Gets the rotation of the 3D pose.
+ Eigen::Quaternion<T> GetRotation() const { return rotation_; }
+
+ // Gets the position of the 3D pose.
+ Eigen::Vector3<T> GetPosition() const { return position_; }
+
+ // Sets the rotation of the 3D pose.
+ void SetRotation(Eigen::Quaternion<T> rotation) { rotation_ = rotation; }
+
+ // Sets the position of the 3D pose.
+ void SetPosition(Eigen::Vector3<T> position) { position_ = position; }
+
+ // Gets a 4x4 matrix representing a transform from the reference space (that
+ // the rotation and position of the pose are relative to) to the object space.
+ Eigen::AffineMatrix<T, 4> GetObjectFromReferenceMatrix() const;
+
+ // Gets a 4x4 matrix representing a transform from the object space to the
+ // reference space (that the rotation and position of the pose are relative
+ // to).
+ Eigen::AffineMatrix<T, 4> GetReferenceFromObjectMatrix() const;
+
+ private:
+ Eigen::Quaternion<T> rotation_;
+ Eigen::Vector3<T> position_;
+};
+
+template <typename T>
+Eigen::AffineMatrix<T, 4> Pose<T>::GetObjectFromReferenceMatrix() const {
+ // The transfrom from the reference is the inverse of the pose.
+ Eigen::AffineMatrix<T, 4> matrix(rotation_.inverse().toRotationMatrix());
+ return matrix.translate(-position_);
+}
+
+template <typename T>
+Eigen::AffineMatrix<T, 4> Pose<T>::GetReferenceFromObjectMatrix() const {
+ // The transfrom to the reference.
+ Eigen::AffineMatrix<T, 4> matrix(rotation_.toRotationMatrix());
+ return matrix.pretranslate(position_);
+}
+
+//------------------------------------------------------------------------------
+// Type-specific typedefs.
+//------------------------------------------------------------------------------
+
+using Posef = Pose<float>;
+using Posed = Pose<double>;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_POSE_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/range.h b/libs/vr/libdvrcommon/include/private/dvr/range.h
new file mode 100644
index 0000000000..1d06c96ec5
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/range.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_RANGE_H_
+#define ANDROID_DVR_RANGE_H_
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// TODO(skiazyk): Replace all instances of this with Eigen::AlignedBox
+
+// Container of two points that define a 2D range.
+template <class T, int d>
+struct Range {
+ // Construct an uninitialized Range.
+ Range() {}
+ Range(Eigen::Vector<T, d> p1, Eigen::Vector<T, d> p2) : p1(p1), p2(p2) {}
+
+ static Range<T, d> FromSize(Eigen::Vector<T, d> p1,
+ Eigen::Vector<T, d> size) {
+ return Range<T, d>(p1, p1 + size);
+ }
+
+ bool operator==(const Range<T, d>& rhs) const {
+ return p1 == rhs.p1 && p2 == rhs.p2;
+ }
+
+ Eigen::Vector<T, d> GetMinPoint() const { return p1; }
+
+ Eigen::Vector<T, d> GetMaxPoint() const { return p2; }
+
+ Eigen::Vector<T, d> GetSize() const { return p2 - p1; }
+
+ Eigen::Vector<T, d> p1;
+ Eigen::Vector<T, d> p2;
+};
+
+typedef Range<int, 2> Range2i;
+typedef Range<float, 2> Range2f;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_RANGE_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h
new file mode 100644
index 0000000000..44485a734c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h
@@ -0,0 +1,99 @@
+#ifndef ANDROID_DVR_RING_BUFFER_H_
+#define ANDROID_DVR_RING_BUFFER_H_
+
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// A simple ring buffer implementation.
+//
+// A vector works but you either have to keep track of start_ and size_ yourself
+// or erase() from the front which is inefficient.
+//
+// A deque works but the common usage pattern of Append() PopFront() Append()
+// PopFront() looks like it allocates each time size goes from 0 --> 1, which we
+// don't want. This class allocates only once.
+template <typename T>
+class RingBuffer {
+ public:
+ RingBuffer() { Reset(0); }
+
+ explicit RingBuffer(size_t capacity) { Reset(capacity); }
+
+ RingBuffer(const RingBuffer& other) = default;
+ RingBuffer(RingBuffer&& other) = default;
+ RingBuffer& operator=(const RingBuffer& other) = default;
+ RingBuffer& operator=(RingBuffer&& other) = default;
+
+ void Append(const T& val) {
+ if (IsFull())
+ PopFront();
+ Get(size_) = val;
+ size_++;
+ }
+
+ void Append(T&& val) {
+ if (IsFull())
+ PopFront();
+ Get(size_) = std::move(val);
+ size_++;
+ }
+
+ bool IsEmpty() const { return size_ == 0; }
+
+ bool IsFull() const { return size_ == buffer_.size(); }
+
+ size_t GetSize() const { return size_; }
+
+ size_t GetCapacity() const { return buffer_.size(); }
+
+ T& Get(size_t i) { return buffer_[(start_ + i) % buffer_.size()]; }
+
+ const T& Get(size_t i) const {
+ return buffer_[(start_ + i) % buffer_.size()];
+ }
+
+ const T& Back() const { return Get(size_ - 1); }
+
+ T& Back() { return Get(size_ - 1); }
+
+ const T& Front() const { return Get(0); }
+
+ T& Front() { return Get(0); }
+
+ void PopBack() {
+ if (size_ != 0) {
+ Get(size_ - 1) = T();
+ size_--;
+ }
+ }
+
+ void PopFront() {
+ if (size_ != 0) {
+ Get(0) = T();
+ start_ = (start_ + 1) % buffer_.size();
+ size_--;
+ }
+ }
+
+ void Clear() { Reset(GetCapacity()); }
+
+ void Reset(size_t capacity) {
+ start_ = size_ = 0;
+ buffer_.clear();
+ buffer_.resize(capacity);
+ }
+
+ private:
+ // Ideally we'd allocate our own memory and use placement new to instantiate
+ // instances of T instead of using a vector, but the vector is simpler.
+ std::vector<T> buffer_;
+ size_t start_, size_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_RING_BUFFER_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h
new file mode 100644
index 0000000000..6048652f92
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h
@@ -0,0 +1,124 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
+
+#include <gtest/gtest.h>
+
+#include <cmath>
+
+#include <private/dvr/numeric.h>
+
+namespace android {
+namespace dvr {
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpArrayLikeFloatEq(
+ const char* expectedStr, const char* actualStr, const char* toleranceStr,
+ const A& expected, const B& actual, const T& tolerance) {
+ for (int i = 0; i < N; ++i) {
+ if (!IsEqual(expected[i], actual[i], tolerance)) {
+ return ::testing::AssertionFailure()
+ << "\"" << expectedStr << "\" and \"" << actualStr
+ << "\" differ at element " << i << " by at least " << tolerance
+ << " : "
+ << " Expected \"" << expected[i] << "\", was \"" << actual[i]
+ << "\".";
+ }
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpMatrixLikeFloatEq(
+ const char* expectedStr, const char* actualStr, const char* toleranceStr,
+ const A& expected, const B& actual, const T& tolerance) {
+ for (int r = 0; r < N; ++r) {
+ for (int c = 0; c < N; ++c) {
+ if (!IsEqual(expected(r, c), actual(r, c), tolerance)) {
+ return ::testing::AssertionFailure()
+ << "\"" << expectedStr << "\" and \"" << actualStr
+ << "\" differ at (" << r << "," << c << ")"
+ << " by at least " << tolerance << " : "
+ << " Expected \"" << expected(r, c) << "\", was \""
+ << actual(r, c) << "\".";
+ }
+ }
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpArrayLikeFloatNe(
+ const char* expectedStr, const char* actualStr, const char* toleranceStr,
+ const A& expected, const B& actual, const T& tolerance) {
+ for (int i = 0; i < N; ++i) {
+ if (!IsEqual(expected[i], actual[i], tolerance)) {
+ return ::testing::AssertionSuccess();
+ }
+ }
+
+ ::testing::Message message;
+ message << "Expected \"" << expectedStr
+ << "\" to differ from provided value \"" << actualStr
+ << "\" by at least " << tolerance << ".";
+
+ return ::testing::AssertionFailure(message);
+}
+
+template <int N, typename A, typename B, typename T>
+::testing::AssertionResult CmpMatrixLikeFloatNe(
+ const char* expectedStr, const char* actualStr, const char* toleranceStr,
+ const A& expected, const B& actual, const T& tolerance) {
+ for (int r = 0; r < N; ++r) {
+ for (int c = 0; c < N; ++c) {
+ if (!IsEqual(expected(r, c), actual(r, c), tolerance)) {
+ return ::testing::AssertionSuccess();
+ }
+ }
+ }
+
+ ::testing::Message message;
+ message << "Expected \"" << expectedStr
+ << "\" to differ from provided value \"" << actualStr
+ << "\" by at least " << tolerance << ".";
+
+ return ::testing::AssertionFailure(message);
+}
+
+} // namespace dvr
+} // namespace android
+
+#define EXPECT_VEC3_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected, actual, \
+ tol)
+
+#define EXPECT_VEC3_NOT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected, actual, \
+ tol)
+
+#define EXPECT_QUAT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected.coeffs(), \
+ actual.coeffs(), tol)
+
+#define EXPECT_QUAT_NOT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected.coeffs(), \
+ actual.coeffs(), tol)
+
+#define EXPECT_MAT4_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatEq<4>, expected, actual, \
+ tol)
+
+#define EXPECT_MAT4_NOT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<4>, expected, actual, \
+ tol)
+
+#define EXPECT_MAT3_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr \
+ : CmpMatrixLikeFloatEq<3>, expected, actual, tol)
+
+#define EXPECT_MAT3_NOT_NEAR(expected, actual, tol) \
+ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<3>, expected, actual, \
+ tol)
+
+#endif // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/types.h b/libs/vr/libdvrcommon/include/private/dvr/types.h
new file mode 100644
index 0000000000..1fa54afe92
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/types.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_TYPES_H_
+#define ANDROID_DVR_TYPES_H_
+
+// All basic types used by VR code.
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+#include <private/dvr/pose.h>
+#include <private/dvr/range.h>
+
+namespace android {
+namespace dvr {
+
+enum RgbColorChannel { kRed, kGreen, kBlue };
+
+// EyeType: 0 for left, 1 for right.
+enum EyeType { kLeftEye = 0, kRightEye = 1 };
+
+// In the context of VR, vector types are used as much as base types.
+
+using vec2f = Eigen::Vector2f;
+using vec2d = Eigen::Vector2d;
+using vec2i = Eigen::Vector2i;
+using vec2 = vec2f;
+
+using vec3f = Eigen::Vector3f;
+using vec3d = Eigen::Vector3d;
+using vec3i = Eigen::Vector3i;
+using vec3 = vec3f;
+
+using vec4f = Eigen::Vector4f;
+using vec4d = Eigen::Vector4d;
+using vec4i = Eigen::Vector4i;
+using vec4 = vec4f;
+
+using mat3f = Eigen::AffineMatrix<float, 3>;
+using mat3d = Eigen::AffineMatrix<double, 3>;
+using mat3 = mat3f;
+
+using mat4f = Eigen::AffineMatrix<float, 4>;
+using mat4d = Eigen::AffineMatrix<double, 4>;
+using mat4 = mat4f;
+
+using quatf = Eigen::Quaternionf;
+using quatd = Eigen::Quaterniond;
+using quat = quatf;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_TYPES_H_
diff --git a/libs/vr/libdvrcommon/tests/numeric_test.cpp b/libs/vr/libdvrcommon/tests/numeric_test.cpp
new file mode 100644
index 0000000000..1ee1447703
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/numeric_test.cpp
@@ -0,0 +1,67 @@
+#include <gtest/gtest.h>
+
+#include <private/dvr/numeric.h>
+
+using TestTypes = ::testing::Types<float, double, int>;
+
+using android::dvr::RandomInRange;
+
+template <typename T>
+class NumericTest : public ::testing::TestWithParam<T> {
+ public:
+ using FT = T;
+};
+
+TYPED_TEST_CASE(NumericTest, TestTypes);
+
+TYPED_TEST(NumericTest, RandomInRange) {
+ using FT = typename TestFixture::FT;
+
+ const int kNumTrials = 50;
+ const FT kLowRange = static_cast<FT>(-100);
+ const FT kHighRange = static_cast<FT>(100);
+
+ for (int i = 0; i < kNumTrials; ++i) {
+ FT value = RandomInRange(kLowRange, kHighRange);
+
+ EXPECT_LE(kLowRange, value);
+ EXPECT_GE(kHighRange, value);
+ }
+}
+
+TEST(RandomInRange, TestIntVersion) {
+ // This checks specifically that the function does not always give the lo
+ // value (this was previously a bug)
+
+ const int kNumTrials = 50;
+ const int kLowRange = -100;
+ const int kHighRange = 100;
+
+ for (int i = 0; i < kNumTrials; ++i) {
+ int value = RandomInRange(kLowRange, kHighRange);
+
+ if (value != kLowRange) {
+ SUCCEED();
+ return;
+ }
+ }
+
+ FAIL() << "Did not produce a value other than the range minimum for "
+ << "integers.";
+}
+
+TEST(RandomInRange, TestVectorVersion) {
+ Eigen::Vector3d lo(-3.0, -4.0, -5.0);
+ Eigen::Vector3d hi(5.0, 4.0, 3.0);
+
+ const int kNumTrials = 50;
+
+ for (int i = 0; i < kNumTrials; ++i) {
+ Eigen::Vector3d result = RandomInRange(lo, hi);
+
+ for (int j = 0; j < 3; ++j) {
+ EXPECT_LE(lo[j], result[j]);
+ EXPECT_GE(hi[j], result[j]);
+ }
+ }
+}
diff --git a/libs/vr/libdvrcommon/tests/pose_test.cpp b/libs/vr/libdvrcommon/tests/pose_test.cpp
new file mode 100644
index 0000000000..aa1896da15
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/pose_test.cpp
@@ -0,0 +1,154 @@
+#include <gtest/gtest.h>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/pose.h>
+#include <private/dvr/test/test_macros.h>
+
+using PoseTypes = ::testing::Types<float, double>;
+
+template <class T>
+class PoseTest : public ::testing::TestWithParam<T> {
+ public:
+ using FT = T;
+ using Pose_t = android::dvr::Pose<FT>;
+ using quat_t = Eigen::Quaternion<FT>;
+ using vec3_t = Eigen::Vector3<FT>;
+ using mat4_t = Eigen::AffineMatrix<FT, 4>;
+};
+
+TYPED_TEST_CASE(PoseTest, PoseTypes);
+
+// Check that the two matrix methods are inverses of each other
+TYPED_TEST(PoseTest, SelfInverse) {
+ using quat_t = typename TestFixture::quat_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using mat4_t = typename TestFixture::mat4_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t initial_rotation(Eigen::AngleAxis<FT>(
+ FT(M_PI / 3.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized()));
+ const vec3_t initial_position = vec3_t(FT(2.0), FT(10.0), FT(-4.0));
+ const Pose_t initial_pose(initial_rotation, initial_position);
+
+ auto result_pose = initial_pose.GetReferenceFromObjectMatrix() *
+ initial_pose.GetObjectFromReferenceMatrix();
+
+ EXPECT_MAT4_NEAR(result_pose, mat4_t::Identity(), tolerance);
+}
+
+TYPED_TEST(PoseTest, TransformPoint) {
+ using quat_t = typename TestFixture::quat_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t pose_rotation(
+ Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0))));
+ const auto pose_position = vec3_t(FT(1.0), FT(1.0), FT(2.0));
+
+ const Pose_t test_pose(pose_rotation, pose_position);
+
+ for (int axis = 0; axis < 3; ++axis) {
+ vec3_t start_position = vec3_t::Zero();
+ start_position[axis] = FT(1.0);
+ const vec3_t expected_transformed =
+ (pose_rotation * start_position) + pose_position;
+ const vec3_t actual_transformed = test_pose.TransformPoint(start_position);
+ EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance);
+ }
+}
+
+TYPED_TEST(PoseTest, TransformVector) {
+ using quat_t = typename TestFixture::quat_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t pose_rotation(Eigen::AngleAxis<FT>(
+ FT(M_PI / 6.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized()));
+
+ const auto pose_position = vec3_t(FT(500.0), FT(-500.0), FT(300.0));
+
+ const Pose_t test_pose(pose_rotation, pose_position);
+
+ for (int axis = 0; axis < 3; ++axis) {
+ vec3_t start_position = vec3_t::Zero();
+ start_position[axis] = FT(1.0);
+ const vec3_t expected_rotated = pose_rotation * start_position;
+ const vec3_t actual_rotated = test_pose.Transform(start_position);
+ EXPECT_VEC3_NEAR(expected_rotated, actual_rotated, tolerance);
+ }
+}
+
+TYPED_TEST(PoseTest, Composition) {
+ using quat_t = typename TestFixture::quat_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t first_rotation(
+ Eigen::AngleAxis<FT>(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0))));
+ const auto first_offset = vec3_t(FT(-3.0), FT(2.0), FT(-1.0));
+ const quat_t second_rotation(Eigen::AngleAxis<FT>(
+ FT(M_PI / 3.0), vec3_t(FT(1.0), FT(-1.0), FT(0.0)).normalized()));
+ const auto second_offset = vec3_t(FT(6.0), FT(-7.0), FT(-8.0));
+
+ const Pose_t first_pose(first_rotation, first_offset);
+ const Pose_t second_pose(second_rotation, second_offset);
+
+ const auto combined_pose(second_pose.Compose(first_pose));
+
+ for (int axis = 0; axis < 3; ++axis) {
+ vec3_t start_position = vec3_t::Zero();
+ start_position[axis] = FT(1.0);
+ const vec3_t expected_transformed =
+ second_pose.TransformPoint(first_pose.TransformPoint(start_position));
+ const vec3_t actual_transformed =
+ combined_pose.TransformPoint(start_position);
+ EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance);
+ }
+}
+
+TYPED_TEST(PoseTest, Inverse) {
+ using quat_t = typename TestFixture::quat_t;
+ using vec3_t = typename TestFixture::vec3_t;
+ using Pose_t = typename TestFixture::Pose_t;
+ using FT = typename TestFixture::FT;
+
+ const auto tolerance = FT(0.0001);
+
+ const quat_t pose_rotation(Eigen::AngleAxis<FT>(
+ FT(M_PI / 2.0), vec3_t(FT(4.0), FT(-2.0), FT(-1.0)).normalized()));
+ const auto pose_position = vec3_t(FT(-1.0), FT(2.0), FT(-4.0));
+
+ Pose_t pose(pose_rotation, pose_position);
+ const Pose_t pose_inverse = pose.Inverse();
+
+ for (int axis = 0; axis < 3; ++axis) {
+ vec3_t start_position = vec3_t::Zero();
+ start_position[axis] = FT(1.0);
+ const vec3_t transformed = pose.Transform(start_position);
+ const vec3_t inverted = pose_inverse.Transform(transformed);
+ EXPECT_VEC3_NEAR(start_position, inverted, tolerance);
+ }
+
+ Pose_t nullified_pose[2] = {
+ pose.Compose(pose_inverse), pose_inverse.Compose(pose),
+ };
+
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_QUAT_NEAR(quat_t::Identity(), nullified_pose[i].GetRotation(),
+ tolerance);
+ EXPECT_VEC3_NEAR(vec3_t::Zero(), nullified_pose[i].GetPosition(),
+ tolerance);
+ }
+}
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
new file mode 100644
index 0000000000..f55e9942db
--- /dev/null
+++ b/libs/vr/libpdx/Android.bp
@@ -0,0 +1,60 @@
+cc_library_static {
+ name: "libpdx",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ export_include_dirs: ["private"],
+ local_include_dirs: ["private"],
+ srcs: [
+ "client.cpp",
+ "service.cpp",
+ "status.cpp",
+ ],
+}
+
+cc_test {
+ name: "pdx_tests",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "client_tests.cpp",
+ "mock_tests.cpp",
+ "serialization_tests.cpp",
+ "service_tests.cpp",
+ "status_tests.cpp",
+ "thread_local_buffer_tests.cpp",
+ "variant_tests.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libpdx",
+ "liblog",
+ "libutils",
+ "libvndksupport",
+ ],
+}
+
+// Code analysis target.
+cc_test {
+ name: "pdx_encoder_performance_test",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-O2",
+ ],
+ srcs: [
+ "encoder_performance_test.cpp",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+}
diff --git a/libs/vr/libpdx/client.cpp b/libs/vr/libpdx/client.cpp
new file mode 100644
index 0000000000..bfa2d879b5
--- /dev/null
+++ b/libs/vr/libpdx/client.cpp
@@ -0,0 +1,287 @@
+#include "pdx/client.h"
+
+#define LOG_TAG "ServiceFramework"
+#include <log/log.h>
+
+#include <pdx/trace.h>
+
+namespace android {
+namespace pdx {
+
+void Client::EnableAutoReconnect(int64_t reconnect_timeout_ms) {
+ if (channel_factory_) {
+ reconnect_timeout_ms_ = reconnect_timeout_ms;
+ auto_reconnect_enabled_ = true;
+ }
+}
+
+void Client::DisableAutoReconnect() { auto_reconnect_enabled_ = false; }
+
+bool Client::IsConnected() const { return channel_.get() != nullptr; }
+
+Status<void> Client::CheckReconnect() {
+ Status<void> ret;
+ bool was_disconnected = !IsConnected();
+ if (auto_reconnect_enabled_ && was_disconnected && channel_factory_) {
+ auto status = channel_factory_->Connect(reconnect_timeout_ms_);
+ if (!status) {
+ error_ = -status.error();
+ ret.SetError(status.error());
+ return ret;
+ }
+ channel_ = status.take();
+ }
+
+ if (!IsConnected()) {
+ ret.SetError(ESHUTDOWN);
+ } else {
+ // Call the subclass OnConnect handler. The subclass may choose to close the
+ // connection in the handler, in which case error_ will be non-zero.
+ if (was_disconnected)
+ OnConnect();
+ if (!IsConnected())
+ ret.SetError(-error_);
+ else
+ ret.SetValue();
+ }
+
+ return ret;
+}
+
+bool Client::NeedToDisconnectChannel(int error) const {
+ return error == ESHUTDOWN && auto_reconnect_enabled_;
+}
+
+void Client::CheckDisconnect(int error) {
+ if (NeedToDisconnectChannel(error))
+ Close(error);
+}
+
+Client::Client(std::unique_ptr<ClientChannel> channel)
+ : channel_{std::move(channel)} {}
+
+Client::Client(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms)
+ : channel_factory_{std::move(channel_factory)} {
+ auto status = channel_factory_->Connect(timeout_ms);
+ if (!status) {
+ ALOGE("Client::Client: Failed to connect to service because: %s",
+ status.GetErrorMessage().c_str());
+ error_ = -status.error();
+ } else {
+ channel_ = status.take();
+ }
+}
+
+bool Client::IsInitialized() const {
+ return IsConnected() || (channel_factory_ && auto_reconnect_enabled_);
+}
+
+void Client::OnConnect() {}
+
+int Client::error() const { return error_; }
+
+Status<void> Client::SendImpulse(int opcode) {
+ PDX_TRACE_NAME("Client::SendImpulse");
+
+ auto status = CheckReconnect();
+ if (!status)
+ return status;
+
+ status = channel_->SendImpulse(opcode, nullptr, 0);
+ CheckDisconnect(status);
+ return status;
+}
+
+Status<void> Client::SendImpulse(int opcode, const void* buffer,
+ size_t length) {
+ PDX_TRACE_NAME("Client::SendImpulse");
+
+ auto status = CheckReconnect();
+ if (!status)
+ return status;
+
+ status = channel_->SendImpulse(opcode, buffer, length);
+ CheckDisconnect(status);
+ return status;
+}
+
+void Client::Close(int error) {
+ channel_.reset();
+ // Normalize error codes to negative integer space.
+ error_ = error <= 0 ? error : -error;
+}
+
+int Client::event_fd() const {
+ return IsConnected() ? channel_->event_fd() : -1;
+}
+
+LocalChannelHandle& Client::GetChannelHandle() {
+ return channel_->GetChannelHandle();
+}
+
+///////////////////////////// Transaction implementation //////////////////////
+
+Transaction::Transaction(Client& client) : client_{client} {}
+
+Transaction::~Transaction() {
+ if (state_allocated_ && client_.GetChannel())
+ client_.GetChannel()->FreeTransactionState(state_);
+}
+
+bool Transaction::EnsureStateAllocated() {
+ if (!state_allocated_ && client_.GetChannel()) {
+ state_ = client_.GetChannel()->AllocateTransactionState();
+ state_allocated_ = true;
+ }
+ return state_allocated_;
+}
+
+void Transaction::SendTransaction(int opcode, Status<void>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ *ret = client_.CheckReconnect();
+ if (!*ret)
+ return;
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ auto status = client_.GetChannel()->SendWithInt(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ if (status) {
+ ret->SetValue();
+ } else {
+ ret->SetError(status.error());
+ }
+ CheckDisconnect(status);
+}
+
+void Transaction::SendTransaction(int opcode, Status<int>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithInt(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+void Transaction::SendTransaction(int opcode, Status<LocalHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithFileHandle(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+void Transaction::SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ auto status = client_.CheckReconnect();
+ if (!status) {
+ ret->SetError(status.error());
+ return;
+ }
+
+ if (!EnsureStateAllocated()) {
+ ret->SetError(ESHUTDOWN);
+ return;
+ }
+
+ *ret = client_.GetChannel()->SendWithChannelHandle(
+ state_, opcode, send_vector, send_count, receive_vector, receive_count);
+
+ CheckDisconnect(*ret);
+}
+
+Status<FileReference> Transaction::PushFileHandle(const LocalHandle& handle) {
+ if (client_.CheckReconnect() && EnsureStateAllocated())
+ return client_.GetChannel()->PushFileHandle(state_, handle);
+ return ErrorStatus{ESHUTDOWN};
+}
+
+Status<FileReference> Transaction::PushFileHandle(
+ const BorrowedHandle& handle) {
+ if (client_.CheckReconnect() && EnsureStateAllocated())
+ return client_.GetChannel()->PushFileHandle(state_, handle);
+ return ErrorStatus{ESHUTDOWN};
+}
+
+Status<FileReference> Transaction::PushFileHandle(const RemoteHandle& handle) {
+ return handle.Get();
+}
+
+Status<ChannelReference> Transaction::PushChannelHandle(
+ const LocalChannelHandle& handle) {
+ if (client_.CheckReconnect() && EnsureStateAllocated())
+ return client_.GetChannel()->PushChannelHandle(state_, handle);
+ return ErrorStatus{ESHUTDOWN};
+}
+
+Status<ChannelReference> Transaction::PushChannelHandle(
+ const BorrowedChannelHandle& handle) {
+ if (client_.CheckReconnect() && EnsureStateAllocated())
+ return client_.GetChannel()->PushChannelHandle(state_, handle);
+ return ErrorStatus{ESHUTDOWN};
+}
+
+Status<ChannelReference> Transaction::PushChannelHandle(
+ const RemoteChannelHandle& handle) {
+ return handle.value();
+}
+
+bool Transaction::GetFileHandle(FileReference ref, LocalHandle* handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated() &&
+ client_.GetChannel()->GetFileHandle(state_, ref, handle);
+}
+
+bool Transaction::GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) {
+ return client_.CheckReconnect() && EnsureStateAllocated() &&
+ client_.GetChannel()->GetChannelHandle(state_, ref, handle);
+}
+
+void Transaction::CheckDisconnect(int error) {
+ if (client_.NeedToDisconnectChannel(error)) {
+ if (state_allocated_) {
+ if (client_.GetChannel())
+ client_.GetChannel()->FreeTransactionState(state_);
+ state_ = nullptr;
+ state_allocated_ = false;
+ }
+ client_.Close(error);
+ }
+}
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/client_tests.cpp b/libs/vr/libpdx/client_tests.cpp
new file mode 100644
index 0000000000..99ccc698c4
--- /dev/null
+++ b/libs/vr/libpdx/client_tests.cpp
@@ -0,0 +1,567 @@
+#include <pdx/client.h>
+
+#include <gmock/gmock.h>
+#include <sys/eventfd.h>
+
+#include <pdx/mock_client_channel.h>
+#include <pdx/mock_client_channel_factory.h>
+#include <pdx/rpc/remote_method.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::ClientBase;
+using android::pdx::ClientChannel;
+using android::pdx::ClientChannelFactory;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::MockClientChannel;
+using android::pdx::MockClientChannelFactory;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::Void;
+
+using testing::A;
+using testing::AnyNumber;
+using testing::ByMove;
+using testing::Invoke;
+using testing::Ne;
+using testing::Return;
+using testing::_;
+
+namespace {
+
+inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); }
+inline const void* IntToConstPtr(intptr_t addr) {
+ return reinterpret_cast<const void*>(addr);
+}
+
+struct TestInterface final {
+ // Op codes.
+ enum {
+ kOpAdd = 0,
+ kOpSendFile,
+ kOpGetFile,
+ kOpPushChannel,
+ };
+
+ // Methods.
+ PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int));
+ PDX_REMOTE_METHOD(SendFile, kOpSendFile, void(const LocalHandle& fd));
+ PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int));
+ PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void));
+
+ PDX_REMOTE_API(API, Add, SendFile, GetFile, PushChannel);
+};
+
+class SimpleClient : public ClientBase<SimpleClient> {
+ public:
+ explicit SimpleClient(std::unique_ptr<ClientChannel> channel)
+ : BASE{std::move(channel)} {}
+ SimpleClient(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms)
+ : BASE{std::move(channel_factory), timeout_ms} {
+ EnableAutoReconnect(timeout_ms);
+ }
+
+ using BASE::SendImpulse;
+ using BASE::InvokeRemoteMethod;
+ using BASE::InvokeRemoteMethodInPlace;
+ using BASE::Close;
+ using BASE::IsConnected;
+ using BASE::EnableAutoReconnect;
+ using BASE::DisableAutoReconnect;
+ using BASE::event_fd;
+ using BASE::GetChannel;
+
+ MOCK_METHOD0(OnConnect, void());
+};
+
+class FailingClient : public ClientBase<FailingClient> {
+ public:
+ explicit FailingClient(std::unique_ptr<ClientChannel> channel, int error_code)
+ : BASE{std::move(channel)} {
+ Close(error_code);
+ }
+};
+
+class ClientChannelTest : public testing::Test {
+ public:
+ ClientChannelTest()
+ : client_{SimpleClient::Create(
+ std::make_unique<testing::StrictMock<MockClientChannel>>())} {}
+
+ MockClientChannel* mock_channel() {
+ return static_cast<MockClientChannel*>(client_->GetChannel());
+ }
+
+ std::unique_ptr<SimpleClient> client_;
+};
+
+class ClientChannelFactoryTest : public testing::Test {
+ public:
+ ClientChannelFactoryTest() {
+ auto factory =
+ std::make_unique<testing::NiceMock<MockClientChannelFactory>>();
+ ON_CALL(*factory, Connect(kTimeout))
+ .WillByDefault(Invoke(this, &ClientChannelFactoryTest::OnConnect));
+ client_ = SimpleClient::Create(std::move(factory), kTimeout);
+ }
+
+ MockClientChannel* mock_channel() {
+ return static_cast<MockClientChannel*>(client_->GetChannel());
+ }
+
+ Status<std::unique_ptr<ClientChannel>> OnConnect(int64_t /*timeout_ms*/) {
+ if (on_connect_error_)
+ return ErrorStatus(on_connect_error_);
+ std::unique_ptr<MockClientChannel> channel =
+ std::make_unique<testing::StrictMock<MockClientChannel>>();
+ if (on_connect_callback_)
+ on_connect_callback_(channel.get());
+ return Status<std::unique_ptr<ClientChannel>>{std::move(channel)};
+ }
+
+ void OnConnectCallback(std::function<void(MockClientChannel*)> callback) {
+ on_connect_callback_ = callback;
+ }
+ void SetOnConnectError(int error) { on_connect_error_ = error; }
+ void ResetOnConnectError() { on_connect_error_ = 0; }
+
+ constexpr static int64_t kTimeout = 123;
+ std::unique_ptr<SimpleClient> client_;
+ std::function<void(MockClientChannel*)> on_connect_callback_;
+ int on_connect_error_{0};
+};
+
+constexpr int64_t ClientChannelFactoryTest::kTimeout;
+
+class ClientTransactionTest : public ClientChannelTest {
+ public:
+ ClientTransactionTest() : transaction_{*client_} {}
+
+ Transaction transaction_;
+};
+
+} // anonymous namespace
+
+TEST_F(ClientChannelTest, IsInitialized) {
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelTest, CloseOnConstruction) {
+ FailingClient failed_client1{std::make_unique<MockClientChannel>(), EACCES};
+ ASSERT_FALSE(failed_client1.IsInitialized());
+ EXPECT_EQ(-EACCES, failed_client1.error());
+
+ FailingClient failed_client2{std::make_unique<MockClientChannel>(), -EACCES};
+ ASSERT_FALSE(failed_client2.IsInitialized());
+ EXPECT_EQ(-EACCES, failed_client2.error());
+
+ auto failed_client3 =
+ FailingClient::Create(std::make_unique<MockClientChannel>(), EIO);
+ ASSERT_EQ(failed_client3.get(), nullptr);
+}
+
+TEST_F(ClientChannelTest, IsConnected) {
+ EXPECT_TRUE(client_->IsConnected());
+ EXPECT_EQ(0, client_->error());
+ client_->Close(-EINVAL);
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-EINVAL, client_->error());
+}
+
+TEST_F(ClientChannelTest, event_fd) {
+ EXPECT_CALL(*mock_channel(), event_fd()).WillOnce(Return(12));
+ EXPECT_EQ(12, client_->event_fd());
+}
+
+TEST_F(ClientChannelTest, SendImpulse) {
+ EXPECT_CALL(*mock_channel(), SendImpulse(123, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(client_->SendImpulse(123));
+
+ EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+
+ const void* const kTestPtr = IntToConstPtr(1234);
+ EXPECT_CALL(*mock_channel(), SendImpulse(1, kTestPtr, 17))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(client_->SendImpulse(1, kTestPtr, 17));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodNullTransactionState) {
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(9));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+ EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::Add>(4, 5));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodAddSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(3));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(3, status.get());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodAddFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<int> status = client_->InvokeRemoteMethod<TestInterface::Add>(1, 2);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ int fd = eventfd(0, 0);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile,
+ _, _, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalHandle{fd})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::GetFile>();
+ ASSERT_TRUE(status);
+ EXPECT_EQ(fd, status.get().Get());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile,
+ _, _, nullptr, 0))
+ .WillOnce(Return(ByMove(ErrorStatus{EACCES})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::GetFile>("file", 0);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EACCES, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ const int32_t kHandleValue = 17;
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _,
+ _, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, kHandleValue})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalChannelHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::PushChannel>();
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kHandleValue, status.get().value());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(
+ *mock_channel(),
+ SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _,
+ _, nullptr, 0))
+ .WillOnce(Return(ByMove(ErrorStatus{EACCES})));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ Status<LocalChannelHandle> status =
+ client_->InvokeRemoteMethod<TestInterface::PushChannel>();
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EACCES, status.error());
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileSuccess) {
+ void* const kTransactionState = IntToPtr(123);
+ LocalHandle fd{eventfd(0, 0)};
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _,
+ nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_TRUE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd));
+}
+
+TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileFailure) {
+ void* const kTransactionState = IntToPtr(123);
+ LocalHandle fd{eventfd(0, 0)};
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _,
+ nullptr, 0))
+ .WillOnce(Return(ErrorStatus{EACCES}));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_FALSE(client_->InvokeRemoteMethod<TestInterface::SendFile>(fd));
+}
+
+TEST_F(ClientChannelFactoryTest, IsInitialized) {
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, NotConnectedButInitialized) {
+ auto factory =
+ std::make_unique<testing::NiceMock<MockClientChannelFactory>>();
+ EXPECT_CALL(*factory, Connect(kTimeout))
+ .WillOnce(Return(ByMove(ErrorStatus(ESHUTDOWN))))
+ .WillOnce(Invoke(this, &ClientChannelFactoryTest::OnConnect));
+ client_ = SimpleClient::Create(std::move(factory), kTimeout);
+ ASSERT_NE(client_.get(), nullptr);
+ EXPECT_TRUE(client_->IsInitialized());
+ EXPECT_FALSE(client_->IsConnected());
+ client_->DisableAutoReconnect();
+ ASSERT_FALSE(client_->SendImpulse(17));
+ EXPECT_FALSE(client_->IsConnected());
+ client_->EnableAutoReconnect(kTimeout);
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, CheckDisconnect) {
+ EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(ErrorStatus{ESHUTDOWN}));
+ ASSERT_FALSE(client_->SendImpulse(17));
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-ESHUTDOWN, client_->error());
+}
+
+TEST_F(ClientChannelFactoryTest, CheckReconnect) {
+ client_->Close(ESHUTDOWN);
+ ASSERT_FALSE(client_->IsConnected());
+
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientChannelFactoryTest, CloseOnConnect) {
+ client_->Close(ESHUTDOWN);
+
+ EXPECT_CALL(*client_, OnConnect()).WillOnce(Invoke([this] {
+ client_->Close(EIO);
+ }));
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+ EXPECT_FALSE(client_->IsConnected());
+ EXPECT_EQ(-EIO, client_->error());
+}
+
+TEST_F(ClientChannelFactoryTest, DisableAutoReconnect) {
+ client_->Close(EIO);
+ ASSERT_FALSE(client_->IsConnected());
+ client_->DisableAutoReconnect();
+ auto status = client_->SendImpulse(17);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(ESHUTDOWN, status.error());
+ EXPECT_FALSE(client_->IsConnected());
+ client_->EnableAutoReconnect(kTimeout);
+ EXPECT_CALL(*client_, OnConnect());
+ OnConnectCallback([](auto* mock) {
+ EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0))
+ .WillOnce(Return(Status<void>{}));
+ });
+ ASSERT_TRUE(client_->SendImpulse(17));
+ EXPECT_TRUE(client_->IsConnected());
+}
+
+TEST_F(ClientTransactionTest, SendNoData) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(kTransactionState, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(1));
+ EXPECT_CALL(*mock_channel(),
+ SendWithFileHandle(kTransactionState, 2, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalHandle{-1})));
+ EXPECT_TRUE(transaction_.Send<LocalHandle>(2));
+ EXPECT_CALL(*mock_channel(), SendWithChannelHandle(kTransactionState, 3,
+ nullptr, 0, nullptr, 0))
+ .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, 1})));
+ EXPECT_TRUE(transaction_.Send<LocalChannelHandle>(3));
+}
+
+TEST_F(ClientTransactionTest, SendNoState) {
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+ EXPECT_TRUE(transaction_.Send<void>(1));
+}
+
+TEST_F(ClientTransactionTest, SendBuffers) {
+ const void* const kSendBuffer = IntToConstPtr(123);
+ const size_t kSendSize = 12;
+ void* const kReceiveBuffer = IntToPtr(456);
+ const size_t kReceiveSize = 34;
+
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(1, nullptr, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 2, Ne(nullptr), 1, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(2, kSendBuffer, kSendSize, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(3, kSendBuffer, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 4, nullptr, 0, Ne(nullptr), 1))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(
+ transaction_.Send<void>(4, nullptr, 0, kReceiveBuffer, kReceiveSize));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(5, nullptr, 0, kReceiveBuffer, 0));
+
+ EXPECT_CALL(*mock_channel(),
+ SendWithInt(nullptr, 5, Ne(nullptr), 1, Ne(nullptr), 1))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.Send<void>(5, kSendBuffer, kSendSize, kReceiveBuffer,
+ kReceiveSize));
+}
+
+TEST_F(ClientTransactionTest, SendVector) {
+ iovec send[3] = {};
+ iovec recv[4] = {};
+
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(1, nullptr, 0, nullptr, 0));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 2, send, 3, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(2, send, 3, recv, 4));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, send, 3, nullptr, 0))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(3, send, nullptr));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 4, nullptr, 0, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(4, nullptr, recv));
+
+ EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, send, 3, recv, 4))
+ .WillOnce(Return(0));
+ EXPECT_TRUE(transaction_.SendVector<void>(5, send, recv));
+}
+
+TEST_F(ClientTransactionTest, PushHandle) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const LocalHandle&>()))
+ .WillOnce(Return(1));
+ EXPECT_EQ(1, transaction_.PushFileHandle(LocalHandle{-1}).get());
+
+ EXPECT_CALL(*mock_channel(),
+ PushFileHandle(kTransactionState, A<const BorrowedHandle&>()))
+ .WillOnce(Return(2));
+ EXPECT_EQ(2, transaction_.PushFileHandle(BorrowedHandle{-1}).get());
+
+ EXPECT_EQ(3, transaction_.PushFileHandle(RemoteHandle{3}).get());
+
+ EXPECT_CALL(
+ *mock_channel(),
+ PushChannelHandle(kTransactionState, A<const LocalChannelHandle&>()))
+ .WillOnce(Return(11));
+ EXPECT_EQ(
+ 11, transaction_.PushChannelHandle(LocalChannelHandle{nullptr, 1}).get());
+
+ EXPECT_CALL(
+ *mock_channel(),
+ PushChannelHandle(kTransactionState, A<const BorrowedChannelHandle&>()))
+ .WillOnce(Return(12));
+ EXPECT_EQ(12, transaction_.PushChannelHandle(BorrowedChannelHandle{2}).get());
+
+ EXPECT_EQ(13, transaction_.PushChannelHandle(RemoteChannelHandle{13}).get());
+}
+
+TEST_F(ClientTransactionTest, GetHandle) {
+ void* const kTransactionState = IntToPtr(123);
+ EXPECT_CALL(*mock_channel(), AllocateTransactionState())
+ .WillOnce(Return(kTransactionState));
+ EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState));
+
+ EXPECT_CALL(*mock_channel(), GetFileHandle(kTransactionState, 1, _))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ LocalHandle file_handle;
+ EXPECT_FALSE(transaction_.GetFileHandle(1, &file_handle));
+ EXPECT_TRUE(transaction_.GetFileHandle(1, &file_handle));
+
+ EXPECT_CALL(*mock_channel(), GetChannelHandle(kTransactionState, 2, _))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ LocalChannelHandle channel_handle;
+ EXPECT_FALSE(transaction_.GetChannelHandle(2, &channel_handle));
+ EXPECT_TRUE(transaction_.GetChannelHandle(2, &channel_handle));
+}
diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp
new file mode 100644
index 0000000000..b7d94b3471
--- /dev/null
+++ b/libs/vr/libpdx/encoder_performance_test.cpp
@@ -0,0 +1,515 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx::rpc;
+using namespace android::pdx;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+using std::placeholders::_4;
+using std::placeholders::_5;
+using std::placeholders::_6;
+
+namespace {
+
+constexpr size_t kMaxStaticBufferSize = 20480;
+
+// Provide numpunct facet that formats numbers with ',' as thousands separators.
+class CommaNumPunct : public std::numpunct<char> {
+ protected:
+ char do_thousands_sep() const override { return ','; }
+ std::string do_grouping() const override { return "\03"; }
+};
+
+class TestPayload : public MessagePayload<SendBuffer>,
+ public MessageWriter,
+ public MessageReader,
+ public NoOpResourceMapper {
+ public:
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ const size_t section_offset = Size();
+ Extend(size);
+ return Data() + section_offset;
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*ConstCursor(), &*ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(ConstCursor(), PointerDistance(new_start, &*ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return this; }
+};
+
+class StaticBuffer : public MessageWriter,
+ public MessageReader,
+ public NoOpResourceMapper {
+ public:
+ void Clear() {
+ read_ptr_ = buffer_;
+ write_ptr_ = 0;
+ }
+ void Rewind() { read_ptr_ = buffer_; }
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ void* ptr = buffer_ + write_ptr_;
+ write_ptr_ += size;
+ return ptr;
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {read_ptr_, std::end(buffer_)};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ read_ptr_ = static_cast<const uint8_t*>(new_start);
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return this; }
+
+ private:
+ uint8_t buffer_[kMaxStaticBufferSize];
+ const uint8_t* read_ptr_{buffer_};
+ size_t write_ptr_{0};
+};
+
+// Simple callback function to clear/reset the input/output buffers for
+// serialization. Using raw function pointer here instead of std::function to
+// minimize the overhead of invocation in the tight test loop over millions of
+// iterations.
+using ResetFunc = void(void*);
+
+// Serialization test function signature, used by the TestRunner.
+using SerializeTestSignature = std::chrono::nanoseconds(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data);
+
+// Deserialization test function signature, used by the TestRunner.
+using DeserializeTestSignature = std::chrono::nanoseconds(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data);
+
+// Generic serialization test runner method. Takes the |value| of type T and
+// serializes it into the output buffer represented by |writer|.
+template <typename T>
+std::chrono::nanoseconds SerializeTestRunner(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data, const T& value) {
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ write_reset(reset_data);
+ Serialize(value, writer);
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// Generic deserialization test runner method. Takes the |value| of type T and
+// temporarily serializes it into the output buffer, then repeatedly
+// deserializes the data back from that buffer.
+template <typename T>
+std::chrono::nanoseconds DeserializeTestRunner(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
+ const T& value) {
+ write_reset(reset_data);
+ Serialize(value, writer);
+ T output_data;
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ read_reset(reset_data);
+ Deserialize(&output_data, reader);
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ if (output_data != value)
+ return start - stop; // Return negative value to indicate error.
+ return stop - start;
+}
+
+// Special version of SerializeTestRunner that doesn't perform any serialization
+// but does all the same setup steps and moves data of size |data_size| into
+// the output buffer. Useful to determine the baseline to calculate time used
+// just for serialization layer.
+std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer,
+ size_t iterations,
+ ResetFunc* write_reset,
+ void* reset_data, size_t data_size) {
+ std::vector<uint8_t> dummy_data(data_size);
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ write_reset(reset_data);
+ memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
+ dummy_data.data(), dummy_data.size());
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// Special version of DeserializeTestRunner that doesn't perform any
+// deserialization but invokes Rewind on the input buffer repeatedly.
+// Useful to determine the baseline to calculate time used just for
+// deserialization layer.
+std::chrono::nanoseconds DeserializeBaseTest(
+ MessageReader* reader, MessageWriter* writer, size_t iterations,
+ ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data,
+ size_t data_size) {
+ std::vector<uint8_t> dummy_data(data_size);
+ write_reset(reset_data);
+ memcpy(writer->GetNextWriteBufferSection(dummy_data.size()),
+ dummy_data.data(), dummy_data.size());
+ auto start = std::chrono::high_resolution_clock::now();
+ for (size_t i = 0; i < iterations; i++) {
+ read_reset(reset_data);
+ auto section = reader->GetNextReadBufferSection();
+ memcpy(dummy_data.data(), section.first, dummy_data.size());
+ reader->ConsumeReadBufferSectionData(
+ AdvancePointer(section.first, dummy_data.size()));
+ }
+ auto stop = std::chrono::high_resolution_clock::now();
+ return stop - start;
+}
+
+// The main class that accumulates individual tests to be executed.
+class TestRunner {
+ public:
+ struct BufferInfo {
+ BufferInfo(const std::string& buffer_name, MessageReader* reader,
+ MessageWriter* writer, ResetFunc* read_reset_func,
+ ResetFunc* write_reset_func, void* reset_data)
+ : name{buffer_name},
+ reader{reader},
+ writer{writer},
+ read_reset_func{read_reset_func},
+ write_reset_func{write_reset_func},
+ reset_data{reset_data} {}
+ std::string name;
+ MessageReader* reader;
+ MessageWriter* writer;
+ ResetFunc* read_reset_func;
+ ResetFunc* write_reset_func;
+ void* reset_data;
+ };
+
+ void AddTestFunc(const std::string& name,
+ std::function<SerializeTestSignature> serialize_test,
+ std::function<DeserializeTestSignature> deserialize_test,
+ size_t data_size) {
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::move(deserialize_test), data_size);
+ }
+
+ template <typename T>
+ void AddSerializationTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ auto serialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageWriter*, size_t, ResetFunc*, void*, const T&)>(
+ &SerializeTestRunner),
+ _1, _2, _3, _4, std::forward<T>(value));
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::function<DeserializeTestSignature>{}, data_size);
+ }
+
+ template <typename T>
+ void AddDeserializationTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ auto deserialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageReader*, MessageWriter*, size_t, ResetFunc*,
+ ResetFunc*, void*, const T&)>(&DeserializeTestRunner),
+ _1, _2, _3, _4, _5, _6, std::forward<T>(value));
+ tests_.emplace_back(name, std::function<SerializeTestSignature>{},
+ std::move(deserialize_test), data_size);
+ }
+
+ template <typename T>
+ void AddTest(const std::string& name, T&& value) {
+ const size_t data_size = GetSerializedSize(value);
+ if (data_size > kMaxStaticBufferSize) {
+ std::cerr << "Test '" << name << "' requires " << data_size
+ << " bytes in the serialization buffer but only "
+ << kMaxStaticBufferSize << " are available." << std::endl;
+ exit(1);
+ }
+ auto serialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageWriter*, size_t, ResetFunc*, void*, const T&)>(
+ &SerializeTestRunner),
+ _1, _2, _3, _4, value);
+ auto deserialize_test =
+ std::bind(static_cast<std::chrono::nanoseconds (*)(
+ MessageReader*, MessageWriter*, size_t, ResetFunc*,
+ ResetFunc*, void*, const T&)>(&DeserializeTestRunner),
+ _1, _2, _3, _4, _5, _6, std::forward<T>(value));
+ tests_.emplace_back(name, std::move(serialize_test),
+ std::move(deserialize_test), data_size);
+ }
+
+ std::string CenterString(std::string text, size_t column_width) {
+ if (text.size() < column_width) {
+ text = std::string((column_width - text.size()) / 2, ' ') + text;
+ }
+ return text;
+ }
+
+ void RunTests(size_t iteration_count,
+ const std::vector<BufferInfo>& buffers) {
+ using float_seconds = std::chrono::duration<double>;
+ const std::string name_column_separator = " : ";
+ const std::string buffer_column_separator = " || ";
+ const std::string buffer_timing_column_separator = " | ";
+ const size_t data_size_column_width = 6;
+ const size_t time_column_width = 9;
+ const size_t qps_column_width = 18;
+ const size_t buffer_column_width = time_column_width +
+ buffer_timing_column_separator.size() +
+ qps_column_width;
+
+ auto compare_name_length = [](const TestEntry& t1, const TestEntry& t2) {
+ return t1.name.size() < t2.name.size();
+ };
+ auto test_with_longest_name =
+ std::max_element(tests_.begin(), tests_.end(), compare_name_length);
+ size_t name_column_width = test_with_longest_name->name.size();
+
+ size_t total_width =
+ name_column_width + name_column_separator.size() +
+ data_size_column_width + buffer_column_separator.size() +
+ buffers.size() * (buffer_column_width + buffer_column_separator.size());
+
+ const std::string dbl_separator(total_width, '=');
+ const std::string separator(total_width, '-');
+
+ auto print_header = [&](const std::string& header) {
+ std::cout << dbl_separator << std::endl;
+ std::stringstream ss;
+ ss.imbue(std::locale(ss.getloc(), new CommaNumPunct));
+ ss << header << " (" << iteration_count << " iterations)";
+ std::cout << CenterString(ss.str(), total_width) << std::endl;
+ std::cout << dbl_separator << std::endl;
+ std::cout << std::setw(name_column_width) << "Test Name" << std::left
+ << name_column_separator << std::setw(data_size_column_width)
+ << CenterString("Size", data_size_column_width)
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ std::cout << std::setw(buffer_column_width)
+ << CenterString(buffer_info.name, buffer_column_width)
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ std::cout << std::setw(name_column_width) << "" << name_column_separator
+ << std::setw(data_size_column_width)
+ << CenterString("bytes", data_size_column_width)
+ << buffer_column_separator << std::left;
+ for (size_t i = 0; i < buffers.size(); i++) {
+ std::cout << std::setw(time_column_width)
+ << CenterString("Time, s", time_column_width)
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width)
+ << CenterString("QPS", qps_column_width)
+ << buffer_column_separator;
+ }
+ std::cout << std::right << std::endl;
+ std::cout << separator << std::endl;
+ };
+
+ print_header("Serialization benchmarks");
+ for (const auto& test : tests_) {
+ if (test.serialize_test) {
+ std::cout << std::setw(name_column_width) << test.name << " : "
+ << std::setw(data_size_column_width) << test.data_size
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ auto seconds =
+ std::chrono::duration_cast<float_seconds>(test.serialize_test(
+ buffer_info.writer, iteration_count,
+ buffer_info.write_reset_func, buffer_info.reset_data));
+ double qps = iteration_count / seconds.count();
+ std::cout << std::fixed << std::setprecision(3)
+ << std::setw(time_column_width) << seconds.count()
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width) << qps
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ }
+ }
+
+ print_header("Deserialization benchmarks");
+ for (const auto& test : tests_) {
+ if (test.deserialize_test) {
+ std::cout << std::setw(name_column_width) << test.name << " : "
+ << std::setw(data_size_column_width) << test.data_size
+ << buffer_column_separator;
+ for (const auto& buffer_info : buffers) {
+ auto seconds =
+ std::chrono::duration_cast<float_seconds>(test.deserialize_test(
+ buffer_info.reader, buffer_info.writer, iteration_count,
+ buffer_info.read_reset_func, buffer_info.write_reset_func,
+ buffer_info.reset_data));
+ double qps = iteration_count / seconds.count();
+ std::cout << std::fixed << std::setprecision(3)
+ << std::setw(time_column_width) << seconds.count()
+ << buffer_timing_column_separator
+ << std::setw(qps_column_width) << qps
+ << buffer_column_separator;
+ }
+ std::cout << std::endl;
+ }
+ }
+ std::cout << dbl_separator << std::endl;
+ }
+
+ private:
+ struct TestEntry {
+ TestEntry(const std::string& test_name,
+ std::function<SerializeTestSignature> serialize_test,
+ std::function<DeserializeTestSignature> deserialize_test,
+ size_t data_size)
+ : name{test_name},
+ serialize_test{std::move(serialize_test)},
+ deserialize_test{std::move(deserialize_test)},
+ data_size{data_size} {}
+ std::string name;
+ std::function<SerializeTestSignature> serialize_test;
+ std::function<DeserializeTestSignature> deserialize_test;
+ size_t data_size;
+ };
+
+ std::vector<TestEntry> tests_;
+};
+
+std::string GenerateContainerName(const std::string& type, size_t count) {
+ std::stringstream ss;
+ ss << type << "(" << count << ")";
+ return ss.str();
+}
+
+} // anonymous namespace
+
+int main(int /*argc*/, char** /*argv*/) {
+ const size_t iteration_count = 10000000; // 10M iterations.
+ TestRunner test_runner;
+ std::cout.imbue(std::locale(std::cout.getloc(), new CommaNumPunct));
+
+ // Baseline tests to figure out the overhead of buffer resizing and data
+ // transfers.
+ for (size_t len : {0, 1, 9, 66, 259}) {
+ auto serialize_base_test =
+ std::bind(&SerializeBaseTest, _1, _2, _3, _4, len);
+ auto deserialize_base_test =
+ std::bind(&DeserializeBaseTest, _1, _2, _3, _4, _5, _6, len);
+ test_runner.AddTestFunc("--Baseline--", std::move(serialize_base_test),
+ std::move(deserialize_base_test), len);
+ }
+
+ // Individual serialization/deserialization tests.
+ test_runner.AddTest("bool", true);
+ test_runner.AddTest("int32_t", 12);
+
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ test_runner.AddTest(GenerateContainerName("string", len),
+ std::string(len, '*'));
+ }
+ // Serialization is too slow to handle such large strings, add this test for
+ // deserialization only.
+ test_runner.AddDeserializationTest(GenerateContainerName("string", 10240),
+ std::string(10240, '*'));
+
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ std::vector<int32_t> int_vector(len);
+ std::iota(int_vector.begin(), int_vector.end(), 0);
+ test_runner.AddTest(GenerateContainerName("vector<int32_t>", len),
+ std::move(int_vector));
+ }
+
+ std::vector<std::string> vector_of_strings = {
+ "012345678901234567890123456789", "012345678901234567890123456789",
+ "012345678901234567890123456789", "012345678901234567890123456789",
+ "012345678901234567890123456789",
+ };
+ test_runner.AddTest(
+ GenerateContainerName("vector<string>", vector_of_strings.size()),
+ std::move(vector_of_strings));
+
+ test_runner.AddTest("tuple<int, bool, string, double>",
+ std::make_tuple(123, true, std::string{"foobar"}, 1.1));
+
+ for (size_t len : {0, 1, 8, 64}) {
+ std::map<int, std::string> test_map;
+ for (size_t i = 0; i < len; i++)
+ test_map.emplace(i, std::to_string(i));
+ test_runner.AddTest(GenerateContainerName("map<int, string>", len),
+ std::move(test_map));
+ }
+
+ for (size_t len : {0, 1, 8, 64}) {
+ std::unordered_map<int, std::string> test_map;
+ for (size_t i = 0; i < len; i++)
+ test_map.emplace(i, std::to_string(i));
+ test_runner.AddTest(
+ GenerateContainerName("unordered_map<int, string>", len),
+ std::move(test_map));
+ }
+
+ // BufferWrapper can't be used with deserialization tests right now because
+ // it requires external buffer to be filled in, which is not available.
+ std::vector<std::vector<uint8_t>> data_buffers;
+ for (size_t len : {0, 1, 8, 64, 256}) {
+ data_buffers.emplace_back(len);
+ test_runner.AddSerializationTest(
+ GenerateContainerName("BufferWrapper<uint8_t*>", len),
+ BufferWrapper<uint8_t*>(data_buffers.back().data(),
+ data_buffers.back().size()));
+ }
+
+ // Various backing buffers to run the tests on.
+ std::vector<TestRunner::BufferInfo> buffers;
+
+ Payload buffer;
+ buffers.emplace_back("Non-TLS Buffer", &buffer, &buffer,
+ [](void* ptr) { static_cast<Payload*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<Payload*>(ptr)->Clear(); },
+ &buffer);
+
+ TestPayload tls_buffer;
+ buffers.emplace_back(
+ "TLS Buffer", &tls_buffer, &tls_buffer,
+ [](void* ptr) { static_cast<TestPayload*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<TestPayload*>(ptr)->Clear(); }, &tls_buffer);
+
+ StaticBuffer static_buffer;
+ buffers.emplace_back(
+ "Static Buffer", &static_buffer, &static_buffer,
+ [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Rewind(); },
+ [](void* ptr) { static_cast<StaticBuffer*>(ptr)->Clear(); },
+ &static_buffer);
+
+ // Finally, run all the tests.
+ test_runner.RunTests(iteration_count, buffers);
+ return 0;
+}
diff --git a/libs/vr/libpdx/mock_tests.cpp b/libs/vr/libpdx/mock_tests.cpp
new file mode 100644
index 0000000000..76fd1541a5
--- /dev/null
+++ b/libs/vr/libpdx/mock_tests.cpp
@@ -0,0 +1,20 @@
+#include <gtest/gtest.h>
+#include <pdx/mock_client_channel.h>
+#include <pdx/mock_client_channel_factory.h>
+#include <pdx/mock_message_reader.h>
+#include <pdx/mock_message_writer.h>
+#include <pdx/mock_service_dispatcher.h>
+#include <pdx/mock_service_endpoint.h>
+
+TEST(MockTypes, Instantiation) {
+ // Make sure all our interfaces are mocked out properly and mock instances
+ // can be created.
+ android::pdx::MockClientChannel client_channel;
+ android::pdx::MockClientChannelFactory client_channel_factory;
+ android::pdx::MockInputResourceMapper input_resource_mapper;
+ android::pdx::MockMessageReader message_reader;
+ android::pdx::MockOutputResourceMapper output_resource_mapper;
+ android::pdx::MockMessageWriter message_writer;
+ android::pdx::MockServiceDispatcher service_dispatcher;
+ android::pdx::MockEndpoint endpoint;
+}
diff --git a/libs/vr/libpdx/private/pdx/channel_handle.h b/libs/vr/libpdx/private/pdx/channel_handle.h
new file mode 100644
index 0000000000..1e62d250ec
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/channel_handle.h
@@ -0,0 +1,123 @@
+#ifndef ANDROID_PDX_CHANNEL_HANDLE_H_
+#define ANDROID_PDX_CHANNEL_HANDLE_H_
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+
+enum class ChannelHandleMode {
+ Local,
+ Borrowed,
+ Remote,
+};
+
+class ChannelManagerInterface {
+ public:
+ virtual void CloseHandle(std::int32_t handle) = 0;
+
+ protected:
+ // Nobody should be allowed to delete the instance of channel manager
+ // through this interface.
+ virtual ~ChannelManagerInterface() = default;
+};
+
+class ChannelHandleBase {
+ public:
+ ChannelHandleBase() = default;
+ ChannelHandleBase(const int32_t& value) : value_{value} {}
+
+ ChannelHandleBase(const ChannelHandleBase&) = delete;
+ ChannelHandleBase& operator=(const ChannelHandleBase&) = delete;
+
+ std::int32_t value() const { return value_; }
+ bool valid() const { return value_ >= 0; }
+ explicit operator bool() const { return valid(); }
+
+ void Close() { value_ = kEmptyHandle; }
+
+ protected:
+ // Must not be used by itself. Must be derived from.
+ ~ChannelHandleBase() = default;
+ enum : std::int32_t { kEmptyHandle = -1 };
+
+ std::int32_t value_{kEmptyHandle};
+};
+
+template <ChannelHandleMode Mode>
+class ChannelHandle : public ChannelHandleBase {
+ public:
+ ChannelHandle() = default;
+ using ChannelHandleBase::ChannelHandleBase;
+ ChannelHandle(ChannelHandle&& other) : ChannelHandleBase{other.value_} {
+ other.value_ = kEmptyHandle;
+ }
+ ~ChannelHandle() = default;
+
+ ChannelHandle Duplicate() const { return ChannelHandle{value_}; }
+
+ ChannelHandle& operator=(ChannelHandle&& other) {
+ value_ = other.value_;
+ other.value_ = kEmptyHandle;
+ return *this;
+ }
+};
+
+template <>
+class ChannelHandle<ChannelHandleMode::Local> : public ChannelHandleBase {
+ public:
+ ChannelHandle() = default;
+ ChannelHandle(ChannelManagerInterface* manager, int32_t value)
+ : ChannelHandleBase{value}, manager_{manager} {}
+
+ ChannelHandle(const ChannelHandle&) = delete;
+ ChannelHandle& operator=(const ChannelHandle&) = delete;
+
+ ChannelHandle(ChannelHandle&& other)
+ : ChannelHandleBase{other.value_}, manager_{other.manager_} {
+ other.manager_ = nullptr;
+ other.value_ = kEmptyHandle;
+ }
+
+ ChannelHandle& operator=(ChannelHandle&& other) {
+ value_ = other.value_;
+ manager_ = other.manager_;
+ other.value_ = kEmptyHandle;
+ other.manager_ = nullptr;
+ return *this;
+ }
+
+ ~ChannelHandle() {
+ if (manager_)
+ manager_->CloseHandle(value_);
+ }
+
+ ChannelHandle<ChannelHandleMode::Borrowed> Borrow() const {
+ return ChannelHandle<ChannelHandleMode::Borrowed>{value_};
+ }
+
+ void Close() {
+ if (manager_)
+ manager_->CloseHandle(value_);
+ manager_ = nullptr;
+ value_ = kEmptyHandle;
+ }
+
+ private:
+ ChannelManagerInterface* manager_{nullptr};
+};
+
+using LocalChannelHandle = ChannelHandle<ChannelHandleMode::Local>;
+using BorrowedChannelHandle = ChannelHandle<ChannelHandleMode::Borrowed>;
+using RemoteChannelHandle = ChannelHandle<ChannelHandleMode::Remote>;
+
+// ChannelReference is a 32 bit integer used in IPC serialization to be
+// transferred across processes. You can convert this value to a local channel
+// handle by calling Transaction.GetChannelHandle().
+using ChannelReference = int32_t;
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CHANNEL_HANDLE_H_
diff --git a/libs/vr/libpdx/private/pdx/client.h b/libs/vr/libpdx/private/pdx/client.h
new file mode 100644
index 0000000000..656de7e2ca
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client.h
@@ -0,0 +1,301 @@
+#ifndef ANDROID_PDX_CLIENT_H_
+#define ANDROID_PDX_CLIENT_H_
+
+#include <errno.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <pdx/channel_handle.h>
+#include <pdx/client_channel.h>
+#include <pdx/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+#include <pdx/rpc/remote_method_type.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class Transaction;
+
+/*
+ * Base class of client-side service API classes.
+ */
+class Client {
+ public:
+ static const int64_t kInfiniteTimeout = -1;
+
+ virtual ~Client() = default;
+
+ /*
+ * Returns true if the Client instance successfully initialized, false
+ * otherwise. Subclasses that can fail to initialize must override this and
+ * AND their initialization result with this base class method's result.
+ *
+ * This method is not intended to perform initialization, only to report
+ * the status of the initialization.
+ */
+ virtual bool IsInitialized() const;
+
+ /*
+ * Returns the error code describing the Client initialization failure, or 0
+ * if there was no failure.
+ */
+ int error() const;
+
+ // Returns a reference to IPC channel handle.
+ LocalChannelHandle& GetChannelHandle();
+
+ protected:
+ friend Transaction;
+ explicit Client(std::unique_ptr<ClientChannel> channel);
+ explicit Client(std::unique_ptr<ClientChannelFactory> channel_factory,
+ int64_t timeout_ms = kInfiniteTimeout);
+
+ /*
+ * Called by Client::Connect() after successfully connecting to the service
+ * endpoint. Subclasses may override this method to perform additional setup,
+ * including sending messages to complete the connection process.
+ *
+ * Subclasses may call Client::Close() within this method to terminate the
+ * connection; Client::Connect() returns the negated error passed to
+ * Client::Close() when this happens.
+ */
+ virtual void OnConnect();
+
+ enum : size_t { MAX_IMPULSE_LENGTH = sizeof(uint64_t) * 4 };
+
+ Status<void> SendImpulse(int opcode);
+ Status<void> SendImpulse(int opcode, const void* buffer, size_t length);
+
+ /*
+ * Remote method call API using pdx::rpc serialization.
+ * Include pdx/rpc/remote_method.h to use these methods.
+ */
+ template <typename RemoteMethodType, typename... Args>
+ Status<typename RemoteMethodType::Return> InvokeRemoteMethod(Args&&... args);
+
+ template <typename RemoteMethodType, typename ReturnType, typename... Args>
+ Status<void> InvokeRemoteMethodInPlace(ReturnType* return_value,
+ Args&&... args);
+
+ /*
+ * Close the endpoint file descriptor and optionally indicate an error, which
+ * may be retrieved through error(). Subclasses may use this in their
+ * constructor to signal failure during initialization or at other times
+ * during operation.
+ */
+ void Close(int error);
+
+ /*
+ * Returns true if the client is connected to the service, false otherwise.
+ */
+ bool IsConnected() const;
+
+ /*
+ * Enables auto-reconnect with the given timeout. Use kInfiniteTimeout (-1)
+ * for no timeout. Auto-reconnect can only be enabled if the Client class
+ * was constructed with a ClientChannelFactory.
+ */
+ void EnableAutoReconnect(int64_t reconnect_timeout_ms);
+
+ /*
+ * Disables auto-reconnect.
+ */
+ void DisableAutoReconnect();
+
+ /*
+ * Returns an fd that the client may use to check/wait for asynchronous
+ * notifications to the channel. It is implementation dependent how the
+ * transport backend handles this feature, however all implementations must
+ * support at least POLLIN/EPOLLIN/readable.
+ *
+ * For uses that require more than one type of event, use
+ * ClientChannel::GetEventMask() to distinguish between events.
+ */
+ int event_fd() const;
+
+ /*
+ * Returns the underlying ClientChannel object.
+ */
+ ClientChannel* GetChannel() const { return channel_.get(); }
+ std::unique_ptr<ClientChannel>&& TakeChannel() { return std::move(channel_); }
+
+ private:
+ Client(const Client&) = delete;
+ void operator=(const Client&) = delete;
+
+ Status<void> CheckReconnect();
+ bool NeedToDisconnectChannel(int error) const;
+ void CheckDisconnect(int error);
+
+ template <typename T>
+ inline void CheckDisconnect(const Status<T>& status) {
+ if (!status)
+ CheckDisconnect(status.error());
+ }
+
+ std::unique_ptr<ClientChannel> channel_;
+ int error_{0};
+
+ // Reconnection state.
+ std::unique_ptr<ClientChannelFactory> channel_factory_;
+ int64_t reconnect_timeout_ms_{0};
+ bool auto_reconnect_enabled_{false};
+};
+
+/*
+ * Utility template base class for client-side service API classes. Handles
+ * initialization checks during allocation and automatically cleans up on
+ * failure.
+ *
+ * @tparam T Type of the class extending this one.
+ * @tparam C Client class to wrap. Defaults to the Client class.
+ */
+template <typename T, typename ParentClient = Client>
+class ClientBase : public ParentClient {
+ public:
+ // Type of the client this class wraps.
+ using ClientType = ParentClient;
+
+ static_assert(std::is_base_of<Client, ParentClient>::value,
+ "The provided parent client is not a Client subclass.");
+
+ /*
+ * Allocates a new instance of the superclass and checks for successful
+ * initialization.
+ *
+ * The variadic arguments must expand to match one of type T's constructors
+ * and are passed through unchanged. If a timeout is desired, subclasses are
+ * responsible for passing this through to the appropriate ClientBase
+ * constructor.
+ *
+ * Returns a unique_ptr to the new instance on success, or an empty unique_ptr
+ * otherwise.
+ */
+ template <typename... Args>
+ static inline std::unique_ptr<T> Create(Args&&... args) {
+ std::unique_ptr<T> client(new T(std::forward<Args>(args)...));
+ if (client->IsInitialized())
+ return client;
+ else
+ return nullptr;
+ }
+
+ protected:
+ /*
+ * Type of the base class. Useful for referencing the base class type and
+ * constructor in subclasses. Subclasses with non-public constructors
+ * must declare BASE a friend.
+ */
+ using BASE = ClientBase<T, ParentClient>;
+
+ /*
+ * Type of the unique_ptr deleter. Useful for friend declarations.
+ */
+ using deleter_type = typename std::unique_ptr<T>::deleter_type;
+
+ using ParentClient::ParentClient;
+};
+
+class Transaction final : public OutputResourceMapper,
+ public InputResourceMapper {
+ public:
+ Transaction(Client& client);
+ ~Transaction();
+
+ template <typename T>
+ Status<T> Send(int opcode) {
+ return SendVector<T>(opcode, nullptr, 0, nullptr, 0);
+ }
+
+ template <typename T>
+ Status<T> Send(int opcode, const void* send_buffer, size_t send_length,
+ void* receive_buffer, size_t receive_length) {
+ const bool send = (send_buffer && send_length);
+ const bool receive = (receive_buffer && receive_length);
+ const iovec send_vector = {const_cast<void*>(send_buffer), send_length};
+ const iovec receive_vector = {receive_buffer, receive_length};
+ return SendVector<T>(opcode, send ? &send_vector : nullptr, send ? 1 : 0,
+ receive ? &receive_vector : nullptr, receive ? 1 : 0);
+ }
+
+ template <typename T>
+ Status<T> SendVector(int opcode, const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count) {
+ Status<T> ret;
+ SendTransaction(opcode, &ret, send_vector, send_count, receive_vector,
+ receive_count);
+ return ret;
+ }
+
+ template <typename T, size_t send_count, size_t receive_count>
+ Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count],
+ const iovec (&receive_vector)[receive_count]) {
+ return SendVector<T>(opcode, send_vector, send_count, receive_vector,
+ receive_count);
+ }
+
+ template <typename T, size_t send_count>
+ Status<T> SendVector(int opcode, const iovec (&send_vector)[send_count],
+ std::nullptr_t) {
+ return SendVector<T>(opcode, send_vector, send_count, nullptr, 0);
+ }
+
+ template <typename T, size_t receive_count>
+ Status<T> SendVector(int opcode, std::nullptr_t,
+ const iovec (&receive_vector)[receive_count]) {
+ return SendVector<T>(opcode, nullptr, 0, receive_vector, receive_count);
+ }
+
+ // OutputResourceMapper
+ Status<FileReference> PushFileHandle(const LocalHandle& handle) override;
+ Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override;
+ Status<FileReference> PushFileHandle(const RemoteHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const LocalChannelHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const BorrowedChannelHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const RemoteChannelHandle& handle) override;
+
+ // InputResourceMapper
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override;
+
+ private:
+ bool EnsureStateAllocated();
+ void SendTransaction(int opcode, Status<void>* ret, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector,
+ size_t receive_count);
+ void SendTransaction(int opcode, Status<int>* ret, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector,
+ size_t receive_count);
+ void SendTransaction(int opcode, Status<LocalHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count);
+ void SendTransaction(int opcode, Status<LocalChannelHandle>* ret,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count);
+ void CheckDisconnect(int error);
+
+ template <typename T>
+ inline void CheckDisconnect(const Status<T>& status) {
+ if (!status)
+ CheckDisconnect(status.error());
+ }
+
+ Client& client_;
+ void* state_{nullptr};
+ bool state_allocated_{false};
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h
new file mode 100644
index 0000000000..dbfd626d6f
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client_channel.h
@@ -0,0 +1,58 @@
+#ifndef ANDROID_PDX_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_CLIENT_CHANNEL_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+struct iovec;
+
+namespace android {
+namespace pdx {
+
+class ClientChannel {
+ public:
+ virtual ~ClientChannel() = default;
+
+ // Returns a tag that uniquely identifies a specific underlying IPC transport.
+ virtual uint32_t GetIpcTag() const = 0;
+
+ virtual int event_fd() const = 0;
+ virtual Status<int> GetEventMask(int events) = 0;
+
+ virtual LocalChannelHandle& GetChannelHandle() = 0;
+ virtual void* AllocateTransactionState() = 0;
+ virtual void FreeTransactionState(void* state) = 0;
+
+ virtual Status<void> SendImpulse(int opcode, const void* buffer,
+ size_t length) = 0;
+
+ virtual Status<int> SendWithInt(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) = 0;
+ virtual Status<LocalHandle> SendWithFileHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) = 0;
+ virtual Status<LocalChannelHandle> SendWithChannelHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) = 0;
+
+ virtual FileReference PushFileHandle(void* transaction_state,
+ const LocalHandle& handle) = 0;
+ virtual FileReference PushFileHandle(void* transaction_state,
+ const BorrowedHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ void* transaction_state, const LocalChannelHandle& handle) = 0;
+ virtual ChannelReference PushChannelHandle(
+ void* transaction_state, const BorrowedChannelHandle& handle) = 0;
+ virtual bool GetFileHandle(void* transaction_state, FileReference ref,
+ LocalHandle* handle) const = 0;
+ virtual bool GetChannelHandle(void* transaction_state, ChannelReference ref,
+ LocalChannelHandle* handle) const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel_factory.h b/libs/vr/libpdx/private/pdx/client_channel_factory.h
new file mode 100644
index 0000000000..a82ab70be3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client_channel_factory.h
@@ -0,0 +1,21 @@
+#ifndef ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
+
+#include <pdx/client_channel.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class ClientChannelFactory {
+ public:
+ virtual ~ClientChannelFactory() = default;
+
+ virtual Status<std::unique_ptr<ClientChannel>> Connect(
+ int64_t timeout_ms) const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx/private/pdx/file_handle.h b/libs/vr/libpdx/private/pdx/file_handle.h
new file mode 100644
index 0000000000..b3c3ad7c8c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/file_handle.h
@@ -0,0 +1,141 @@
+#ifndef ANDROID_PDX_FILE_HANDLE_H_
+#define ANDROID_PDX_FILE_HANDLE_H_
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace android {
+namespace pdx {
+
+enum class FileHandleMode {
+ Local,
+ Remote,
+ Borrowed,
+};
+
+// Manages ownership, sharing, and lifetime of file descriptors.
+template <FileHandleMode Mode>
+class FileHandle {
+ public:
+ static constexpr int kEmptyFileHandle = -1;
+
+ // Constructs an empty FileHandle object.
+ FileHandle() : fd_(kEmptyFileHandle) {}
+
+ // Constructs a FileHandle from an integer file descriptor and takes
+ // ownership.
+ explicit FileHandle(int fd) : fd_(fd) {}
+
+ // Constructs a FileHandle by opening |path|. The arguments follow the
+ // semantics of open().
+ FileHandle(const std::string& path, int flags, mode_t mode = 0) {
+ fd_ = open(path.c_str(), flags, mode);
+ }
+
+ // Constructs a FileHandle by opening |path| relative to |dir_fd|, following
+ // the semantics of openat().
+ FileHandle(const int directory_fd, const std::string& path, int flags,
+ mode_t mode = 0) {
+ fd_ = openat(directory_fd, path.c_str(), flags, mode);
+ }
+
+ // Move constructor that assumes ownership of the file descriptor, leaving the
+ // other FileHandle object empty.
+ FileHandle(FileHandle&& other) {
+ fd_ = other.fd_;
+ other.fd_ = kEmptyFileHandle;
+ }
+
+ // Returns a FileHandle as a duplicate handle of |fd|. Borrowed handles and
+ // handles in remote handle space are not duplicated.
+ static FileHandle AsDuplicate(const int fd) {
+ if (Mode == FileHandleMode::Local)
+ return FileHandle(dup(fd));
+ else
+ return FileHandle(fd);
+ }
+
+ // Destructor closes the file descriptor when non-empty.
+ ~FileHandle() { Close(); }
+
+ // Move assignment operator that assumes ownership of the underlying file
+ // descriptor, leaving the other FileHandle object empty.
+ FileHandle& operator=(FileHandle&& other) {
+ if (this != &other) {
+ Reset(other.fd_);
+ other.fd_ = kEmptyFileHandle;
+ }
+ return *this;
+ }
+
+ // Resets the underlying file handle to |fd|.
+ void Reset(int fd) {
+ Close();
+ fd_ = fd;
+ }
+
+ // Closes the underlying file descriptor when non-empty.
+ void Close() {
+ if (IsValid() && Mode == FileHandleMode::Local)
+ close(fd_);
+ fd_ = kEmptyFileHandle;
+ }
+
+ // Return the internal fd, passing ownership to the caller.
+ int Release() {
+ int release_fd = fd_;
+ fd_ = kEmptyFileHandle;
+ return release_fd;
+ }
+
+ // Duplicates the underlying file descriptor and returns a FileHandle that
+ // owns the new file descriptor. File descriptors are not duplicated when Mode
+ // is Remote or Borrowed.
+ FileHandle Duplicate() const {
+ return FileHandle(Mode == FileHandleMode::Local ? dup(fd_) : fd_);
+ }
+
+ FileHandle<FileHandleMode::Borrowed> Borrow() const {
+ return FileHandle<FileHandleMode::Borrowed>(Get());
+ }
+
+ // Gets the underlying file descriptor value.
+ int Get() const { return fd_; }
+ bool IsValid() const { return fd_ >= 0; }
+ explicit operator bool() const { return IsValid(); }
+
+ private:
+ int fd_;
+
+ FileHandle(const FileHandle&) = delete;
+ void operator=(const FileHandle&) = delete;
+};
+
+// Alias for a file handle in the local process' handle space.
+using LocalHandle = FileHandle<FileHandleMode::Local>;
+
+// Alias for a file handle in another process' handle space. Handles returned
+// from pushing a file object or channel must be stored in this type of handle
+// class, which doesn't close the underlying file descriptor. The underlying
+// file descriptor in this wrapper should not be passed to close() because doing
+// so would close an unrelated file descriptor in the local handle space.
+using RemoteHandle = FileHandle<FileHandleMode::Remote>;
+
+// Alias for borrowed handles in the local process' handle space. A borrowed
+// file handle is not close() because this wrapper does not own the underlying
+// file descriptor. Care must be take to ensure that a borrowed file handle
+// remains valid during use.
+using BorrowedHandle = FileHandle<FileHandleMode::Borrowed>;
+
+// FileReference is a 16 bit integer used in IPC serialization to be
+// transferred across processes. You can convert this value to a local file
+// handle by calling Transaction.GetFileHandle() on client side and
+// Message.GetFileHandle() on service side.
+using FileReference = int16_t;
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_FILE_HANDLE_H_
diff --git a/libs/vr/libpdx/private/pdx/message_reader.h b/libs/vr/libpdx/private/pdx/message_reader.h
new file mode 100644
index 0000000000..bc280cffd7
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_reader.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_MESSAGE_READER_H_
+#define ANDROID_PDX_MESSAGE_READER_H_
+
+#include <memory>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+
+class InputResourceMapper {
+ public:
+ virtual bool GetFileHandle(FileReference ref, LocalHandle* handle) = 0;
+ virtual bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) = 0;
+
+ protected:
+ virtual ~InputResourceMapper() = default;
+};
+
+class MessageReader {
+ public:
+ // Pointers to start/end of the region in the read buffer.
+ using BufferSection = std::pair<const void*, const void*>;
+
+ virtual BufferSection GetNextReadBufferSection() = 0;
+ virtual void ConsumeReadBufferSectionData(const void* new_start) = 0;
+ virtual InputResourceMapper* GetInputResourceMapper() = 0;
+
+ protected:
+ virtual ~MessageReader() = default;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MESSAGE_READER_H_
diff --git a/libs/vr/libpdx/private/pdx/message_writer.h b/libs/vr/libpdx/private/pdx/message_writer.h
new file mode 100644
index 0000000000..4a101d6806
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_writer.h
@@ -0,0 +1,40 @@
+#ifndef ANDROID_PDX_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MESSAGE_WRITER_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+
+class OutputResourceMapper {
+ public:
+ virtual Status<FileReference> PushFileHandle(const LocalHandle& handle) = 0;
+ virtual Status<FileReference> PushFileHandle(
+ const BorrowedHandle& handle) = 0;
+ virtual Status<FileReference> PushFileHandle(const RemoteHandle& handle) = 0;
+ virtual Status<ChannelReference> PushChannelHandle(
+ const LocalChannelHandle& handle) = 0;
+ virtual Status<ChannelReference> PushChannelHandle(
+ const BorrowedChannelHandle& handle) = 0;
+ virtual Status<ChannelReference> PushChannelHandle(
+ const RemoteChannelHandle& handle) = 0;
+
+ protected:
+ virtual ~OutputResourceMapper() = default;
+};
+
+class MessageWriter {
+ public:
+ virtual void* GetNextWriteBufferSection(size_t size) = 0;
+ virtual OutputResourceMapper* GetOutputResourceMapper() = 0;
+
+ protected:
+ virtual ~MessageWriter() = default;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MESSAGE_WRITER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h
new file mode 100644
index 0000000000..561c939daf
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h
@@ -0,0 +1,56 @@
+#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
+
+#include <gmock/gmock.h>
+#include <pdx/client_channel.h>
+
+namespace android {
+namespace pdx {
+
+class MockClientChannel : public ClientChannel {
+ public:
+ MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
+ MOCK_CONST_METHOD0(event_fd, int());
+ MOCK_METHOD1(GetEventMask, Status<int>(int));
+ MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&());
+ MOCK_METHOD0(AllocateTransactionState, void*());
+ MOCK_METHOD1(FreeTransactionState, void(void* state));
+ MOCK_METHOD3(SendImpulse,
+ Status<void>(int opcode, const void* buffer, size_t length));
+ MOCK_METHOD6(SendWithInt,
+ Status<int>(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count));
+ MOCK_METHOD6(SendWithFileHandle,
+ Status<LocalHandle>(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count));
+ MOCK_METHOD6(SendWithChannelHandle,
+ Status<LocalChannelHandle>(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count));
+ MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state,
+ const LocalHandle& handle));
+ MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state,
+ const BorrowedHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(void* transaction_state,
+ const LocalChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ ChannelReference(void* transaction_state,
+ const BorrowedChannelHandle& handle));
+ MOCK_CONST_METHOD3(GetFileHandle,
+ bool(void* transaction_state, FileReference ref,
+ LocalHandle* handle));
+ MOCK_CONST_METHOD3(GetChannelHandle,
+ bool(void* transaction_state, ChannelReference ref,
+ LocalChannelHandle* handle));
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h
new file mode 100644
index 0000000000..0190f5e458
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel_factory.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
+
+#include <gmock/gmock.h>
+#include <pdx/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+
+class MockClientChannelFactory : public ClientChannelFactory {
+ public:
+ MOCK_CONST_METHOD1(
+ Connect, Status<std::unique_ptr<ClientChannel>>(int64_t timeout_ms));
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_message_reader.h b/libs/vr/libpdx/private/pdx/mock_message_reader.h
new file mode 100644
index 0000000000..85e96ef993
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_reader.h
@@ -0,0 +1,27 @@
+#ifndef ANDROID_PDX_MOCK_MESSAGE_READER_H_
+#define ANDROID_PDX_MOCK_MESSAGE_READER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/message_reader.h>
+
+namespace android {
+namespace pdx {
+
+class MockInputResourceMapper : public InputResourceMapper {
+ public:
+ MOCK_METHOD2(GetFileHandle, bool(FileReference ref, LocalHandle* handle));
+ MOCK_METHOD2(GetChannelHandle,
+ bool(ChannelReference ref, LocalChannelHandle* handle));
+};
+
+class MockMessageReader : public MessageReader {
+ public:
+ MOCK_METHOD0(GetNextReadBufferSection, BufferSection());
+ MOCK_METHOD1(ConsumeReadBufferSectionData, void(const void* new_start));
+ MOCK_METHOD0(GetInputResourceMapper, InputResourceMapper*());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_MESSAGE_READER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_message_writer.h b/libs/vr/libpdx/private/pdx/mock_message_writer.h
new file mode 100644
index 0000000000..e06e5bbc2b
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_writer.h
@@ -0,0 +1,35 @@
+#ifndef ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/message_writer.h>
+
+namespace android {
+namespace pdx {
+
+class MockOutputResourceMapper : public OutputResourceMapper {
+ public:
+ MOCK_METHOD1(PushFileHandle,
+ Status<FileReference>(const LocalHandle& handle));
+ MOCK_METHOD1(PushFileHandle,
+ Status<FileReference>(const BorrowedHandle& handle));
+ MOCK_METHOD1(PushFileHandle,
+ Status<FileReference>(const RemoteHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ Status<ChannelReference>(const LocalChannelHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ Status<ChannelReference>(const BorrowedChannelHandle& handle));
+ MOCK_METHOD1(PushChannelHandle,
+ Status<ChannelReference>(const RemoteChannelHandle& handle));
+};
+
+class MockMessageWriter : public MessageWriter {
+ public:
+ MOCK_METHOD1(GetNextWriteBufferSection, void*(size_t size));
+ MOCK_METHOD0(GetOutputResourceMapper, OutputResourceMapper*());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_MESSAGE_WRITER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
new file mode 100644
index 0000000000..9b51d30592
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_dispatcher.h
@@ -0,0 +1,24 @@
+#ifndef ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
+
+#include <gmock/gmock.h>
+#include <pdx/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+
+class MockServiceDispatcher : public ServiceDispatcher {
+ public:
+ MOCK_METHOD1(AddService, int(const std::shared_ptr<Service>& service));
+ MOCK_METHOD1(RemoveService, int(const std::shared_ptr<Service>& service));
+ MOCK_METHOD0(ReceiveAndDispatch, int());
+ MOCK_METHOD1(ReceiveAndDispatch, int(int timeout));
+ MOCK_METHOD0(EnterDispatchLoop, int());
+ MOCK_METHOD1(SetCanceled, void(bool cancel));
+ MOCK_CONST_METHOD0(IsCanceled, bool());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx/private/pdx/mock_service_endpoint.h b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
new file mode 100644
index 0000000000..e741d4a46b
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
@@ -0,0 +1,74 @@
+#ifndef ANDROID_PDX_MOCK_ENDPOINT_H_
+#define ANDROID_PDX_MOCK_ENDPOINT_H_
+
+#include <gmock/gmock.h>
+#include <pdx/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+
+class MockEndpoint : public Endpoint {
+ public:
+ MOCK_CONST_METHOD0(GetIpcTag, uint32_t());
+ MOCK_METHOD1(SetService, Status<void>(Service* service));
+ MOCK_METHOD2(SetChannel, Status<void>(int channel_id, Channel* channel));
+ MOCK_METHOD1(CloseChannel, Status<void>(int channel_id));
+ MOCK_METHOD3(ModifyChannelEvents,
+ Status<void>(int channel_id, int clear_mask, int set_mask));
+ MOCK_METHOD4(PushChannel,
+ Status<RemoteChannelHandle>(Message* message, int flags,
+ Channel* channel, int* channel_id));
+ MOCK_METHOD3(CheckChannel,
+ Status<int>(const Message* message, ChannelReference ref,
+ Channel** channel));
+ MOCK_METHOD1(MessageReceive, Status<void>(Message* message));
+ MOCK_METHOD2(MessageReply, Status<void>(Message* message, int return_code));
+ MOCK_METHOD2(MessageReplyFd,
+ Status<void>(Message* message, unsigned int push_fd));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ Status<void>(Message* message,
+ const LocalChannelHandle& handle));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ Status<void>(Message* message,
+ const BorrowedChannelHandle& handle));
+ MOCK_METHOD2(MessageReplyChannelHandle,
+ Status<void>(Message* message,
+ const RemoteChannelHandle& handle));
+ MOCK_METHOD3(ReadMessageData,
+ Status<size_t>(Message* message, const iovec* vector,
+ size_t vector_length));
+ MOCK_METHOD3(WriteMessageData,
+ Status<size_t>(Message* message, const iovec* vector,
+ size_t vector_length));
+ MOCK_METHOD2(PushFileHandle,
+ Status<FileReference>(Message* message,
+ const LocalHandle& handle));
+ MOCK_METHOD2(PushFileHandle,
+ Status<FileReference>(Message* message,
+ const BorrowedHandle& handle));
+ MOCK_METHOD2(PushFileHandle,
+ Status<FileReference>(Message* message,
+ const RemoteHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ Status<ChannelReference>(Message* message,
+ const LocalChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ Status<ChannelReference>(Message* message,
+ const BorrowedChannelHandle& handle));
+ MOCK_METHOD2(PushChannelHandle,
+ Status<ChannelReference>(Message* message,
+ const RemoteChannelHandle& handle));
+ MOCK_CONST_METHOD2(GetFileHandle,
+ LocalHandle(Message* message, FileReference ref));
+ MOCK_CONST_METHOD2(GetChannelHandle,
+ LocalChannelHandle(Message* message,
+ ChannelReference ref));
+ MOCK_METHOD0(AllocateMessageState, void*());
+ MOCK_METHOD1(FreeMessageState, void(void* state));
+ MOCK_METHOD0(Cancel, Status<void>());
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_MOCK_ENDPOINT_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h
new file mode 100644
index 0000000000..e0062845a5
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/argument_encoder.h
@@ -0,0 +1,184 @@
+#ifndef ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
+#define ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
+
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/rpc/serialization.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Provides automatic serialization of argument lists and return
+// values by analyzing the supplied function signature types.
+// Examples:
+// ArgumentEncoder<int(int, float)> encoder(writer);
+// encoder.EncodeArguments(1, 1.0);
+
+template <typename T>
+class ArgumentEncoder;
+
+// Specialization of ArgumentEncoder for void return types.
+template <typename... Args>
+class ArgumentEncoder<void(Args...)> {
+ public:
+ explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {}
+
+ // Serializes the arguments as a tuple.
+ void EncodeArguments(Args... args) {
+ Serialize(std::forward_as_tuple(args...), writer_);
+ }
+
+ private:
+ MessageWriter* writer_;
+};
+
+// Specialization of ArgumentEncoder for non-void return types.
+template <typename Return, typename... Args>
+class ArgumentEncoder<Return(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ReturnType = typename std::decay<Return>::type;
+
+ explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {}
+
+ // Serializes the arguments as a tuple.
+ void EncodeArguments(Args... args) {
+ Serialize(std::forward_as_tuple(args...), writer_);
+ }
+
+ // Serializes the return value for rvalue references.
+ void EncodeReturn(const ReturnType& return_value) {
+ Serialize(return_value, writer_);
+ }
+
+ private:
+ MessageWriter* writer_;
+};
+
+// Utility to build an ArgumentEncoder from a function pointer and a message
+// writer.
+template <typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (*)(Args...), MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a method pointer and a message
+// writer.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (Class::*)(Args...), MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a const method pointer and a
+// message writer.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentEncoder<Return(Args...)> MakeArgumentEncoder(
+ Return (Class::*)(Args...) const, MessageWriter* writer) {
+ return ArgumentEncoder<Return(Args...)>(writer);
+}
+
+// Utility to build an ArgumentEncoder from a function type and a message
+// writer.
+template <typename Signature>
+inline ArgumentEncoder<Signature> MakeArgumentEncoder(MessageWriter* writer) {
+ return ArgumentEncoder<Signature>(writer);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Provides automatic deserialization of argument lists and return
+// values by analyzing the supplied function signature types.
+// Examples:
+// auto decoder = MakeArgumentDecoder<std::string(void)>(reader);
+// ErrorType error = decoder.DecodeReturn(&return_value);
+
+template <typename T>
+class ArgumentDecoder;
+
+// Specialization of ArgumentDecoder for void return types.
+template <typename... Args>
+class ArgumentDecoder<void(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+
+ explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {}
+
+ // Deserializes arguments into a tuple.
+ ArgsTupleType DecodeArguments(ErrorType* error) {
+ ArgsTupleType value;
+ *error = Deserialize(&value, reader_);
+ return value;
+ }
+
+ private:
+ MessageReader* reader_;
+};
+
+// Specialization of ArgumentDecoder for non-void return types.
+template <typename Return, typename... Args>
+class ArgumentDecoder<Return(Args...)> {
+ public:
+ // Simplified types with reference and cv removed.
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+ using ReturnType = typename std::decay<Return>::type;
+
+ explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {}
+
+ // Deserializes arguments into a tuple.
+ ArgsTupleType DecodeArguments(ErrorType* error) {
+ ArgsTupleType value;
+ *error = Deserialize(&value, reader_);
+ return value;
+ }
+
+ // Deserializes the return value.
+ ErrorType DecodeReturn(ReturnType* value) {
+ return Deserialize(value, reader_);
+ }
+
+ private:
+ MessageReader* reader_;
+};
+
+// Utility to build an ArgumentDecoder from a function pointer and a message
+// reader.
+template <typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (*)(Args...), MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a method pointer and a message
+// reader.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (Class::*)(Args...), MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a const method pointer and a
+// message reader.
+template <typename Class, typename Return, typename... Args>
+inline ArgumentDecoder<Return(Args...)> MakeArgumentDecoder(
+ Return (Class::*)(Args...) const, MessageReader* reader) {
+ return ArgumentDecoder<Return(Args...)>(reader);
+}
+
+// Utility to build an ArgumentDecoder from a function type and a message
+// reader.
+template <typename Signature>
+inline ArgumentDecoder<Signature> MakeArgumentDecoder(MessageReader* reader) {
+ return ArgumentDecoder<Signature>(reader);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h
new file mode 100644
index 0000000000..93d87f3cab
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/array_wrapper.h
@@ -0,0 +1,111 @@
+#ifndef ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
+#define ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for C array buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class serializes to the same
+// format as std::vector, and may be substituted for std::vector during
+// serialization and deserialization. This substitution makes handling of C
+// arrays more efficient by avoiding unnecessary copies when remote method
+// signatures specify std::vector arguments or return values.
+template <typename T>
+class ArrayWrapper {
+ public:
+ // Define types in the style of STL containers to support STL operators.
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+
+ ArrayWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ ArrayWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ ArrayWrapper(pointer buffer, size_type size)
+ : ArrayWrapper(buffer, size, size) {}
+
+ ArrayWrapper(const ArrayWrapper& other) { *this = other; }
+
+ ArrayWrapper(ArrayWrapper&& other) { *this = std::move(other); }
+
+ ArrayWrapper& operator=(const ArrayWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ ArrayWrapper& operator=(ArrayWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ // Moves the end marker to |size|, clamping the end marker to the max capacity
+ // of the underlying array. This method does not change the size of the
+ // underlying array.
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+template <typename T, typename SizeType = std::size_t>
+ArrayWrapper<T> WrapArray(T* buffer, SizeType size) {
+ return ArrayWrapper<T>(buffer, size);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ARRAY_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h
new file mode 100644
index 0000000000..aa86531061
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h
@@ -0,0 +1,177 @@
+#ifndef ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
+#define ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class supports serialization of
+// buffers as raw bytes.
+template <typename T>
+class BufferWrapper;
+
+template <typename T>
+class BufferWrapper<T*> {
+ public:
+ // Define types in the style of STL containers to support STL operators.
+ typedef T value_type;
+ typedef std::size_t size_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+
+ BufferWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ BufferWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ BufferWrapper(pointer buffer, size_type size)
+ : BufferWrapper(buffer, size, size) {}
+
+ BufferWrapper(const BufferWrapper& other) { *this = other; }
+
+ BufferWrapper(BufferWrapper&& other) { *this = std::move(other); }
+
+ BufferWrapper& operator=(const BufferWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ BufferWrapper& operator=(BufferWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+template <typename T, typename Allocator>
+class BufferWrapper<std::vector<T, Allocator>> {
+ public:
+ using BufferType = typename std::vector<T, Allocator>;
+ using value_type = typename BufferType::value_type;
+ using size_type = typename BufferType::size_type;
+ using reference = typename BufferType::reference;
+ using const_reference = typename BufferType::const_reference;
+ using pointer = typename BufferType::pointer;
+ using const_pointer = typename BufferType::const_pointer;
+ using iterator = typename BufferType::iterator;
+ using const_iterator = typename BufferType::const_iterator;
+
+ BufferWrapper() {}
+ BufferWrapper(const BufferType& buffer) : buffer_(buffer) {}
+ BufferWrapper(const BufferType& buffer, const Allocator& allocator)
+ : buffer_(buffer, allocator) {}
+ BufferWrapper(BufferType&& buffer) : buffer_(std::move(buffer)) {}
+ BufferWrapper(BufferType&& buffer, const Allocator& allocator)
+ : buffer_(std::move(buffer), allocator) {}
+ BufferWrapper(const BufferWrapper&) = default;
+ BufferWrapper(BufferWrapper&&) = default;
+ BufferWrapper& operator=(const BufferWrapper&) = default;
+ BufferWrapper& operator=(BufferWrapper&&) = default;
+
+ pointer data() { return buffer_.data(); }
+ const_pointer data() const { return buffer_.data(); }
+
+ iterator begin() { return buffer_.begin(); }
+ iterator end() { return buffer_.end(); }
+ const_iterator begin() const { return buffer_.begin(); }
+ const_iterator end() const { return buffer_.end(); }
+
+ size_type size() const { return buffer_.size(); }
+ size_type max_size() const { return buffer_.capacity(); }
+ size_type capacity() const { return buffer_.capacity(); }
+
+ void resize(size_type size) { buffer_.resize(size); }
+ void reserve(size_type size) { buffer_.reserve(size); }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ BufferType& buffer() { return buffer_; }
+ const BufferType& buffer() const { return buffer_; }
+
+ private:
+ BufferType buffer_;
+};
+
+template <typename T, typename SizeType = std::size_t>
+BufferWrapper<T*> WrapBuffer(T* buffer, SizeType size) {
+ return BufferWrapper<T*>(buffer, size);
+}
+
+template <typename SizeType = std::size_t>
+BufferWrapper<std::uint8_t*> WrapBuffer(void* buffer, SizeType size) {
+ return BufferWrapper<std::uint8_t*>(static_cast<std::uint8_t*>(buffer), size);
+}
+
+template <typename SizeType = std::size_t>
+BufferWrapper<const std::uint8_t*> WrapBuffer(const void* buffer,
+ SizeType size) {
+ return BufferWrapper<const std::uint8_t*>(
+ static_cast<const std::uint8_t*>(buffer), size);
+}
+
+template <typename T, typename Allocator = std::allocator<T>>
+BufferWrapper<std::vector<T, Allocator>> WrapBuffer(
+ std::vector<T, Allocator>&& buffer) {
+ return BufferWrapper<std::vector<T, Allocator>>(
+ std::forward<std::vector<T, Allocator>>(buffer));
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_BUFFER_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h
new file mode 100644
index 0000000000..5ce34f8f9c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
+#define ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
+
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Copies const, void, and reference qualifiers from type T to type U, such that
+// the new type U' carries the same cv-reference qualifiers as T, with the same
+// underlying type as U.
+template <typename T, typename U>
+class CopyCVReference {
+ private:
+ using R = typename std::remove_reference<T>::type;
+ using U1 =
+ typename std::conditional<std::is_const<R>::value,
+ typename std::add_const<U>::type, U>::type;
+ using U2 =
+ typename std::conditional<std::is_volatile<R>::value,
+ typename std::add_volatile<U1>::type, U1>::type;
+ using U3 =
+ typename std::conditional<std::is_lvalue_reference<T>::value,
+ typename std::add_lvalue_reference<U2>::type,
+ U2>::type;
+ using U4 =
+ typename std::conditional<std::is_rvalue_reference<T>::value,
+ typename std::add_rvalue_reference<U3>::type,
+ U3>::type;
+
+ public:
+ using Type = U4;
+};
+
+template <typename T, typename U>
+using CopyCVReferenceType = typename CopyCVReference<T, U>::Type;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h
new file mode 100644
index 0000000000..b6e298084e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h
@@ -0,0 +1,47 @@
+#ifndef ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
+#define ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
+
+#include <memory>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Allocator adaptor that interposes construct() calls to convert value
+// initialization into default initialization. All standard containers
+// value-initialize their elements when constructed with a single size_type
+// argument or when grown by a call to resize. This allocator avoids potentially
+// costly value-initialization in these situations for value types that are
+// default constructible. As a consequence, elements of non-class types are left
+// uninitialized; this is desirable when using std::vector as a resizable
+// buffer, for example.
+template <typename T, typename Allocator = std::allocator<T>>
+class DefaultInitializationAllocator : public Allocator {
+ typedef std::allocator_traits<Allocator> AllocatorTraits;
+
+ public:
+ template <typename U>
+ struct rebind {
+ using other = DefaultInitializationAllocator<
+ U, typename AllocatorTraits::template rebind_alloc<U>>;
+ };
+
+ using Allocator::Allocator;
+
+ template <typename U>
+ void construct(U* pointer) noexcept(
+ std::is_nothrow_default_constructible<U>::value) {
+ ::new (static_cast<void*>(pointer)) U;
+ }
+ template <typename U, typename... Args>
+ void construct(U* pointer, Args&&... args) {
+ AllocatorTraits::construct(static_cast<Allocator&>(*this), pointer,
+ std::forward<Args>(args)...);
+ }
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/encoding.h b/libs/vr/libpdx/private/pdx/rpc/encoding.h
new file mode 100644
index 0000000000..f51d807f5b
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/encoding.h
@@ -0,0 +1,616 @@
+#ifndef ANDROID_PDX_RPC_ENCODING_H_
+#define ANDROID_PDX_RPC_ENCODING_H_
+
+#include <array>
+#include <cstdint>
+#include <cstring>
+#include <map>
+#include <numeric>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+#include "array_wrapper.h"
+#include "buffer_wrapper.h"
+#include "string_wrapper.h"
+#include "variant.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// This library uses a subset, or profile, of MessagePack (http://msgpack.org)
+// to encode supported data types during serialization and to verify the
+// expected data types during deserialization. One notable deviation from the
+// MessagePack specification is that little-endian byte order is used for
+// multi-byte numeric types to avoid unnecessary conversion on nearly all
+// relevant architectures.
+//
+// Some data types, integers for example, support multiple encoding strategies.
+// This library attempts to optimize for space based on the value of such types.
+// However, during decode all valid encodings for a given type are accepted.
+
+// Prefix byte for type encodings. This is the complete list of prefix bytes
+// from the MessagePack specification, even though not all types are used by
+// this library.
+enum EncodingPrefix {
+ ENCODING_TYPE_POSITIVE_FIXINT = 0x00,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN = 0x00,
+ ENCODING_TYPE_POSITIVE_FIXINT_MAX = 0x7f,
+ ENCODING_TYPE_POSITIVE_FIXINT_MASK = 0x7f,
+ ENCODING_TYPE_FIXMAP = 0x80,
+ ENCODING_TYPE_FIXMAP_MIN = 0x80,
+ ENCODING_TYPE_FIXMAP_MAX = 0x8f,
+ ENCODING_TYPE_FIXMAP_MASK = 0x0f,
+ ENCODING_TYPE_FIXARRAY = 0x90,
+ ENCODING_TYPE_FIXARRAY_MIN = 0x90,
+ ENCODING_TYPE_FIXARRAY_MAX = 0x9f,
+ ENCODING_TYPE_FIXARRAY_MASK = 0x0f,
+ ENCODING_TYPE_FIXSTR = 0xa0,
+ ENCODING_TYPE_FIXSTR_MIN = 0xa0,
+ ENCODING_TYPE_FIXSTR_MAX = 0xbf,
+ ENCODING_TYPE_FIXSTR_MASK = 0x1f,
+ ENCODING_TYPE_NIL = 0xc0,
+ ENCODING_TYPE_RESERVED = 0xc1,
+ ENCODING_TYPE_FALSE = 0xc2,
+ ENCODING_TYPE_TRUE = 0xc3,
+ ENCODING_TYPE_BIN8 = 0xc4,
+ ENCODING_TYPE_BIN16 = 0xc5,
+ ENCODING_TYPE_BIN32 = 0xc6,
+ ENCODING_TYPE_EXT8 = 0xc7,
+ ENCODING_TYPE_EXT16 = 0xc8,
+ ENCODING_TYPE_EXT32 = 0xc9,
+ ENCODING_TYPE_FLOAT32 = 0xca,
+ ENCODING_TYPE_FLOAT64 = 0xcb,
+ ENCODING_TYPE_UINT8 = 0xcc,
+ ENCODING_TYPE_UINT16 = 0xcd,
+ ENCODING_TYPE_UINT32 = 0xce,
+ ENCODING_TYPE_UINT64 = 0xcf,
+ ENCODING_TYPE_INT8 = 0xd0,
+ ENCODING_TYPE_INT16 = 0xd1,
+ ENCODING_TYPE_INT32 = 0xd2,
+ ENCODING_TYPE_INT64 = 0xd3,
+ ENCODING_TYPE_FIXEXT1 = 0xd4,
+ ENCODING_TYPE_FIXEXT2 = 0xd5,
+ ENCODING_TYPE_FIXEXT4 = 0xd6,
+ ENCODING_TYPE_FIXEXT8 = 0xd7,
+ ENCODING_TYPE_FIXEXT16 = 0xd8,
+ ENCODING_TYPE_STR8 = 0xd9,
+ ENCODING_TYPE_STR16 = 0xda,
+ ENCODING_TYPE_STR32 = 0xdb,
+ ENCODING_TYPE_ARRAY16 = 0xdc,
+ ENCODING_TYPE_ARRAY32 = 0xdd,
+ ENCODING_TYPE_MAP16 = 0xde,
+ ENCODING_TYPE_MAP32 = 0xdf,
+ ENCODING_TYPE_NEGATIVE_FIXINT = 0xe0,
+ ENCODING_TYPE_NEGATIVE_FIXINT_MIN = 0xe0,
+ ENCODING_TYPE_NEGATIVE_FIXINT_MAX = 0xff,
+};
+
+// Base encoding classes grouping multi-strategy encodings.
+enum EncodingClass {
+ ENCODING_CLASS_BOOL,
+ ENCODING_CLASS_NIL,
+ ENCODING_CLASS_INT,
+ ENCODING_CLASS_UINT,
+ ENCODING_CLASS_FLOAT,
+ ENCODING_CLASS_ARRAY,
+ ENCODING_CLASS_MAP,
+ ENCODING_CLASS_STRING,
+ ENCODING_CLASS_BINARY,
+ ENCODING_CLASS_EXTENSION,
+};
+
+// Encoding prefixes are unsigned bytes.
+typedef std::uint8_t EncodingType;
+
+// Extension encoding types defined by this library.
+enum EncodingExtType : int8_t {
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR,
+ ENCODING_EXT_TYPE_CHANNEL_HANDLE,
+};
+
+// Encoding predicates. Determines whether the given encoding is of a specific
+// type.
+inline constexpr bool IsFixintEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUnsignedFixintEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt8Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt8Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt16Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt16Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_INT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_UINT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsInt64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_INT32:
+ case ENCODING_TYPE_INT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsUInt64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_UINT32:
+ case ENCODING_TYPE_UINT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixmapEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixarrayEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixstrEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFixextEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXEXT1:
+ case ENCODING_TYPE_FIXEXT2:
+ case ENCODING_TYPE_FIXEXT4:
+ case ENCODING_TYPE_FIXEXT8:
+ case ENCODING_TYPE_FIXEXT16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFloat32Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FLOAT32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsFloat64Encoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FLOAT32:
+ case ENCODING_TYPE_FLOAT64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr bool IsBoolEncoding(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FALSE:
+ case ENCODING_TYPE_TRUE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline constexpr std::size_t GetFixstrSize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXSTR_MASK;
+}
+
+inline constexpr std::size_t GetFixarraySize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXARRAY_MASK;
+}
+
+inline constexpr std::size_t GetFixmapSize(EncodingType encoding) {
+ return encoding & ENCODING_TYPE_FIXMAP_MASK;
+}
+
+inline constexpr std::size_t GetFixextSize(EncodingType encoding) {
+ switch (encoding) {
+ case ENCODING_TYPE_FIXEXT1:
+ return 1;
+ case ENCODING_TYPE_FIXEXT2:
+ return 2;
+ case ENCODING_TYPE_FIXEXT4:
+ return 4;
+ case ENCODING_TYPE_FIXEXT8:
+ return 8;
+ case ENCODING_TYPE_FIXEXT16:
+ return 16;
+ default:
+ return 0; // Invalid fixext size.
+ }
+}
+
+// Gets the size of the encoding in bytes, not including external payload data.
+inline constexpr std::size_t GetEncodingSize(EncodingType encoding) {
+ switch (encoding) {
+ // Encoding is fully contained within the type value.
+ case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX:
+ case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX:
+ case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX:
+ case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX:
+ case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX:
+ case ENCODING_TYPE_NIL:
+ case ENCODING_TYPE_RESERVED:
+ case ENCODING_TYPE_FALSE:
+ case ENCODING_TYPE_TRUE:
+ return 1;
+
+ // Encoding type followed by one-byte size or immediate value.
+ case ENCODING_TYPE_BIN8:
+ case ENCODING_TYPE_EXT8:
+ case ENCODING_TYPE_UINT8:
+ case ENCODING_TYPE_INT8:
+ case ENCODING_TYPE_STR8:
+ // Encoding type followed by one-byte extension type.
+ case ENCODING_TYPE_FIXEXT1:
+ case ENCODING_TYPE_FIXEXT2:
+ case ENCODING_TYPE_FIXEXT4:
+ case ENCODING_TYPE_FIXEXT8:
+ case ENCODING_TYPE_FIXEXT16:
+ return 2;
+
+ // Encoding type followed by two-byte size or immediate value.
+ case ENCODING_TYPE_BIN16:
+ case ENCODING_TYPE_EXT16:
+ case ENCODING_TYPE_UINT16:
+ case ENCODING_TYPE_INT16:
+ case ENCODING_TYPE_STR16:
+ case ENCODING_TYPE_ARRAY16:
+ case ENCODING_TYPE_MAP16:
+ return 3;
+
+ // Encoding type followed by four-byte size or immediate value.
+ case ENCODING_TYPE_BIN32:
+ case ENCODING_TYPE_EXT32:
+ case ENCODING_TYPE_FLOAT32:
+ case ENCODING_TYPE_UINT32:
+ case ENCODING_TYPE_INT32:
+ case ENCODING_TYPE_STR32:
+ case ENCODING_TYPE_ARRAY32:
+ case ENCODING_TYPE_MAP32:
+ return 5;
+
+ // Encoding type followed by eight-byte immediate value.
+ case ENCODING_TYPE_FLOAT64:
+ case ENCODING_TYPE_UINT64:
+ case ENCODING_TYPE_INT64:
+ return 9;
+
+ default:
+ return 0;
+ }
+}
+
+// Encoding for standard types. Each supported data type has an associated
+// encoding or set of encodings. These functions determine the MessagePack
+// encoding based on the data type, value, and size of their arguments.
+
+inline constexpr EncodingType EncodeArrayType(std::size_t size) {
+ if (size < (1U << 4))
+ return ENCODING_TYPE_FIXARRAY | (size & ENCODING_TYPE_FIXARRAY_MASK);
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_ARRAY16;
+ else
+ return ENCODING_TYPE_ARRAY32;
+}
+
+inline constexpr EncodingType EncodeMapType(std::size_t size) {
+ if (size < (1U << 4))
+ return ENCODING_TYPE_FIXMAP | (size & ENCODING_TYPE_FIXMAP_MASK);
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_MAP16;
+ else
+ return ENCODING_TYPE_MAP32;
+}
+
+inline constexpr EncodingType EncodeStringType(std::size_t size) {
+ if (size < (1U << 5))
+ return ENCODING_TYPE_FIXSTR | (size & ENCODING_TYPE_FIXSTR_MASK);
+ else if (size < (1U << 8))
+ return ENCODING_TYPE_STR8;
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_STR16;
+ else
+ return ENCODING_TYPE_STR32;
+}
+
+inline constexpr EncodingType EncodeBinType(std::size_t size) {
+ if (size < (1U << 8))
+ return ENCODING_TYPE_BIN8;
+ else if (size < (1U << 16))
+ return ENCODING_TYPE_BIN16;
+ else
+ return ENCODING_TYPE_BIN32;
+}
+
+inline EncodingType EncodeType(const EmptyVariant& /*empty*/) {
+ return ENCODING_TYPE_NIL;
+}
+
+// Variant is encoded as a single-element map, with the type index as the key.
+template <typename... Types>
+inline EncodingType EncodeType(const Variant<Types...>& /*variant*/) {
+ return EncodeMapType(1);
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const StringWrapper<T>& value) {
+ return EncodeStringType(value.length());
+}
+
+inline constexpr EncodingType EncodeType(const std::string& value) {
+ return EncodeStringType(value.length());
+}
+
+template <typename T, std::size_t Size>
+inline constexpr EncodingType EncodeType(const std::array<T, Size>& /*value*/) {
+ return EncodeArrayType(Size);
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const ArrayWrapper<T>& value) {
+ return EncodeArrayType(value.size());
+}
+
+template <typename T, typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::vector<T, Allocator>& value) {
+ return EncodeArrayType(value.size());
+}
+
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::map<Key, T, Compare, Allocator>& value) {
+ return EncodeMapType(value.size());
+}
+
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline constexpr EncodingType EncodeType(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value) {
+ return EncodeMapType(value.size());
+}
+
+template <typename T>
+inline constexpr EncodingType EncodeType(const BufferWrapper<T>& value) {
+ // BIN size is in bytes.
+ return EncodeBinType(value.size() *
+ sizeof(typename BufferWrapper<T>::value_type));
+}
+
+template <typename T, typename U>
+inline constexpr EncodingType EncodeType(const std::pair<T, U>& /*value*/) {
+ return EncodeArrayType(2);
+}
+
+template <typename... T>
+inline constexpr EncodingType EncodeType(const std::tuple<T...>& /*value*/) {
+ return EncodeArrayType(sizeof...(T));
+}
+
+// FileHandle is encoded as a FIXEXT2 with a type code for "FileDescriptor"
+// and a signed 16-bit index into the pushed fd array. Empty file descriptor
+// have an array index of -1.
+template <FileHandleMode Mode>
+inline constexpr EncodingType EncodeType(const FileHandle<Mode>& /*fd*/) {
+ return ENCODING_TYPE_FIXEXT2;
+}
+
+// ChannelHandle is encoded as a FIXEXT4 with a type of
+// ENCODING_EXT_TYPE_CHANNEL_HANDLE and a signed 32-bit value representing
+// a client channel in a remote process. Empty handle has a value of -1.
+template <ChannelHandleMode Mode>
+inline constexpr EncodingType EncodeType(
+ const ChannelHandle<Mode>& /*handle*/) {
+ return ENCODING_TYPE_FIXEXT4;
+}
+
+inline constexpr EncodingType EncodeType(const bool& value) {
+ return value ? ENCODING_TYPE_TRUE : ENCODING_TYPE_FALSE;
+}
+
+// Type 'char' is a little bit special in that it is distinct from 'signed char'
+// and 'unsigned char'. Treating it as an unsigned 8-bit value is safe for
+// encoding purposes and nicely handles 7-bit ASCII encodings as FIXINT.
+inline constexpr EncodingType EncodeType(const char& value) {
+ if (value < static_cast<char>(1 << 7))
+ return value;
+ else
+ return ENCODING_TYPE_UINT8;
+}
+
+inline constexpr EncodingType EncodeType(const uint8_t& value) {
+ if (value < (1U << 7))
+ return value;
+ else
+ return ENCODING_TYPE_UINT8;
+}
+inline constexpr EncodingType EncodeType(const int8_t& value) {
+ if (value >= -32)
+ return value;
+ else
+ return ENCODING_TYPE_INT8;
+}
+inline constexpr EncodingType EncodeType(const uint16_t& value) {
+ if (value < (1U << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1U << 8))
+ return ENCODING_TYPE_UINT8;
+ else
+ return ENCODING_TYPE_UINT16;
+}
+inline constexpr EncodingType EncodeType(const int16_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127)
+ return ENCODING_TYPE_INT8;
+ else
+ return ENCODING_TYPE_INT16;
+}
+inline constexpr EncodingType EncodeType(const uint32_t& value) {
+ if (value < (1U << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1U << 8))
+ return ENCODING_TYPE_UINT8;
+ else if (value < (1U << 16))
+ return ENCODING_TYPE_UINT16;
+ else
+ return ENCODING_TYPE_UINT32;
+}
+inline constexpr EncodingType EncodeType(const int32_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127)
+ return ENCODING_TYPE_INT8;
+ else if (value >= -32768 && value <= 32767)
+ return ENCODING_TYPE_INT16;
+ else
+ return ENCODING_TYPE_INT32;
+}
+inline constexpr EncodingType EncodeType(const uint64_t& value) {
+ if (value < (1ULL << 7))
+ return static_cast<EncodingType>(value);
+ else if (value < (1ULL << 8))
+ return ENCODING_TYPE_UINT8;
+ else if (value < (1ULL << 16))
+ return ENCODING_TYPE_UINT16;
+ else if (value < (1ULL << 32))
+ return ENCODING_TYPE_UINT32;
+ else
+ return ENCODING_TYPE_UINT64;
+}
+inline constexpr EncodingType EncodeType(const int64_t& value) {
+ if (value >= -32 && value <= 127)
+ return static_cast<EncodingType>(value);
+ else if (value >= -128 && value <= 127) // Effectively [-128, -32).
+ return ENCODING_TYPE_INT8;
+ else if (value >= -32768 && value <= 32767)
+ return ENCODING_TYPE_INT16;
+ else if (value >= -2147483648 && value <= 2147483647)
+ return ENCODING_TYPE_INT32;
+ else
+ return ENCODING_TYPE_INT64;
+}
+
+inline constexpr EncodingType EncodeType(const float& /*value*/) {
+ return ENCODING_TYPE_FLOAT32;
+}
+
+inline constexpr EncodingType EncodeType(const double& /*value*/) {
+ return ENCODING_TYPE_FLOAT64;
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ENCODING_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/enumeration.h b/libs/vr/libpdx/private/pdx/rpc/enumeration.h
new file mode 100644
index 0000000000..7a35d31b22
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/enumeration.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_PDX_RPC_ENUMERATION_H_
+#define ANDROID_PDX_RPC_ENUMERATION_H_
+
+#include <pdx/rpc/sequence.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility for manipulating lists of types. Provides operations to lookup an
+// element by type or index.
+
+namespace detail {
+
+// Helper type that captures type and index for each element of a type
+// enumeration.
+template <std::size_t I, typename T>
+struct IndexedElement {
+ using Type = T;
+ static constexpr std::size_t Index = I;
+};
+
+// Helper type that captures an IndexSequence and corresponding list of types.
+template <typename Is, typename... Ts>
+struct ElementIndexer;
+
+// Partial specialization that generates an instantiation of IndexElement<I, T>
+// for each element of a type enumeration using inheritance. Once a type
+// enumeration is instantiated this way the compiler is able to deduce either I
+// or T from the other using the method below.
+template <std::size_t... Is, typename... Ts>
+struct ElementIndexer<IndexSequence<Is...>, Ts...> : IndexedElement<Is, Ts>... {
+};
+
+// Helper function that causes the compiler to deduce an IndexedElement<I, T>
+// given T.
+template <typename T, std::size_t I>
+static IndexedElement<I, T> SelectElementByType(IndexedElement<I, T>);
+
+// Helper function that causes the compiler to deduce an IndexedElement<I, T>
+// given I.
+template <std::size_t I, typename T>
+static IndexedElement<I, T> SelectElementByIndex(IndexedElement<I, T>);
+
+} // namespace detail
+
+// Deduces the IndexedElement<I, T> given T and a type sequence Ts. This may be
+// used to determine the index of T within Ts at compile time.
+template <typename T, typename... Ts>
+using ElementForType = decltype(detail::SelectElementByType<T>(
+ detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{}));
+
+// Deduces the IndexedElement<I, T> given I and a type sequence Ts. This may be
+// used to determine the type of the element at index I within Ts at compile
+// time. Tuple operations may also be used to accomplish the same task, however
+// this implementation is provided here for symmetry.
+template <std::size_t I, typename... Ts>
+using ElementForIndex = decltype(detail::SelectElementByIndex<I>(
+ detail::ElementIndexer<typename IndexSequenceFor<Ts...>::type, Ts...>{}));
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_ENUMERATION_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/find_replace.h b/libs/vr/libpdx/private/pdx/rpc/find_replace.h
new file mode 100644
index 0000000000..b4b086bd05
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/find_replace.h
@@ -0,0 +1,45 @@
+#ifndef ANDROID_PDX_RPC_FIND_REPLACE_H_
+#define ANDROID_PDX_RPC_FIND_REPLACE_H_
+
+#include <type_traits>
+
+#include <pdx/rpc/copy_cv_reference.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class to capture types to find and replace.
+template <typename Find, typename Replace>
+struct FindReplace;
+
+template <typename T, typename U>
+using IsSameBaseType = typename std::is_same<typename std::decay<T>::type,
+ typename std::decay<U>::type>;
+
+// Replaces the type Subject with type Replace if type Subject is the same type
+// as type Find, excluding cv-reference qualifiers in the match.
+template <typename Find, typename Replace, typename Subject>
+using ReplaceType =
+ typename std::conditional<IsSameBaseType<Find, Subject>::value,
+ CopyCVReferenceType<Subject, Replace>,
+ Subject>::type;
+
+// Determines whether the type Find (excluding cv-reference qualifiers) is in
+// the given parameter pack.
+template <typename Find, typename... Types>
+struct ContainsType : std::true_type {};
+
+template <typename Find, typename First, typename... Rest>
+struct ContainsType<Find, First, Rest...>
+ : std::conditional<IsSameBaseType<Find, First>::value, std::true_type,
+ ContainsType<Find, Rest...>>::type {};
+
+template <typename Find>
+struct ContainsType<Find> : std::false_type {};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_FIND_REPLACE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/function_traits.h b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
new file mode 100644
index 0000000000..7641b0a7c1
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
@@ -0,0 +1,61 @@
+#ifndef ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
+#define ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
+
+#include <type_traits>
+
+#include <pdx/rpc/type_operators.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility type to capture return and argument types of a function signature.
+// Examples:
+// typedef SignatureType<int(int)> SignatureType;
+// using SignatureType = SignatureType<int(int)>;
+template <typename T>
+using SignatureType = T;
+
+// Utility class to extract return and argument types from function types.
+// Provides nested types for return value, arguments, and full signature. Also
+// provides accessor types for individual arguments, argument-arity, and type
+// substitution.
+template <typename T>
+struct FunctionTraits;
+
+template <typename Return_, typename... Args_>
+struct FunctionTraits<Return_(Args_...)> {
+ using Return = Return_;
+ using Args = std::tuple<Args_...>;
+ using Signature = SignatureType<Return_(Args_...)>;
+
+ enum : std::size_t { Arity = sizeof...(Args_) };
+
+ template <std::size_t Index>
+ using Arg = typename std::tuple_element<Index, Args>::type;
+
+ template <typename... Params>
+ using RewriteArgs =
+ SignatureType<Return_(ConditionalRewrite<Args_, Params>...)>;
+
+ template <typename ReturnType, typename... Params>
+ using RewriteSignature =
+ SignatureType<ConditionalRewrite<Return_, ReturnType>(
+ ConditionalRewrite<Args_, Params>...)>;
+
+ template <template <typename> class Wrapper, typename ReturnType,
+ typename... Params>
+ using RewriteSignatureWrapReturn =
+ SignatureType<Wrapper<ConditionalRewrite<Return_, ReturnType>>(
+ ConditionalRewrite<Args_, Params>...)>;
+
+ template <typename ReturnType>
+ using RewriteReturn =
+ SignatureType<ConditionalRewrite<Return_, ReturnType>(Args_...)>;
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_FUNCTION_TRAITS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h
new file mode 100644
index 0000000000..aeae9d3e5e
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/macros.h
@@ -0,0 +1,148 @@
+#ifndef ANDROID_PDX_RPC_MACROS_H_
+#define ANDROID_PDX_RPC_MACROS_H_
+
+// Macros to apply other macros over all elements in a list.
+//
+// For example, for a macro A(x) and B(x, y):
+// - FOR_EACH(A, 1, 2, 3) -> A(1) A(2) A(3).
+// - FOR_EACH_BINARY(B, z, 1, 2, 3) -> B(z, 1) B(z, 2) B(z, 3)
+// - FOR_EACH_LIST(A, 1, 2, 3) -> A(1), B(2), C(3)
+// - FOR_EACH_BINARY_LIST(B, z, 1, 2, 3) -> B(z, 1), B(z, 2), B(z, 3)
+//
+// Empty lists are supported and will produce no output.
+
+// Recursive expansion macros.
+#define _PDX_EXPAND0(...) __VA_ARGS__
+#define _PDX_EXPAND1(...) _PDX_EXPAND0(_PDX_EXPAND0(_PDX_EXPAND0(__VA_ARGS__)))
+#define _PDX_EXPAND2(...) _PDX_EXPAND1(_PDX_EXPAND1(_PDX_EXPAND1(__VA_ARGS__)))
+#define _PDX_EXPAND3(...) _PDX_EXPAND2(_PDX_EXPAND2(_PDX_EXPAND2(__VA_ARGS__)))
+#define _PDX_EXPAND4(...) _PDX_EXPAND3(_PDX_EXPAND3(_PDX_EXPAND3(__VA_ARGS__)))
+#define _PDX_EXPAND(...) _PDX_EXPAND4(_PDX_EXPAND4(_PDX_EXPAND4(__VA_ARGS__)))
+
+// Required to workaround a bug in the VC++ preprocessor.
+#define _PDX_INDIRECT_EXPAND(macro, args) macro args
+
+// Defines a step separation for macro expansion.
+#define _PDX_SEPARATOR
+
+// Clears any remaining contents wrapped in parentheses.
+#define _PDX_CLEAR(...)
+
+// Introduces a first dummy argument and _PDX_CLEAR as second argument.
+#define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR
+
+// Returns the first argument of a list.
+#define _PDX_FIRST_ARG(first, ...) first
+
+// Returns the second argument of a list.
+#define _PDX_SECOND_ARG(_, second, ...) second
+
+// Expands the arguments and introduces a separator.
+#define _PDX_EXPAND_NEXT_FUNC(_, next_func, ...) \
+ _PDX_INDIRECT_EXPAND(_PDX_SECOND_ARG, (_, next_func)) \
+ _PDX_SEPARATOR
+
+// Returns next_func if the next element is not (), or _PDX_CLEAR
+// otherwise.
+//
+// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is ().
+#define _PDX_NEXT_FUNC(next_element, next_func) \
+ _PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func)
+
+// Macros for the unary version of PDX_FOR_EACH.
+
+// Applies the unary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_1(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_2)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_2(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_1)(macro, next, __VA_ARGS__)
+
+// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_1
+// otherwise.
+#define _PDX_HANDLE_EMPTY_ARGS(macro, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_1) \
+ (macro, __VA_ARGS__, ())
+
+// Applies a unary macro over all the elements in a list.
+#define PDX_FOR_EACH(macro, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS(macro, __VA_ARGS__))
+
+// Applies the unary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_LIST_1(macro, head, next, ...) \
+ , macro(head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_2)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_LIST_2(macro, head, next, ...) \
+ , macro(head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__)
+
+// Applies the unary macro at the start of a list.
+#define _PDX_APPLY_LIST_0(macro, head, next, ...) \
+ macro(head) _PDX_NEXT_FUNC(next, _PDX_APPLY_LIST_1)(macro, next, __VA_ARGS__)
+
+// Stops expansion if __VA_ARGS__ is empty, calling _PDX_APPLY_LIST_0
+// otherwise.
+#define _PDX_HANDLE_EMPTY_LIST(macro, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_LIST_0) \
+ (macro, __VA_ARGS__, ())
+
+// Applies a unary macro over all the elements in a list.
+#define PDX_FOR_EACH_LIST(macro, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST(macro, __VA_ARGS__))
+
+// Macros for the binary version of PDX_FOR_EACH.
+
+// Applies the binary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_BINARY_1(macro, arg, head, next, ...) \
+ macro(arg, head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_2)(macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro. Duplicated for macro recursive expansion.
+#define _PDX_APPLY_BINARY_2(macro, arg, head, next, ...) \
+ macro(arg, head) \
+ _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_1)(macro, arg, next, __VA_ARGS__)
+
+// Version of _PDX_HANDLE_EMPTY_ARGS that takes 1 fixed argument for a
+// binary macro.
+#define _PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_1) \
+ (macro, arg, __VA_ARGS__, ())
+
+// Applies a binary macro over all the elements in a list and a given argument.
+#define PDX_FOR_EACH_BINARY(macro, arg, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_ARGS_BINARY(macro, arg, __VA_ARGS__))
+
+// Applies the binary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_BINARY_LIST_1(macro, arg, head, next, ...) \
+ , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_2)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro at the end of a list. Duplicated for macro recursive
+// expansion.
+#define _PDX_APPLY_BINARY_LIST_2(macro, arg, head, next, ...) \
+ , macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Applies the binary macro at the start of a list. Duplicated for macro
+// recursive expansion.
+#define _PDX_APPLY_BINARY_LIST_0(macro, arg, head, next, ...) \
+ macro(arg, head) _PDX_NEXT_FUNC(next, _PDX_APPLY_BINARY_LIST_1)( \
+ macro, arg, next, __VA_ARGS__)
+
+// Version of _PDX_HANDLE_EMPTY_LIST that takes 1 fixed argument for a
+// binary macro.
+#define _PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, ...) \
+ _PDX_NEXT_FUNC(_PDX_FIRST_ARG(__VA_ARGS__()), _PDX_APPLY_BINARY_LIST_0) \
+ (macro, arg, __VA_ARGS__, ())
+
+// Applies a binary macro over all the elements in a list and a given argument.
+#define PDX_FOR_EACH_BINARY_LIST(macro, arg, ...) \
+ _PDX_EXPAND(_PDX_HANDLE_EMPTY_LIST_BINARY(macro, arg, __VA_ARGS__))
+
+#endif // ANDROID_PDX_RPC_MACROS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/message_buffer.h b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h
new file mode 100644
index 0000000000..ba4e86e0de
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/message_buffer.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
+#define ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
+
+#include <pdx/rpc/thread_local_buffer.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility type for thread-local buffers, providing suitable defaults for most
+// situations. Independent thread-local buffers may be created by using
+// different types for Slot -- ThreadLocalSlot, ThreadLocalTypedSlot and
+// ThreadLocalIndexedSlot provide utilities for building these types.
+template <typename Slot, std::size_t Capacity = 4096, typename T = std::uint8_t,
+ typename Allocator = DefaultInitializationAllocator<T>>
+using MessageBuffer = ThreadLocalBuffer<T, Allocator, Capacity, Slot>;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_MESSAGE_BUFFER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/payload.h b/libs/vr/libpdx/private/pdx/rpc/payload.h
new file mode 100644
index 0000000000..a48a64ce15
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/payload.h
@@ -0,0 +1,157 @@
+#ifndef ANDROID_PDX_RPC_PAYLOAD_H_
+#define ANDROID_PDX_RPC_PAYLOAD_H_
+
+#include <iterator>
+
+#include <pdx/client.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/service.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Implements the payload interface, required by Serialize/Deserialize, on top
+// of a thread-local MessageBuffer.
+template <typename Slot>
+class MessagePayload {
+ public:
+ using BufferType = typename MessageBuffer<Slot>::BufferType;
+ using ValueType = typename MessageBuffer<Slot>::ValueType;
+
+ // Constructs a MessagePayload with an empty TLS buffer.
+ MessagePayload()
+ : buffer_(MessageBuffer<Slot>::GetEmptyBuffer()),
+ cursor_(buffer_.begin()),
+ const_cursor_(buffer_.cbegin()) {}
+
+ // Returns a reference to the cursor iterator to be used during serialization
+ // into the underlying MessageBuffer.
+ typename BufferType::iterator& Cursor() { return cursor_; }
+
+ // Returns a reference to the const cursor iterator at the beginning of the
+ // underlying MessageBuffer.
+ typename BufferType::const_iterator& ConstCursor() { return const_cursor_; }
+
+ // Returns a const iterator marking the end of the underlying MessageBuffer.
+ typename BufferType::const_iterator ConstEnd() { return buffer_.cend(); }
+
+ // Resizes the underlying MessageBuffer and sets the cursor to the beginning.
+ void Resize(std::size_t size) {
+ buffer_.resize(size);
+ cursor_ = buffer_.begin();
+ const_cursor_ = buffer_.cbegin();
+ }
+
+ // Resets the read cursor so that data can be read from the buffer again.
+ void Rewind() { const_cursor_ = buffer_.cbegin(); }
+
+ // Adds |size| bytes to the size of the underlying MessageBuffer and positions
+ // the cursor at the beginning of the extended region.
+ void Extend(std::size_t size) {
+ const std::size_t offset = buffer_.size();
+ buffer_.resize(offset + size);
+ cursor_ = buffer_.begin() + offset;
+ const_cursor_ = buffer_.cbegin() + offset;
+ }
+
+ // Clears the underlying MessageBuffer and sets the cursor to the beginning.
+ void Clear() {
+ buffer_.clear();
+ cursor_ = buffer_.begin();
+ const_cursor_ = buffer_.cbegin();
+ }
+
+ ValueType* Data() { return buffer_.data(); }
+ const ValueType* Data() const { return buffer_.data(); }
+ std::size_t Size() const { return buffer_.size(); }
+ std::size_t Capacity() const { return buffer_.capacity(); }
+
+ private:
+ BufferType& buffer_;
+ typename BufferType::iterator cursor_;
+ typename BufferType::const_iterator const_cursor_;
+
+ MessagePayload(const MessagePayload<Slot>&) = delete;
+ void operator=(const MessagePayload<Slot>&) = delete;
+};
+
+// Implements the payload interface for service-side RPCs. Handles translating
+// between remote and local handle spaces automatically.
+template <typename Slot>
+class ServicePayload : public MessagePayload<Slot>,
+ public MessageWriter,
+ public MessageReader {
+ public:
+ ServicePayload(Message& message) : message_(message) {}
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ this->Extend(size);
+ return &*this->Cursor();
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return &message_; }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*this->ConstCursor(), &*this->ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(this->ConstCursor(),
+ PointerDistance(new_start, &*this->ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override { return &message_; }
+
+ private:
+ Message& message_;
+};
+
+// Implements the payload interface for client-side RPCs. Handles gathering file
+// handles to be sent over IPC automatically.
+template <typename Slot>
+class ClientPayload : public MessagePayload<Slot>,
+ public MessageWriter,
+ public MessageReader {
+ public:
+ using ContainerType =
+ MessageBuffer<ThreadLocalTypeSlot<ClientPayload<Slot>>, 1024u, int>;
+ using BufferType = typename ContainerType::BufferType;
+
+ ClientPayload(Transaction& transaction) : transaction_{transaction} {}
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ this->Extend(size);
+ return &*this->Cursor();
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override {
+ return &transaction_;
+ }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {&*this->ConstCursor(), &*this->ConstEnd()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ std::advance(this->ConstCursor(),
+ PointerDistance(new_start, &*this->ConstCursor()));
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override {
+ return &transaction_;
+ }
+
+ private:
+ Transaction& transaction_;
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_PAYLOAD_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h
new file mode 100644
index 0000000000..d496719a58
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/pointer_wrapper.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_RPC_POINTER_WRAPPER_H_
+#define ANDROID_PDX_RPC_POINTER_WRAPPER_H_
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for pointers to any serializable type. This class is used by
+// serialization/deserialization to handle pointers to objects that are to be
+// serialized or deserialized.
+template <typename T>
+class PointerWrapper {
+ public:
+ using BaseType = T;
+
+ PointerWrapper(T* pointer) : pointer_(pointer) {}
+ PointerWrapper(const PointerWrapper&) = default;
+ PointerWrapper(PointerWrapper&&) = default;
+ PointerWrapper& operator=(const PointerWrapper&) = default;
+ PointerWrapper& operator=(PointerWrapper&&) = default;
+
+ T& Dereference() { return *pointer_; }
+ const T& Dereference() const { return *pointer_; }
+
+ private:
+ T* pointer_;
+};
+
+template <typename T>
+PointerWrapper<T> WrapPointer(T* pointer) {
+ return PointerWrapper<T>(pointer);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_POINTER_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method.h b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
new file mode 100644
index 0000000000..505c63b1bf
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
@@ -0,0 +1,473 @@
+#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_H_
+#define ANDROID_PDX_RPC_REMOTE_METHOD_H_
+
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/client.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/remote_method_type.h>
+#include <pdx/service.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+#ifdef __clang__
+// Stand-in type to avoid Clang compiler bug. Clang currently has a bug where
+// performing parameter pack expansion for arguments with empty packs causes a
+// compiler crash. Provide a substitute void type and specializations/overloads
+// of CheckArgumentTypes and DispatchRemoteMethod to work around this problem.
+struct Void {};
+
+// Evaluates to true if the method type is <any>(Void), false otherwise.
+template <typename RemoteMethodType>
+using IsVoidMethod = typename std::integral_constant<
+ bool, RemoteMethodType::Traits::Arity == 1 &&
+ std::is_same<typename RemoteMethodType::Traits::template Arg<0>,
+ Void>::value>;
+
+// Utility to determine if a method is of type <any>(Void).
+template <typename RemoteMethodType>
+using EnableIfVoidMethod =
+ typename std::enable_if<IsVoidMethod<RemoteMethodType>::value>::type;
+
+// Utility to determine if a method is not of type <any>(Void).
+template <typename RemoteMethodType>
+using EnableIfNotVoidMethod =
+ typename std::enable_if<!IsVoidMethod<RemoteMethodType>::value>::type;
+
+#else
+// GCC works fine with void argument types, always enable the regular
+// implementation of DispatchRemoteMethod.
+using Void = void;
+template <typename RemoteMethodType>
+using EnableIfVoidMethod = void;
+template <typename RemoteMethodType>
+using EnableIfNotVoidMethod = void;
+#endif
+
+// Helper type trait to specialize InvokeRemoteMethods for return types that
+// can be obtained directly from Transaction::Send<T>() without deserializing
+// reply payload.
+template <typename T>
+struct IsDirectReturn : std::false_type {};
+
+template <>
+struct IsDirectReturn<void> : std::true_type {};
+
+template <>
+struct IsDirectReturn<int> : std::true_type {};
+
+template <>
+struct IsDirectReturn<LocalHandle> : std::true_type {};
+
+template <>
+struct IsDirectReturn<LocalChannelHandle> : std::true_type {};
+
+template <typename Return, typename Type = void>
+using EnableIfDirectReturn =
+ typename std::enable_if<IsDirectReturn<Return>::value, Type>::type;
+
+template <typename Return, typename Type = void>
+using EnableIfNotDirectReturn =
+ typename std::enable_if<!IsDirectReturn<Return>::value, Type>::type;
+
+// Utility class to invoke a method with arguments packed in a tuple.
+template <typename Class, typename T>
+class UnpackArguments;
+
+// Utility class to invoke a method with arguments packed in a tuple.
+template <typename Class, typename Return, typename... Args>
+class UnpackArguments<Class, Return(Args...)> {
+ public:
+ using ArgsTupleType = std::tuple<typename std::decay<Args>::type...>;
+ using MethodType = Return (Class::*)(Message&, Args...);
+
+ UnpackArguments(Class& instance, MethodType method, Message& message,
+ ArgsTupleType& parameters)
+ : instance_(instance),
+ method_(method),
+ message_(message),
+ parameters_(parameters) {}
+
+ // Invokes method_ on intance_ with the packed arguments from parameters_.
+ Return Invoke() {
+ constexpr auto Arity = sizeof...(Args);
+ return static_cast<Return>(InvokeHelper(MakeIndexSequence<Arity>{}));
+ }
+
+ private:
+ Class& instance_;
+ MethodType method_;
+ Message& message_;
+ ArgsTupleType& parameters_;
+
+ template <std::size_t... Index>
+ Return InvokeHelper(IndexSequence<Index...>) {
+ return static_cast<Return>((instance_.*method_)(
+ message_,
+ std::get<Index>(std::forward<ArgsTupleType>(parameters_))...));
+ }
+
+ UnpackArguments(const UnpackArguments&) = delete;
+ void operator=(const UnpackArguments&) = delete;
+};
+
+// Returns an error code from a remote method to the client. May be called
+// either during dispatch of the remote method handler or at a later time if the
+// message is moved for delayed response.
+inline void RemoteMethodError(Message& message, int error_code) {
+ const auto status = message.ReplyError(error_code);
+ ALOGE_IF(!status, "RemoteMethodError: Failed to reply to message: %s",
+ status.GetErrorMessage().c_str());
+}
+
+// Returns a value from a remote method to the client. The usual method to
+// return a value during dispatch of a remote method is to simply use a return
+// statement in the handler. If the message is moved however, these methods may
+// be used to return a value at a later time, outside of initial dispatch.
+
+// Overload for direct return types.
+template <typename RemoteMethodType, typename Return>
+EnableIfDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn(
+ Message& message, const Return& return_value) {
+ const auto status = message.Reply(return_value);
+ ALOGE_IF(!status, "RemoteMethodReturn: Failed to reply to message: %s",
+ status.GetErrorMessage().c_str());
+}
+
+// Overload for non-direct return types.
+template <typename RemoteMethodType, typename Return>
+EnableIfNotDirectReturn<typename RemoteMethodType::Return> RemoteMethodReturn(
+ Message& message, const Return& return_value) {
+ using Signature = typename RemoteMethodType::template RewriteReturn<Return>;
+ rpc::ServicePayload<ReplyBuffer> payload(message);
+ MakeArgumentEncoder<Signature>(&payload).EncodeReturn(return_value);
+
+ auto ret = message.WriteAll(payload.Data(), payload.Size());
+ auto status = message.Reply(ret);
+ ALOGE_IF(!status, "RemoteMethodReturn: Failed to reply to message: %s",
+ status.GetErrorMessage().c_str());
+}
+
+// Overload for Status<void> return types.
+template <typename RemoteMethodType>
+void RemoteMethodReturn(Message& message, const Status<void>& return_value) {
+ if (return_value)
+ RemoteMethodReturn<RemoteMethodType>(message, 0);
+ else
+ RemoteMethodError(message, return_value.error());
+}
+
+// Overload for Status<T> return types. This overload forwards the underlying
+// value or error within the Status<T>.
+template <typename RemoteMethodType, typename Return>
+void RemoteMethodReturn(Message& message, const Status<Return>& return_value) {
+ if (return_value)
+ RemoteMethodReturn<RemoteMethodType, Return>(message, return_value.get());
+ else
+ RemoteMethodError(message, return_value.error());
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface check. Overload for void return types.
+template <typename RemoteMethodType, typename Class, typename... Args,
+ typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ void (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature = typename RemoteMethodType::template RewriteArgs<Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ Status<size_t> read_status = message.Read(payload.Data(), payload.Size());
+ if (!read_status) {
+ RemoteMethodError(message, read_status.error());
+ return;
+ }
+
+ payload.Resize(read_status.get());
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, 0);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface signature check. Overload for generic return types.
+template <typename RemoteMethodType, typename Class, typename Return,
+ typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ Return (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature =
+ typename RemoteMethodType::template RewriteSignature<Return, Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ Status<size_t> read_status = message.Read(payload.Data(), payload.Size());
+ if (!read_status) {
+ RemoteMethodError(message, read_status.error());
+ return;
+ }
+
+ payload.Resize(read_status.get());
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value =
+ UnpackArguments<Class, Signature>(instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Dispatches a method by deserializing arguments from the given Message, with
+// compile-time interface signature check. Overload for Status<T> return types.
+template <typename RemoteMethodType, typename Class, typename Return,
+ typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ Status<Return> (Class::*method)(Message&, Args...),
+ Message& message,
+ std::size_t max_capacity = InitialBufferCapacity) {
+ using Signature =
+ typename RemoteMethodType::template RewriteSignature<Return, Args...>;
+ using InvokeSignature =
+ typename RemoteMethodType::template RewriteSignatureWrapReturn<
+ Status, Return, Args...>;
+ rpc::ServicePayload<ReceiveBuffer> payload(message);
+ payload.Resize(max_capacity);
+
+ Status<size_t> read_status = message.Read(payload.Data(), payload.Size());
+ if (!read_status) {
+ RemoteMethodError(message, read_status.error());
+ return;
+ }
+
+ payload.Resize(read_status.get());
+
+ ErrorType error;
+ auto decoder = MakeArgumentDecoder<Signature>(&payload);
+ auto arguments = decoder.DecodeArguments(&error);
+ if (error) {
+ RemoteMethodError(message, EIO);
+ return;
+ }
+
+ auto return_value = UnpackArguments<Class, InvokeSignature>(
+ instance, method, message, arguments)
+ .Invoke();
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+#ifdef __clang__
+// Overloads to handle Void argument type without exploding clang.
+
+// Overload for void return type.
+template <typename RemoteMethodType, typename Class,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, void (Class::*method)(Message&),
+ Message& message) {
+ (instance.*method)(message);
+ // Return to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, 0);
+}
+
+// Overload for generic return type.
+template <typename RemoteMethodType, typename Class, typename Return,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, Return (Class::*method)(Message&),
+ Message& message) {
+ auto return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+
+// Overload for Status<T> return type.
+template <typename RemoteMethodType, typename Class, typename Return,
+ typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+ Status<Return> (Class::*method)(Message&),
+ Message& message) {
+ auto return_value = (instance.*method)(message);
+ // Return the value to the caller unless the message was moved.
+ if (message)
+ RemoteMethodReturn<RemoteMethodType>(message, return_value);
+}
+#endif
+
+} // namespace rpc
+
+// Definitions for template methods declared in pdx/client.h.
+
+template <int Opcode, typename T>
+struct CheckArgumentTypes;
+
+template <int Opcode, typename Return, typename... Args>
+struct CheckArgumentTypes<Opcode, Return(Args...)> {
+ template <typename R>
+ static typename rpc::EnableIfDirectReturn<R, Status<R>> Invoke(Client& client,
+ Args... args) {
+ Transaction trans{client};
+ rpc::ClientPayload<rpc::SendBuffer> payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&payload).EncodeArguments(
+ std::forward<Args>(args)...);
+ return trans.Send<R>(Opcode, payload.Data(), payload.Size(), nullptr, 0);
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfNotDirectReturn<R, Status<R>> Invoke(
+ Client& client, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans};
+ reply_payload.Resize(reply_payload.Capacity());
+
+ Status<R> result;
+ auto status =
+ trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(),
+ reply_payload.Data(), reply_payload.Size());
+ if (!status) {
+ result.SetError(status.error());
+ } else {
+ R return_value;
+ rpc::ErrorType error =
+ rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload)
+ .DecodeReturn(&return_value);
+
+ switch (error.error_code()) {
+ case rpc::ErrorCode::NO_ERROR:
+ result.SetValue(std::move(return_value));
+ break;
+
+ // This error is returned when ArrayWrapper/StringWrapper is too
+ // small to receive the payload.
+ case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ result.SetError(ENOBUFS);
+ break;
+
+ default:
+ result.SetError(EIO);
+ break;
+ }
+ }
+ return result;
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfDirectReturn<R, Status<void>> InvokeInPlace(
+ Client& client, R* return_value, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ Status<void> result;
+ auto status = trans.Send<R>(Opcode, send_payload.Data(),
+ send_payload.Size(), nullptr, 0);
+ if (status) {
+ *return_value = status.take();
+ result.SetValue();
+ } else {
+ result.SetError(status.error());
+ }
+ return result;
+ }
+
+ template <typename R>
+ static typename rpc::EnableIfNotDirectReturn<R, Status<void>> InvokeInPlace(
+ Client& client, R* return_value, Args... args) {
+ Transaction trans{client};
+
+ rpc::ClientPayload<rpc::SendBuffer> send_payload{trans};
+ rpc::MakeArgumentEncoder<Return(Args...)>(&send_payload)
+ .EncodeArguments(std::forward<Args>(args)...);
+
+ rpc::ClientPayload<rpc::ReplyBuffer> reply_payload{trans};
+ reply_payload.Resize(reply_payload.Capacity());
+
+ auto result =
+ trans.Send<void>(Opcode, send_payload.Data(), send_payload.Size(),
+ reply_payload.Data(), reply_payload.Size());
+ if (result) {
+ rpc::ErrorType error =
+ rpc::MakeArgumentDecoder<Return(Args...)>(&reply_payload)
+ .DecodeReturn(return_value);
+
+ switch (error.error_code()) {
+ case rpc::ErrorCode::NO_ERROR:
+ result.SetValue();
+ break;
+
+ // This error is returned when ArrayWrapper/StringWrapper is too
+ // small to receive the payload.
+ case rpc::ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ result.SetError(ENOBUFS);
+ break;
+
+ default:
+ result.SetError(EIO);
+ break;
+ }
+ }
+ return result;
+ }
+};
+
+// Invokes the remote method with opcode and signature described by
+// |RemoteMethodType|.
+template <typename RemoteMethodType, typename... Args>
+Status<typename RemoteMethodType::Return> Client::InvokeRemoteMethod(
+ Args&&... args) {
+ return CheckArgumentTypes<
+ RemoteMethodType::Opcode,
+ typename RemoteMethodType::template RewriteArgs<Args...>>::
+ template Invoke<typename RemoteMethodType::Return>(
+ *this, std::forward<Args>(args)...);
+}
+
+template <typename RemoteMethodType, typename Return, typename... Args>
+Status<void> Client::InvokeRemoteMethodInPlace(Return* return_value,
+ Args&&... args) {
+ return CheckArgumentTypes<
+ RemoteMethodType::Opcode,
+ typename RemoteMethodType::template RewriteSignature<Return, Args...>>::
+ template InvokeInPlace(*this, return_value, std::forward<Args>(args)...);
+}
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_REMOTE_METHOD_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
new file mode 100644
index 0000000000..cf9a189e60
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
@@ -0,0 +1,73 @@
+#ifndef ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
+#define ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+
+#include <pdx/rpc/enumeration.h>
+#include <pdx/rpc/function_traits.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class binding a remote method opcode to its function signature.
+// Describes the interface between RPC clients and services for a single method.
+template <int Opcode_, typename Signature_>
+struct RemoteMethodType {
+ typedef FunctionTraits<Signature_> Traits;
+
+ enum : int { Opcode = Opcode_ };
+
+ typedef typename Traits::Signature Signature;
+ typedef typename Traits::Return Return;
+ typedef typename Traits::Args Args;
+
+ template <typename... Params>
+ using RewriteArgs = typename Traits::template RewriteArgs<Params...>;
+
+ template <typename ReturnType, typename... Params>
+ using RewriteSignature =
+ typename Traits::template RewriteSignature<ReturnType, Params...>;
+
+ template <template <typename> class Wrapper, typename ReturnType,
+ typename... Params>
+ using RewriteSignatureWrapReturn =
+ typename Traits::template RewriteSignatureWrapReturn<Wrapper, ReturnType,
+ Params...>;
+
+ template <typename ReturnType>
+ using RewriteReturn = typename Traits::template RewriteReturn<ReturnType>;
+};
+
+// Utility class representing a set of related RemoteMethodTypes. Describes the
+// interface between RPC clients and services as a set of methods.
+template <typename... MethodTypes>
+struct RemoteAPI {
+ typedef std::tuple<MethodTypes...> Methods;
+ enum : std::size_t { Length = sizeof...(MethodTypes) };
+
+ template <std::size_t Index>
+ using Method = typename std::tuple_element<Index, Methods>::type;
+
+ template <typename MethodType>
+ static constexpr std::size_t MethodIndex() {
+ return ElementForType<MethodType, MethodTypes...>::Index;
+ }
+};
+
+// Macro to simplify defining remote method signatures. Remote method signatures
+// are specified by defining a RemoteMethodType for each remote method.
+#define PDX_REMOTE_METHOD(name, opcode, ... /*signature*/) \
+ using name = ::android::pdx::rpc::RemoteMethodType<opcode, __VA_ARGS__>
+
+// Macro to simplify defining a set of remote method signatures.
+#define PDX_REMOTE_API(name, ... /*methods*/) \
+ using name = ::android::pdx::rpc::RemoteAPI<__VA_ARGS__>
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_REMOTE_METHOD_TYPE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/sequence.h b/libs/vr/libpdx/private/pdx/rpc/sequence.h
new file mode 100644
index 0000000000..5fd898a846
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/sequence.h
@@ -0,0 +1,56 @@
+#ifndef ANDROID_PDX_RPC_SEQUENCE_H_
+#define ANDROID_PDX_RPC_SEQUENCE_H_
+
+#include <cstdint>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Provides a C++11 implementation of C++14 index_sequence and
+// make_index_sequence for compatibility with common compilers. This
+// implementation may be conditionally replaced with compiler-provided versions
+// when C++14 support is available.
+
+// Utility to capture a sequence of unsigned indices.
+template <std::size_t... I>
+struct IndexSequence {
+ using type = IndexSequence;
+ using value_type = std::size_t;
+ static constexpr std::size_t size() { return sizeof...(I); }
+};
+
+namespace detail {
+
+// Helper class to merge and renumber sequence parts in log N instantiations.
+template <typename S1, typename S2>
+struct MergeSequencesAndRenumber;
+
+template <std::size_t... I1, std::size_t... I2>
+struct MergeSequencesAndRenumber<IndexSequence<I1...>, IndexSequence<I2...>>
+ : IndexSequence<I1..., (sizeof...(I1) + I2)...> {};
+
+} // namespace detail
+
+// Utility to build an IndexSequence with N indices.
+template <std::size_t N>
+struct MakeIndexSequence : detail::MergeSequencesAndRenumber<
+ typename MakeIndexSequence<N / 2>::type,
+ typename MakeIndexSequence<N - N / 2>::type> {};
+
+// Identity sequences.
+template <>
+struct MakeIndexSequence<0> : IndexSequence<> {};
+template <>
+struct MakeIndexSequence<1> : IndexSequence<0> {};
+
+// Utility to build an IndexSequence with indices for each element of a
+// parameter pack.
+template <typename... T>
+using IndexSequenceFor = MakeIndexSequence<sizeof...(T)>;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SEQUENCE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/serializable.h b/libs/vr/libpdx/private/pdx/rpc/serializable.h
new file mode 100644
index 0000000000..04a4352268
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serializable.h
@@ -0,0 +1,150 @@
+#ifndef ANDROID_PDX_RPC_SERIALIZABLE_H_
+#define ANDROID_PDX_RPC_SERIALIZABLE_H_
+
+#include <cstddef>
+#include <string>
+#include <tuple>
+
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+
+#include "macros.h"
+#include "serialization.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// This file provides utilities to define serializable types for communication
+// between clients and services. Supporting efficient, typed communication
+// protocols is the primary goal, NOT providing a general-purpose solution for
+// all your C++ serialization needs. Features that are not aligned to the goals
+// are not supported, such as static/const member serialization and serializable
+// types with virtual methods (requiring a virtual destructor).
+
+// Captures the type and value of a pointer to member. Pointer to members are
+// essentially compile-time constant offsets that can be stored in the type
+// system without adding to the size of the structures they describe. This
+// library uses this property to implement a limited form of reflection for
+// serialization/deserialization functions.
+template <typename T, T>
+struct MemberPointer;
+
+template <typename Type, typename Class, Type Class::*Pointer>
+struct MemberPointer<Type Class::*, Pointer> {
+ // Type of the member pointer this type represents.
+ using PointerType = Type Class::*;
+
+ // Resolves a pointer to member with the given instance, yielding a
+ // reference to the member in that instance.
+ static Type& Resolve(Class& instance) { return (instance.*Pointer); }
+ static const Type& Resolve(const Class& instance) {
+ return (instance.*Pointer);
+ }
+};
+
+// Describes a set of members to be serialized/deserialized by this library. The
+// parameter pack MemberPointers takes a list of MemberPointer types that
+// describe each member to participate in serialization/deserialization.
+template <typename T, typename... MemberPointers>
+struct SerializableMembersType {
+ using Type = T;
+
+ // The number of member pointers described by this type.
+ enum : std::size_t { MemberCount = sizeof...(MemberPointers) };
+
+ // The member pointers described by this type.
+ using Members = std::tuple<MemberPointers...>;
+
+ // Accessor for individual member pointer types.
+ template <std::size_t Index>
+ using At = typename std::tuple_element<Index, Members>::type;
+};
+
+// Classes must do the following to correctly define a serializable type:
+// 1. Define a type called "SerializableMembers" as a template instantiation
+// of SerializableMembersType, describing the members of the class to
+// participate in serialization (presumably all of them). Use the macro
+// PDX_SERIALIZABLE_MEMBERS(...) below to aid the correct type
+// definition. This type should be private to prevent leaking member
+// access information.
+// 2. Make SerializableTraits and HasSerilizableMembers types a friend of
+// the class. The macro PDX_SERIALIZABLE_MEMEBRS(...) takes care of
+// this automatically.
+// 3. Define a public default constructor, if necessary. Deserialization
+// requires instances to be default-constructible.
+//
+// Example usage:
+// class MySerializableType : public AnotherBaseType {
+// public:
+// MySerializableType();
+// ...
+// private:
+// int a;
+// string b;
+// PDX_SERIALIZABLE_MEMBERS(MySerializableType, a, b);
+// };
+//
+// Note that const and static member serialization is not supported.
+
+template <typename T>
+class SerializableTraits {
+ public:
+ // Gets the serialized size of type T.
+ static std::size_t GetSerializedSize(const T& value) {
+ return GetEncodingSize(EncodeArrayType(SerializableMembers::MemberCount)) +
+ GetMembersSize<SerializableMembers>(value);
+ }
+
+ // Serializes type T.
+ static void SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArrayEncoding(EncodeArrayType(SerializableMembers::MemberCount),
+ SerializableMembers::MemberCount, buffer);
+ SerializeMembers<SerializableMembers>(value, writer, buffer);
+ }
+
+ // Deserializes type T.
+ static ErrorType DeserializeObject(T* value, MessageReader* reader,
+ const void*& start, const void* end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != SerializableMembers::MemberCount) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else {
+ return DeserializeMembers<SerializableMembers>(value, reader, start, end);
+ }
+ }
+
+ private:
+ using SerializableMembers = typename T::SerializableMembers;
+};
+
+// Utility macro to define a MemberPointer type for a member name.
+#define PDX_MEMBER_POINTER(type, member) \
+ ::android::pdx::rpc::MemberPointer<decltype(&type::member), &type::member>
+
+// Defines a list of MemberPointer types given a list of member names.
+#define PDX_MEMBERS(type, ... /*members*/) \
+ PDX_FOR_EACH_BINARY_LIST(PDX_MEMBER_POINTER, type, __VA_ARGS__)
+
+// Defines the serializable members of a type given a list of member names and
+// befriends SerializableTraits and HasSerializableMembers for the class. This
+// macro handles requirements #1 and #2 above.
+#define PDX_SERIALIZABLE_MEMBERS(type, ... /*members*/) \
+ template <typename T> \
+ friend class ::android::pdx::rpc::SerializableTraits; \
+ template <typename, typename> \
+ friend struct ::android::pdx::rpc::HasSerializableMembers; \
+ using SerializableMembers = ::android::pdx::rpc::SerializableMembersType< \
+ type, PDX_MEMBERS(type, __VA_ARGS__)>
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SERIALIZABLE_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/serialization.h b/libs/vr/libpdx/private/pdx/rpc/serialization.h
new file mode 100644
index 0000000000..f12aef1329
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serialization.h
@@ -0,0 +1,1998 @@
+#ifndef ANDROID_PDX_RPC_SERIALIZATION_H_
+#define ANDROID_PDX_RPC_SERIALIZATION_H_
+
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <map>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/message_reader.h>
+#include <pdx/message_writer.h>
+#include <pdx/trace.h>
+#include <pdx/utility.h>
+
+#include "array_wrapper.h"
+#include "default_initialization_allocator.h"
+#include "encoding.h"
+#include "pointer_wrapper.h"
+#include "string_wrapper.h"
+#include "variant.h"
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Automatic serialization/deserialization library based on MessagePack
+// (http://msgpack.org). This library provides top level Serialize() and
+// Deserialize() functions to encode/decode a variety of data types.
+//
+// The following data types are supported:
+// * Standard signed integer types: int8_t, int16_t, int32_t, and int64_t.
+// * Regular signed integer types equivalent to the standard types:
+// signed char, short, int, long, and long long.
+// * Standard unsigned integer types: uint8_t, uint16_t, uint32_t, and
+// uint64_t.
+// * Regular unsigned integer types equivalent to the standard types:
+// unsigned char, unsigned short, unsigned int, unsigned long,
+// and unsigned long long.
+// * char without signed/unsigned qualifiers.
+// * bool.
+// * std::vector with value type of any supported type, including nesting.
+// * std::string.
+// * std::tuple with elements of any supported type, including nesting.
+// * std::pair with elements of any supported type, including nesting.
+// * std::map with keys and values of any supported type, including nesting.
+// * std::unordered_map with keys and values of any supported type, including
+// nesting.
+// * std::array with values of any supported type, including nesting.
+// * ArrayWrapper of any supported basic type.
+// * BufferWrapper of any POD type.
+// * StringWrapper of any supported char type.
+// * User types with correctly defined SerializableMembers member type.
+//
+// Planned support for:
+// * std::basic_string with all supported char types.
+
+// Counting template for managing template recursion.
+template <std::size_t N>
+struct Index {};
+
+// Forward declaration of traits type to access types with a SerializedMembers
+// member type.
+template <typename T>
+class SerializableTraits;
+
+template <typename T, typename... MemberPointers>
+struct SerializableMembersType;
+
+// Utility to deduce the template type from a derived type.
+template <template <typename...> class TT, typename... Ts>
+std::true_type DeduceTemplateType(const TT<Ts...>*);
+template <template <typename...> class TT>
+std::false_type DeduceTemplateType(...);
+
+// Utility determining whether template type TT<...> is a base of type T.
+template <template <typename...> class TT, typename T>
+using IsTemplateBaseOf = decltype(DeduceTemplateType<TT>(std::declval<T*>()));
+
+// Utility type for SFINAE in HasHasSerializableMembers.
+template <typename... Ts>
+using TrySerializableMembersType = void;
+
+// Determines whether type T has a member type named SerializableMembers of
+// template type SerializableMembersType.
+template <typename, typename = void>
+struct HasSerializableMembers : std::false_type {};
+template <typename T>
+struct HasSerializableMembers<
+ T, TrySerializableMembersType<typename T::SerializableMembers>>
+ : std::integral_constant<
+ bool, IsTemplateBaseOf<SerializableMembersType,
+ typename T::SerializableMembers>::value> {};
+
+// Utility to simplify overload enable expressions for types with correctly
+// defined SerializableMembers.
+template <typename T>
+using EnableIfHasSerializableMembers =
+ typename std::enable_if<HasSerializableMembers<T>::value>::type;
+
+// Utility to simplify overload enable expressions for enum types.
+template <typename T, typename ReturnType = void>
+using EnableIfEnum =
+ typename std::enable_if<std::is_enum<T>::value, ReturnType>::type;
+
+///////////////////////////////////////////////////////////////////////////////
+// Error Reporting //
+///////////////////////////////////////////////////////////////////////////////
+
+// Error codes returned by the deserialization code.
+enum class ErrorCode {
+ NO_ERROR = 0,
+ UNEXPECTED_ENCODING,
+ UNEXPECTED_TYPE_SIZE,
+ INSUFFICIENT_BUFFER,
+ INSUFFICIENT_DESTINATION_SIZE,
+ GET_FILE_DESCRIPTOR_FAILED,
+ GET_CHANNEL_HANDLE_FAILED,
+ INVALID_VARIANT_ELEMENT,
+};
+
+// Type for errors returned by the deserialization code.
+class ErrorType {
+ public:
+ ErrorType() : error_code_(ErrorCode::NO_ERROR) {}
+
+ // ErrorType constructor for generic error codes. Explicitly not explicit,
+ // implicit conversion from ErrorCode to ErrorType is desirable behavior.
+ // NOLINTNEXTLINE(runtime/explicit)
+ ErrorType(ErrorCode error_code) : error_code_(error_code) {}
+
+ // ErrorType constructor for encoding type errors.
+ ErrorType(ErrorCode error_code, EncodingClass encoding_class,
+ EncodingType encoding_type)
+ : error_code_(error_code) {
+ unexpected_encoding_.encoding_class = encoding_class;
+ unexpected_encoding_.encoding_type = encoding_type;
+ }
+
+ // Evaluates to true if the ErrorType represents an error.
+ explicit operator bool() const { return error_code_ != ErrorCode::NO_ERROR; }
+
+ operator ErrorCode() const { return error_code_; }
+ ErrorCode error_code() const { return error_code_; }
+
+ // Accessors for extra info about unexpected encoding errors.
+ EncodingClass encoding_class() const {
+ return unexpected_encoding_.encoding_class;
+ }
+ EncodingType encoding_type() const {
+ return unexpected_encoding_.encoding_type;
+ }
+
+ operator std::string() const {
+ std::ostringstream stream;
+
+ switch (error_code_) {
+ case ErrorCode::NO_ERROR:
+ return "NO_ERROR";
+ case ErrorCode::UNEXPECTED_ENCODING:
+ stream << "UNEXPECTED_ENCODING: " << static_cast<int>(encoding_class())
+ << ", " << static_cast<int>(encoding_type());
+ return stream.str();
+ case ErrorCode::UNEXPECTED_TYPE_SIZE:
+ return "UNEXPECTED_TYPE_SIZE";
+ case ErrorCode::INSUFFICIENT_BUFFER:
+ return "INSUFFICIENT_BUFFER";
+ case ErrorCode::INSUFFICIENT_DESTINATION_SIZE:
+ return "INSUFFICIENT_DESTINATION_SIZE";
+ default:
+ return "[Unknown Error]";
+ }
+ }
+
+ private:
+ ErrorCode error_code_;
+
+ // Union of extra information for different error code types.
+ union {
+ // UNEXPECTED_ENCODING.
+ struct {
+ EncodingClass encoding_class;
+ EncodingType encoding_type;
+ } unexpected_encoding_;
+ };
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Size //
+///////////////////////////////////////////////////////////////////////////////
+
+inline constexpr std::size_t GetSerializedSize(const bool& b) {
+ return GetEncodingSize(EncodeType(b));
+}
+
+// Overloads of GetSerializedSize() for standard integer types.
+inline constexpr std::size_t GetSerializedSize(const char& c) {
+ return GetEncodingSize(EncodeType(c));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint8_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int8_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint16_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int16_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint32_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int32_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::uint64_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+inline constexpr std::size_t GetSerializedSize(const std::int64_t& i) {
+ return GetEncodingSize(EncodeType(i));
+}
+
+inline constexpr std::size_t GetSerializedSize(const float& f) {
+ return GetEncodingSize(EncodeType(f));
+}
+inline constexpr std::size_t GetSerializedSize(const double& d) {
+ return GetEncodingSize(EncodeType(d));
+}
+
+// Overload for enum types.
+template <typename T>
+inline EnableIfEnum<T, std::size_t> GetSerializedSize(T v) {
+ return GetSerializedSize(static_cast<std::underlying_type_t<T>>(v));
+}
+
+// Forward declaration for nested definitions.
+inline std::size_t GetSerializedSize(const EmptyVariant&);
+template <typename... Types>
+inline std::size_t GetSerializedSize(const Variant<Types...>&);
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline constexpr std::size_t GetSerializedSize(const T&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>&);
+inline constexpr std::size_t GetSerializedSize(const std::string&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>&);
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>&);
+template <FileHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>&);
+template <ChannelHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const ChannelHandle<Mode>&);
+template <typename T, typename Allocator>
+inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::map<Key, T, Compare, Allocator>& m);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&);
+template <typename T>
+inline std::size_t GetSerializedSize(const ArrayWrapper<T>&);
+template <typename T, std::size_t Size>
+inline std::size_t GetSerializedSize(const std::array<T, Size>& v);
+template <typename T, typename U>
+inline std::size_t GetSerializedSize(const std::pair<T, U>& p);
+template <typename... T>
+inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple);
+
+// Overload for empty variant type.
+inline std::size_t GetSerializedSize(const EmptyVariant& empty) {
+ return GetEncodingSize(EncodeType(empty));
+}
+
+// Overload for Variant types.
+template <typename... Types>
+inline std::size_t GetSerializedSize(const Variant<Types...>& variant) {
+ return GetEncodingSize(EncodeType(variant)) +
+ GetSerializedSize(variant.index()) +
+ variant.Visit(
+ [](const auto& value) { return GetSerializedSize(value); });
+}
+
+// Overload for structs/classes with SerializableMembers defined.
+template <typename T, typename Enabled>
+inline constexpr std::size_t GetSerializedSize(const T& value) {
+ return SerializableTraits<T>::GetSerializedSize(value);
+}
+
+// Overload for PointerWrapper.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const PointerWrapper<T>& p) {
+ return GetSerializedSize(p.Dereference());
+}
+
+// Overload for std::string.
+inline constexpr std::size_t GetSerializedSize(const std::string& s) {
+ return GetEncodingSize(EncodeType(s)) +
+ s.length() * sizeof(std::string::value_type);
+}
+
+// Overload for StringWrapper.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const StringWrapper<T>& s) {
+ return GetEncodingSize(EncodeType(s)) +
+ s.length() * sizeof(typename StringWrapper<T>::value_type);
+}
+
+// Overload for BufferWrapper types.
+template <typename T>
+inline constexpr std::size_t GetSerializedSize(const BufferWrapper<T>& b) {
+ return GetEncodingSize(EncodeType(b)) +
+ b.size() * sizeof(typename BufferWrapper<T>::value_type);
+}
+
+// Overload for FileHandle. FileHandle is encoded as a FIXEXT2, with a type code
+// of "FileHandle" and a signed 16-bit offset into the pushed fd array. Empty
+// FileHandles are encoded with an array index of -1.
+template <FileHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(const FileHandle<Mode>& fd) {
+ return GetEncodingSize(EncodeType(fd)) + sizeof(std::int16_t);
+}
+
+// Overload for ChannelHandle. ChannelHandle is encoded as a FIXEXT4, with a
+// type code of "ChannelHandle" and a signed 32-bit offset into the pushed
+// channel array. Empty ChannelHandles are encoded with an array index of -1.
+template <ChannelHandleMode Mode>
+inline constexpr std::size_t GetSerializedSize(
+ const ChannelHandle<Mode>& channel_handle) {
+ return GetEncodingSize(EncodeType(channel_handle)) + sizeof(std::int32_t);
+}
+
+// Overload for standard vector types.
+template <typename T, typename Allocator>
+inline std::size_t GetSerializedSize(const std::vector<T, Allocator>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for standard map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::map<Key, T, Compare, Allocator>& v) {
+ return std::accumulate(
+ v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const std::pair<Key, T>& object) {
+ return sum + GetSerializedSize(object.first) +
+ GetSerializedSize(object.second);
+ });
+}
+
+// Overload for standard unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline std::size_t GetSerializedSize(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v) {
+ return std::accumulate(
+ v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const std::pair<Key, T>& object) {
+ return sum + GetSerializedSize(object.first) +
+ GetSerializedSize(object.second);
+ });
+}
+
+// Overload for ArrayWrapper types.
+template <typename T>
+inline std::size_t GetSerializedSize(const ArrayWrapper<T>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for std::array types.
+template <typename T, std::size_t Size>
+inline std::size_t GetSerializedSize(const std::array<T, Size>& v) {
+ return std::accumulate(v.begin(), v.end(), GetEncodingSize(EncodeType(v)),
+ [](const std::size_t& sum, const T& object) {
+ return sum + GetSerializedSize(object);
+ });
+}
+
+// Overload for std::pair.
+template <typename T, typename U>
+inline std::size_t GetSerializedSize(const std::pair<T, U>& p) {
+ return GetEncodingSize(EncodeType(p)) + GetSerializedSize(p.first) +
+ GetSerializedSize(p.second);
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline std::size_t GetTupleSize(const std::tuple<T...>&, Index<0>) {
+ return 0;
+}
+
+// Gets the size of each element in a tuple recursively.
+template <typename... T, std::size_t index>
+inline std::size_t GetTupleSize(const std::tuple<T...>& tuple, Index<index>) {
+ return GetTupleSize(tuple, Index<index - 1>()) +
+ GetSerializedSize(std::get<index - 1>(tuple));
+}
+
+// Overload for tuple types. Gets the size of the tuple, recursing
+// through the elements.
+template <typename... T>
+inline std::size_t GetSerializedSize(const std::tuple<T...>& tuple) {
+ return GetEncodingSize(EncodeType(tuple)) +
+ GetTupleSize(tuple, Index<sizeof...(T)>());
+}
+
+// Stops template recursion when the last member of a Serializable
+// type is reached.
+template <typename Members, typename T>
+inline std::size_t GetMemberSize(const T&, Index<0>) {
+ return 0;
+}
+
+// Gets the size of each member of a Serializable type recursively.
+template <typename Members, typename T, std::size_t index>
+inline std::size_t GetMemberSize(const T& object, Index<index>) {
+ return GetMemberSize<Members>(object, Index<index - 1>()) +
+ GetSerializedSize(Members::template At<index - 1>::Resolve(object));
+}
+
+// Gets the size of a type using the given SerializableMembersType
+// type.
+template <typename Members, typename T>
+inline std::size_t GetMembersSize(const T& object) {
+ return GetMemberSize<Members>(object, Index<Members::MemberCount>());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Serialization //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// SerializeRaw() converts a primitive array or type into a raw byte string.
+// These functions are named differently from SerializeObject() expressly to
+// avoid catch-all specialization of that template, which can be difficult to
+// detect otherwise.
+//
+
+inline void WriteRawData(void*& dest, const void* src, size_t size) {
+ memcpy(dest, src, size);
+ dest = static_cast<uint8_t*>(dest) + size;
+}
+
+// Serializes a primitive array into a raw byte string.
+template <typename T,
+ typename = typename std::enable_if<std::is_pod<T>::value>::type>
+inline void SerializeRaw(const T& value, void*& buffer) {
+ WriteRawData(buffer, &value, sizeof(value));
+}
+
+inline void SerializeEncoding(EncodingType encoding, void*& buffer) {
+ SerializeRaw(encoding, buffer);
+}
+
+inline void SerializeType(const bool& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Serializes the type code, extended type code, and size for
+// extension types.
+inline void SerializeExtEncoding(EncodingType encoding,
+ EncodingExtType ext_type, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_EXT8) {
+ std::uint8_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_EXT16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_EXT32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixextEncoding(encoding) */ {
+ // Encoding byte contains the fixext length, nothing else to do.
+ }
+ SerializeRaw(ext_type, buffer);
+}
+
+// Serializes the type code for file descriptor types.
+template <FileHandleMode Mode>
+inline void SerializeType(const FileHandle<Mode>& value, void*& buffer) {
+ SerializeExtEncoding(EncodeType(value), ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 2,
+ buffer);
+}
+
+// Serializes the type code for channel handle types.
+template <ChannelHandleMode Mode>
+inline void SerializeType(const ChannelHandle<Mode>& handle, void*& buffer) {
+ SerializeExtEncoding(EncodeType(handle), ENCODING_EXT_TYPE_CHANNEL_HANDLE, 4,
+ buffer);
+}
+
+// Serializes type code for variant types.
+template <typename... Types>
+inline void SerializeType(const Variant<Types...>& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Serializes the type code for string types.
+template <typename StringType>
+inline void SerializeStringType(const StringType& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_STR8) {
+ std::uint8_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_STR16) {
+ std::uint16_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_STR32) {
+ std::uint32_t length = value.length();
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixstrEncoding(encoding) */ {
+ // Encoding byte contains the fixstr length, nothing else to do.
+ }
+}
+
+// Serializes the type code for std::string and StringWrapper. These types are
+// interchangeable and must serialize to the same format.
+inline void SerializeType(const std::string& value, void*& buffer) {
+ SerializeStringType(value, buffer);
+}
+template <typename T>
+inline void SerializeType(const StringWrapper<T>& value, void*& buffer) {
+ SerializeStringType(value, buffer);
+}
+
+// Serializes the type code for bin types.
+inline void SerializeBinEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_BIN8) {
+ std::uint8_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_BIN16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_BIN32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else {
+ // Invalid encoding for BIN type.
+ }
+}
+
+// Serializes the type code for BufferWrapper types.
+template <typename T>
+inline void SerializeType(const BufferWrapper<T>& value, void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeBinEncoding(
+ encoding, value.size() * sizeof(typename BufferWrapper<T>::value_type),
+ buffer);
+}
+
+// Serializes the array encoding type and length.
+inline void SerializeArrayEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_ARRAY16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_ARRAY32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixarrayEncoding(encoding) */ {
+ // Encoding byte contains the fixarray length, nothing else to do.
+ }
+}
+
+// Serializes the map encoding type and length.
+inline void SerializeMapEncoding(EncodingType encoding, std::size_t size,
+ void*& buffer) {
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_MAP16) {
+ std::uint16_t length = size;
+ SerializeRaw(length, buffer);
+ } else if (encoding == ENCODING_TYPE_MAP32) {
+ std::uint32_t length = size;
+ SerializeRaw(length, buffer);
+ } else /* if (IsFixmapEncoding(encoding) */ {
+ // Encoding byte contains the fixmap length, nothing else to do.
+ }
+}
+
+// Serializes the type code for array types.
+template <typename ArrayType>
+inline void SerializeArrayType(const ArrayType& value, std::size_t size,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeArrayEncoding(encoding, size, buffer);
+}
+
+// Serializes the type code for map types.
+template <typename MapType>
+inline void SerializeMapType(const MapType& value, std::size_t size,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeMapEncoding(encoding, size, buffer);
+}
+
+// Serializes the type code for std::vector and ArrayWrapper. These types are
+// interchangeable and must serialize to the same format.
+template <typename T, typename Allocator>
+inline void SerializeType(const std::vector<T, Allocator>& value,
+ void*& buffer) {
+ SerializeArrayType(value, value.size(), buffer);
+}
+template <typename T>
+inline void SerializeType(const ArrayWrapper<T>& value, void*& buffer) {
+ SerializeArrayType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::array. This type serializes to the same
+// format as std::vector and ArrayWrapper and is interchangeable in certain
+// situations.
+template <typename T, std::size_t Size>
+inline void SerializeType(const std::array<T, Size>& value, void*& buffer) {
+ SerializeArrayType(value, Size, buffer);
+}
+
+// Serializes the type code for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeType(const std::map<Key, T, Compare, Allocator>& value,
+ void*& buffer) {
+ SerializeMapType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeType(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& value,
+ void*& buffer) {
+ SerializeMapType(value, value.size(), buffer);
+}
+
+// Serializes the type code for std::pair types.
+template <typename T, typename U>
+inline void SerializeType(const std::pair<T, U>& value, void*& buffer) {
+ SerializeArrayType(value, 2, buffer);
+}
+
+// Serializes the type code for std::tuple types.
+template <typename... T>
+inline void SerializeType(const std::tuple<T...>& value, void*& buffer) {
+ SerializeArrayType(value, sizeof...(T), buffer);
+}
+
+// Specialization of SerializeObject for boolean type.
+inline void SerializeObject(const bool& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ SerializeType(value, buffer);
+ // Encoding contains the boolean value, nothing else to do.
+}
+
+// Overloads of SerializeObject for float and double types.
+inline void SerializeObject(const float& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ SerializeRaw(value, buffer);
+}
+
+inline void SerializeObject(const double& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ SerializeRaw(value, buffer);
+}
+
+// Overloads of SerializeObject() for standard integer types.
+inline void SerializeObject(const char& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int8_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint8_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int16_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint16_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int32_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ const int16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_INT32) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint32_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ const uint16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT32) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const int64_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_INT8) {
+ const int8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_INT16) {
+ const int16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_INT32) {
+ const int32_t word = value;
+ SerializeRaw(word, buffer);
+ } else if (encoding == ENCODING_TYPE_INT64) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+inline void SerializeObject(const uint64_t& value, MessageWriter* /*writer*/,
+ void*& buffer) {
+ const EncodingType encoding = EncodeType(value);
+ SerializeEncoding(encoding, buffer);
+ if (encoding == ENCODING_TYPE_UINT8) {
+ const uint8_t byte = value;
+ SerializeRaw(byte, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT16) {
+ const uint16_t half = value;
+ SerializeRaw(half, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT32) {
+ const uint32_t word = value;
+ SerializeRaw(word, buffer);
+ } else if (encoding == ENCODING_TYPE_UINT64) {
+ SerializeRaw(value, buffer);
+ } else /* if (IsUnsignedFixintEncoding(encoding) */ {
+ // Encoding byte contains the value, nothing else to do.
+ }
+}
+
+// Serialize enum types.
+template <typename T>
+inline EnableIfEnum<T> SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializeObject(static_cast<std::underlying_type_t<T>>(value), writer,
+ buffer);
+}
+
+// Forward declaration for nested definitions.
+inline void SerializeObject(const EmptyVariant&, MessageWriter*, void*&);
+template <typename... Types>
+inline void SerializeObject(const Variant<Types...>&, MessageWriter*, void*&);
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline void SerializeObject(const T&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const PointerWrapper<T>&, MessageWriter*, void*&);
+template <FileHandleMode Mode>
+inline void SerializeObject(const FileHandle<Mode>&, MessageWriter*, void*&);
+template <ChannelHandleMode Mode>
+inline void SerializeObject(const ChannelHandle<Mode>&, MessageWriter*, void*&);
+template <typename T, typename Allocator>
+inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const BufferWrapper<T*>&, MessageWriter*, void*&);
+inline void SerializeObject(const std::string&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const StringWrapper<T>&, MessageWriter*, void*&);
+template <typename T, typename Allocator>
+inline void SerializeObject(const std::vector<T, Allocator>&, MessageWriter*, void*&);
+template <typename T>
+inline void SerializeObject(const ArrayWrapper<T>&, MessageWriter*, void*&);
+template <typename T, std::size_t Size>
+inline void SerializeObject(const std::array<T, Size>&, MessageWriter*, void*&);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeObject(const std::map<Key, T, Compare, Allocator>&, MessageWriter*, void*&);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeObject(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>&, MessageWriter*, void*&);
+template <typename T, typename U>
+inline void SerializeObject(const std::pair<T, U>&, MessageWriter*, void*&);
+template <typename... T>
+inline void SerializeObject(const std::tuple<T...>&, MessageWriter*, void*&);
+
+// Overload for empty variant type.
+inline void SerializeObject(const EmptyVariant& empty,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const EncodingType encoding = EncodeType(empty);
+ SerializeEncoding(encoding, buffer);
+}
+
+// Overload for Variant types.
+template <typename... Types>
+inline void SerializeObject(const Variant<Types...>& variant,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(variant, buffer);
+ SerializeObject(variant.index(), writer, buffer);
+ return variant.Visit([writer, &buffer](const auto& value) {
+ return SerializeObject(value, writer, buffer);
+ });
+}
+
+// Overload for serializable structure/class types.
+template <typename T, typename Enabled>
+inline void SerializeObject(const T& value, MessageWriter* writer,
+ void*& buffer) {
+ SerializableTraits<T>::SerializeObject(value, writer, buffer);
+}
+
+// Serializes the payload of a PointerWrapper.
+template <typename T>
+inline void SerializeObject(const PointerWrapper<T>& pointer,
+ MessageWriter* writer, void*& buffer) {
+ SerializeObject(pointer.Dereference(), writer, buffer);
+}
+
+// Serializes the payload of file descriptor types.
+template <FileHandleMode Mode>
+inline void SerializeObject(const FileHandle<Mode>& fd, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(fd, buffer);
+ const Status<FileReference> status =
+ writer->GetOutputResourceMapper()->PushFileHandle(fd);
+ FileReference value = status ? status.get() : -status.error();
+ SerializeRaw(value, buffer);
+}
+
+// Serializes the payload of channel handle types.
+template <ChannelHandleMode Mode>
+inline void SerializeObject(const ChannelHandle<Mode>& handle,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(handle, buffer);
+ const Status<ChannelReference> status =
+ writer->GetOutputResourceMapper()->PushChannelHandle(handle);
+ ChannelReference value = status ? status.get() : -status.error();
+ SerializeRaw(value, buffer);
+}
+
+// Serializes the payload of BufferWrapper types.
+template <typename T, typename Allocator>
+inline void SerializeObject(const BufferWrapper<std::vector<T, Allocator>>& b,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const auto value_type_size =
+ sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type);
+ SerializeType(b, buffer);
+ WriteRawData(buffer, b.data(), b.size() * value_type_size);
+}
+template <typename T>
+inline void SerializeObject(const BufferWrapper<T*>& b,
+ MessageWriter* /*writer*/, void*& buffer) {
+ const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type);
+ SerializeType(b, buffer);
+ WriteRawData(buffer, b.data(), b.size() * value_type_size);
+}
+
+// Serializes the payload of string types.
+template <typename StringType>
+inline void SerializeString(const StringType& s, void*& buffer) {
+ const auto value_type_size = sizeof(typename StringType::value_type);
+ SerializeType(s, buffer);
+ WriteRawData(buffer, s.data(), s.length() * value_type_size);
+}
+
+// Overload of SerializeObject() for std::string and StringWrapper. These types
+// are interchangeable and must serialize to the same format.
+inline void SerializeObject(const std::string& s, MessageWriter* /*writer*/,
+ void*& buffer) {
+ SerializeString(s, buffer);
+}
+template <typename T>
+inline void SerializeObject(const StringWrapper<T>& s,
+ MessageWriter* /*writer*/, void*& buffer) {
+ SerializeString(s, buffer);
+}
+
+// Serializes the payload of array types.
+template <typename ArrayType>
+inline void SerializeArray(const ArrayType& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(v, buffer);
+ for (const auto& element : v)
+ SerializeObject(element, writer, buffer);
+}
+
+// Serializes the payload for map types.
+template <typename MapType>
+inline void SerializeMap(const MapType& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(v, buffer);
+ for (const auto& element : v) {
+ SerializeObject(element.first, writer, buffer);
+ SerializeObject(element.second, writer, buffer);
+ }
+}
+
+// Overload of SerializeObject() for std::vector and ArrayWrapper types. These
+// types are interchangeable and must serialize to the same format.
+template <typename T, typename Allocator>
+inline void SerializeObject(const std::vector<T, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+template <typename T>
+inline void SerializeObject(const ArrayWrapper<T>& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::array types. These types serialize to
+// the same format at std::vector and ArrayWrapper and are interchangeable in
+// certain situations.
+template <typename T, std::size_t Size>
+inline void SerializeObject(const std::array<T, Size>& v, MessageWriter* writer,
+ void*& buffer) {
+ SerializeArray(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline void SerializeObject(const std::map<Key, T, Compare, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeMap(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline void SerializeObject(
+ const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v,
+ MessageWriter* writer, void*& buffer) {
+ SerializeMap(v, writer, buffer);
+}
+
+// Overload of SerializeObject() for std:pair types.
+template <typename T, typename U>
+inline void SerializeObject(const std::pair<T, U>& pair, MessageWriter* writer,
+ void*& buffer) {
+ SerializeType(pair, buffer);
+ SerializeObject(pair.first, writer, buffer);
+ SerializeObject(pair.second, writer, buffer);
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline void SerializeTuple(const std::tuple<T...>&, MessageWriter*, void*&,
+ Index<0>) {}
+
+// Serializes each element of a tuple recursively.
+template <typename... T, std::size_t index>
+inline void SerializeTuple(const std::tuple<T...>& tuple, MessageWriter* writer,
+ void*& buffer, Index<index>) {
+ SerializeTuple(tuple, writer, buffer, Index<index - 1>());
+ SerializeObject(std::get<index - 1>(tuple), writer, buffer);
+}
+
+// Overload of SerializeObject() for tuple types.
+template <typename... T>
+inline void SerializeObject(const std::tuple<T...>& tuple,
+ MessageWriter* writer, void*& buffer) {
+ SerializeType(tuple, buffer);
+ SerializeTuple(tuple, writer, buffer, Index<sizeof...(T)>());
+}
+
+// Stops template recursion when the last member pointer is reached.
+template <typename Members, typename T>
+inline void SerializeMember(const T&, MessageWriter*, void*&, Index<0>) {}
+
+// Serializes each member pointer recursively.
+template <typename Members, typename T, std::size_t index>
+inline void SerializeMember(const T& object, MessageWriter* writer,
+ void*& buffer, Index<index>) {
+ SerializeMember<Members>(object, writer, buffer, Index<index - 1>());
+ SerializeObject(Members::template At<index - 1>::Resolve(object), writer,
+ buffer);
+}
+
+// Serializes the members of a type using the given SerializableMembersType
+// type.
+template <typename Members, typename T>
+inline void SerializeMembers(const T& object, MessageWriter* writer,
+ void*& buffer) {
+ SerializeMember<Members>(object, writer, buffer,
+ Index<Members::MemberCount>());
+}
+
+// Top level serialization function that replaces the buffer's contents.
+template <typename T>
+inline void Serialize(const T& object, MessageWriter* writer) {
+ PDX_TRACE_NAME("Serialize");
+ const std::size_t size = GetSerializedSize(object);
+
+ // Reserve the space needed for the object(s).
+ void* buffer = writer->GetNextWriteBufferSection(size);
+ SerializeObject(object, writer, buffer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Object Deserialization //
+///////////////////////////////////////////////////////////////////////////////
+
+inline ErrorType ReadRawDataFromNextSection(void* dest, MessageReader* reader,
+ const void*& start,
+ const void*& end, size_t size) {
+ while (AdvancePointer(start, size) > end) {
+ auto remaining_size = PointerDistance(end, start);
+ if (remaining_size > 0) {
+ memcpy(dest, start, remaining_size);
+ dest = AdvancePointer(dest, remaining_size);
+ size -= remaining_size;
+ }
+ reader->ConsumeReadBufferSectionData(AdvancePointer(start, remaining_size));
+ std::tie(start, end) = reader->GetNextReadBufferSection();
+ if (start == end)
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ }
+ memcpy(dest, start, size);
+ start = AdvancePointer(start, size);
+ return ErrorCode::NO_ERROR;
+}
+
+inline ErrorType ReadRawData(void* dest, MessageReader* /*reader*/,
+ const void*& start, const void*& end,
+ size_t size) {
+ if (PDX_UNLIKELY(AdvancePointer(start, size) > end)) {
+ // TODO(avakulenko): Enabling reading from next sections of input buffer
+ // (using ReadRawDataFromNextSection) screws up clang compiler optimizations
+ // (probably inefficient inlining) making the whole deserialization
+ // code path about twice as slow. Investigate and enable more generic
+ // deserialization code, but right now we don't really need/support this
+ // scenario, so I keep this commented out for the time being...
+
+ // return ReadRawDataFromNextSection(dest, reader, start, end, size);
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ }
+ memcpy(dest, start, size);
+ start = AdvancePointer(start, size);
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes a primitive object from raw bytes.
+template <typename T,
+ typename = typename std::enable_if<std::is_pod<T>::value>::type>
+inline ErrorType DeserializeRaw(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return ReadRawData(value, reader, start, end, sizeof(T));
+}
+
+// Utility to deserialize POD types when the serialized type is different
+// (smaller) than the target real type. This happens when values are serialized
+// into more compact encodings.
+template <typename SerializedType, typename RealType>
+ErrorType DeserializeValue(RealType* real_value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ SerializedType serialized_value;
+ if (const auto error =
+ DeserializeRaw(&serialized_value, reader, start, end)) {
+ return error;
+ } else {
+ *real_value = serialized_value;
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+inline ErrorType DeserializeEncoding(EncodingType* encoding,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeRaw(encoding, reader, start, end);
+}
+
+// Overload to deserialize bool type.
+inline ErrorType DeserializeObject(bool* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsBoolEncoding(encoding)) {
+ *value = (encoding == ENCODING_TYPE_TRUE);
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BOOL,
+ encoding);
+ }
+}
+
+// Specializations to deserialize float and double types.
+inline ErrorType DeserializeObject(float* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFloat32Encoding(encoding)) {
+ return DeserializeValue<float>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(double* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFloat32Encoding(encoding)) {
+ return DeserializeValue<float>(value, reader, start, end);
+ } else if (IsFloat64Encoding(encoding)) {
+ return DeserializeValue<double>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_FLOAT,
+ encoding);
+ }
+}
+
+// Specializations to deserialize standard integer types.
+inline ErrorType DeserializeObject(char* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = static_cast<char>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<char>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int8_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint8_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int16_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint16_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int32_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else if (IsInt32Encoding(encoding)) {
+ return DeserializeValue<std::int32_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint32_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else if (IsUInt32Encoding(encoding)) {
+ return DeserializeValue<std::uint32_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::int64_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixintEncoding(encoding)) {
+ *value = static_cast<std::int8_t>(encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (IsInt8Encoding(encoding)) {
+ return DeserializeValue<std::int8_t>(value, reader, start, end);
+ } else if (IsInt16Encoding(encoding)) {
+ return DeserializeValue<std::int16_t>(value, reader, start, end);
+ } else if (IsInt32Encoding(encoding)) {
+ return DeserializeValue<std::int32_t>(value, reader, start, end);
+ } else if (IsInt64Encoding(encoding)) {
+ return DeserializeValue<std::int64_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_INT,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(std::uint64_t* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (IsUnsignedFixintEncoding(encoding)) {
+ *value = encoding;
+ return ErrorCode::NO_ERROR;
+ } else if (IsUInt8Encoding(encoding)) {
+ return DeserializeValue<std::uint8_t>(value, reader, start, end);
+ } else if (IsUInt16Encoding(encoding)) {
+ return DeserializeValue<std::uint16_t>(value, reader, start, end);
+ } else if (IsUInt32Encoding(encoding)) {
+ return DeserializeValue<std::uint32_t>(value, reader, start, end);
+ } else if (IsUInt64Encoding(encoding)) {
+ return DeserializeValue<std::uint64_t>(value, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_UINT,
+ encoding);
+ }
+}
+
+template <typename T>
+inline EnableIfEnum<T, ErrorType> DeserializeObject(T* value,
+ MessageReader* reader,
+ const void*& start,
+ const void*& end) {
+ std::underlying_type_t<T> enum_value;
+ ErrorType error = DeserializeObject(&enum_value, reader, start, end);
+ if (!error)
+ *value = static_cast<T>(enum_value);
+ return error;
+}
+
+// Forward declarations for nested definitions.
+template <typename T, typename Enabled = EnableIfHasSerializableMembers<T>>
+inline ErrorType DeserializeObject(T*, MessageReader*, const void*&,
+ const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(PointerWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(LocalHandle*, MessageReader*, const void*&,
+ const void*&);
+inline ErrorType DeserializeObject(LocalChannelHandle*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(BufferWrapper<std::vector<T, Allocator>>*,
+ MessageReader*, const void*&, const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(BufferWrapper<T*>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(std::string*, MessageReader*, const void*&,
+ const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(StringWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*,
+ const void*&, const void*&);
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(std::vector<T, Allocator>*, MessageReader*,
+ const void*&, const void*&);
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>*,
+ MessageReader*, const void*&, const void*&);
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline ErrorType DeserializeObject(
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T>
+inline ErrorType DeserializeObject(ArrayWrapper<T>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, std::size_t Size>
+inline ErrorType DeserializeObject(std::array<T, Size>*, MessageReader*,
+ const void*&, const void*&);
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>*, MessageReader*,
+ const void*&, const void*&);
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*&);
+inline ErrorType DeserializeObject(EmptyVariant*,
+ MessageReader*, const void*&,
+ const void*&);
+template <typename... Types>
+inline ErrorType DeserializeObject(Variant<Types...>*,
+ MessageReader*, const void*&,
+ const void*&);
+
+// Deserializes a Serializable type.
+template <typename T, typename Enable>
+inline ErrorType DeserializeObject(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return SerializableTraits<T>::DeserializeObject(value, reader, start, end);
+}
+
+// Deserializes a PointerWrapper.
+template <typename T>
+inline ErrorType DeserializeObject(PointerWrapper<T>* pointer,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeObject(&pointer->Dereference(), reader, start, end);
+}
+
+// Deserializes the type code and size for extension types.
+inline ErrorType DeserializeExtType(EncodingType* encoding,
+ EncodingExtType* type, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixextEncoding(*encoding)) {
+ *size = GetFixextSize(*encoding);
+ } else if (*encoding == ENCODING_TYPE_EXT8) {
+ if (const auto error =
+ DeserializeValue<std::uint8_t>(size, reader, start, end))
+ return error;
+ } else if (*encoding == ENCODING_TYPE_EXT16) {
+ if (const auto error =
+ DeserializeValue<std::uint16_t>(size, reader, start, end))
+ return error;
+ } else if (*encoding == ENCODING_TYPE_EXT32) {
+ if (const auto error =
+ DeserializeValue<std::uint32_t>(size, reader, start, end))
+ return error;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ *encoding);
+ }
+
+ // The extension type code follows the encoding and size.
+ return DeserializeRaw(type, reader, start, end);
+}
+
+// Deserializes a file handle and performs handle space translation, if
+// required.
+inline ErrorType DeserializeObject(LocalHandle* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ EncodingExtType type;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeExtType(&encoding, &type, &size, reader, start, end)) {
+ return error;
+ } else if (size != 2) {
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION,
+ encoding);
+ } else if (type == ENCODING_EXT_TYPE_FILE_DESCRIPTOR) {
+ // Read the encoded file descriptor value.
+ FileReference ref;
+ if (const auto error = DeserializeRaw(&ref, reader, start, end)) {
+ return error;
+ }
+
+ return reader->GetInputResourceMapper()->GetFileHandle(ref, value)
+ ? ErrorCode::NO_ERROR
+ : ErrorCode::GET_FILE_DESCRIPTOR_FAILED;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ encoding);
+ }
+}
+
+inline ErrorType DeserializeObject(LocalChannelHandle* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ EncodingExtType type;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeExtType(&encoding, &type, &size, reader, start, end)) {
+ return error;
+ } else if (size != 4) {
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_EXTENSION,
+ encoding);
+ } else if (type == ENCODING_EXT_TYPE_CHANNEL_HANDLE) {
+ // Read the encoded channel handle value.
+ ChannelReference ref;
+ if (const auto error = DeserializeRaw(&ref, reader, start, end)) {
+ return error;
+ }
+ return reader->GetInputResourceMapper()->GetChannelHandle(ref, value)
+ ? ErrorCode::NO_ERROR
+ : ErrorCode::GET_CHANNEL_HANDLE_FAILED;
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_EXTENSION,
+ encoding);
+ }
+}
+
+// Deserializes the type code and size for bin types.
+inline ErrorType DeserializeBinType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (*encoding == ENCODING_TYPE_BIN8) {
+ return DeserializeValue<std::uint8_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_BIN16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_BIN32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_BINARY,
+ *encoding);
+ }
+}
+
+// Overload of DeserializeObject() for BufferWrapper types.
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(
+ BufferWrapper<std::vector<T, Allocator>>* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ const auto value_type_size =
+ sizeof(typename BufferWrapper<std::vector<T, Allocator>>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeBinType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the BufferWrapper to the size of the payload.
+ value->resize(size / value_type_size);
+
+ if (size > value->size() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+template <typename T>
+inline ErrorType DeserializeObject(BufferWrapper<T*>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ const auto value_type_size = sizeof(typename BufferWrapper<T*>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeBinType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the BufferWrapper to the size of the payload.
+ value->resize(size / value_type_size);
+
+ if (size > value->size() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+
+// Deserializes the type code and size for string types.
+inline ErrorType DeserializeStringType(EncodingType* encoding,
+ std::size_t* size, MessageReader* reader,
+ const void*& start, const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixstrEncoding(*encoding)) {
+ *size = GetFixstrSize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_STR8) {
+ return DeserializeValue<std::uint8_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_STR16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_STR32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_STRING,
+ *encoding);
+ }
+}
+
+// Overload of DeserializeObject() for std::string types.
+inline ErrorType DeserializeObject(std::string* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeStringType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size == 0U) {
+ value->clear();
+ return ErrorCode::NO_ERROR;
+ } else {
+ value->resize(size);
+ return ReadRawData(&(*value)[0], reader, start, end, size);
+ }
+}
+
+// Overload of DeserializeObject() for StringWrapper types.
+template <typename T>
+inline ErrorType DeserializeObject(StringWrapper<T>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ const auto value_type_size = sizeof(typename StringWrapper<T>::value_type);
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeStringType(&encoding, &size, reader, start, end))
+ return error;
+
+ // Try to resize the StringWrapper to the size of the payload
+ // string.
+ value->resize(size / value_type_size);
+
+ if (size > value->length() * value_type_size || size % value_type_size != 0) {
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+ } else if (size == 0U) {
+ return ErrorCode::NO_ERROR;
+ } else {
+ return ReadRawData(value->data(), reader, start, end, size);
+ }
+}
+
+// Deserializes the type code and size of array types.
+inline ErrorType DeserializeArrayType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixarrayEncoding(*encoding)) {
+ *size = GetFixarraySize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_ARRAY16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_ARRAY32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_ARRAY,
+ *encoding);
+ }
+}
+
+// Deserializes the type code and size of map types.
+inline ErrorType DeserializeMapType(EncodingType* encoding, std::size_t* size,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ if (const auto error = DeserializeEncoding(encoding, reader, start, end)) {
+ return error;
+ } else if (IsFixmapEncoding(*encoding)) {
+ *size = GetFixmapSize(*encoding);
+ return ErrorCode::NO_ERROR;
+ } else if (*encoding == ENCODING_TYPE_MAP16) {
+ return DeserializeValue<std::uint16_t>(size, reader, start, end);
+ } else if (*encoding == ENCODING_TYPE_MAP32) {
+ return DeserializeValue<std::uint32_t>(size, reader, start, end);
+ } else {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP,
+ *encoding);
+ }
+}
+
+// Overload for std::vector types.
+template <typename T, typename Allocator>
+inline ErrorType DeserializeObject(std::vector<T, Allocator>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end))
+ return error;
+
+ std::vector<T, Allocator> result(size);
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&result[i], reader, start, end))
+ return error;
+ }
+
+ *value = std::move(result);
+ return ErrorCode::NO_ERROR;
+
+// TODO(eieio): Consider the benefits and trade offs of this alternative.
+#if 0
+ value->resize(size);
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+ return ErrorCode::NO_ERROR;
+#endif
+}
+
+// Deserializes an EmptyVariant value.
+inline ErrorType DeserializeObject(EmptyVariant* /*empty*/,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+
+ if (const auto error = DeserializeEncoding(&encoding, reader, start, end)) {
+ return error;
+ } else if (encoding != ENCODING_TYPE_NIL) {
+ return ErrorType(ErrorCode::UNEXPECTED_ENCODING, ENCODING_CLASS_MAP,
+ encoding);
+ } else {
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+// Deserializes a Variant type.
+template <typename... Types>
+inline ErrorType DeserializeObject(Variant<Types...>* variant,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeMapType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ if (size != 1)
+ return ErrorType(ErrorCode::UNEXPECTED_TYPE_SIZE, ENCODING_CLASS_MAP,
+ encoding);
+
+ std::int32_t type;
+ if (const auto error = DeserializeObject(&type, reader, start, end)) {
+ return error;
+ } else if (type < Variant<Types...>::kEmptyIndex ||
+ type >= static_cast<std::int32_t>(sizeof...(Types))) {
+ return ErrorCode::INVALID_VARIANT_ELEMENT;
+ } else {
+ variant->Become(type);
+ return variant->Visit([reader, &start, &end](auto&& value) {
+ return DeserializeObject(&value, reader, start, end);
+ });
+ }
+}
+
+// Deserializes map types.
+template <typename MapType>
+inline ErrorType DeserializeMap(MapType* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeMapType(&encoding, &size, reader, start, end))
+ return error;
+
+ MapType result;
+ for (std::size_t i = 0; i < size; i++) {
+ std::pair<typename MapType::key_type, typename MapType::mapped_type>
+ element;
+ if (const auto error =
+ DeserializeObject(&element.first, reader, start, end))
+ return error;
+ if (const auto error =
+ DeserializeObject(&element.second, reader, start, end))
+ return error;
+ result.emplace(std::move(element));
+ }
+
+ *value = std::move(result);
+ return ErrorCode::NO_ERROR;
+}
+
+// Overload for std::map types.
+template <typename Key, typename T, typename Compare, typename Allocator>
+inline ErrorType DeserializeObject(std::map<Key, T, Compare, Allocator>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ return DeserializeMap(value, reader, start, end);
+}
+
+// Overload for std::unordered_map types.
+template <typename Key, typename T, typename Hash, typename KeyEqual,
+ typename Allocator>
+inline ErrorType DeserializeObject(
+ std::unordered_map<Key, T, Hash, KeyEqual, Allocator>* value,
+ MessageReader* reader, const void*& start, const void*& end) {
+ return DeserializeMap(value, reader, start, end);
+}
+
+// Overload for ArrayWrapper types.
+template <typename T>
+inline ErrorType DeserializeObject(ArrayWrapper<T>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ // Try to resize the wrapper.
+ value->resize(size);
+
+ // Make sure there is enough space in the ArrayWrapper for the
+ // payload.
+ if (size > value->capacity())
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+
+ return ErrorCode::NO_ERROR;
+}
+
+// Overload for std::array types.
+template <typename T, std::size_t Size>
+inline ErrorType DeserializeObject(std::array<T, Size>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ }
+
+ if (size != Size)
+ return ErrorCode::INSUFFICIENT_DESTINATION_SIZE;
+
+ for (std::size_t i = 0; i < size; i++) {
+ if (const auto error = DeserializeObject(&(*value)[i], reader, start, end))
+ return error;
+ }
+
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes std::pair types.
+template <typename T, typename U>
+inline ErrorType DeserializeObject(std::pair<T, U>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != 2) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else if (const auto error =
+ DeserializeObject(&value->first, reader, start, end)) {
+ return error;
+ } else if (const auto error =
+ DeserializeObject(&value->second, reader, start, end)) {
+ return error;
+ } else {
+ return ErrorCode::NO_ERROR;
+ }
+}
+
+// Stops template recursion when the last tuple element is reached.
+template <typename... T>
+inline ErrorType DeserializeTuple(std::tuple<T...>*, MessageReader*,
+ const void*&, const void*, Index<0>) {
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes each element of a tuple recursively.
+template <typename... T, std::size_t index>
+inline ErrorType DeserializeTuple(std::tuple<T...>* tuple,
+ MessageReader* reader, const void*& start,
+ const void*& end, Index<index>) {
+ if (const auto error =
+ DeserializeTuple(tuple, reader, start, end, Index<index - 1>()))
+ return error;
+ else
+ return DeserializeObject(&std::get<index - 1>(*tuple), reader, start, end);
+}
+
+// Overload for standard tuple types.
+template <typename... T>
+inline ErrorType DeserializeObject(std::tuple<T...>* value,
+ MessageReader* reader, const void*& start,
+ const void*& end) {
+ EncodingType encoding;
+ std::size_t size;
+
+ if (const auto error =
+ DeserializeArrayType(&encoding, &size, reader, start, end)) {
+ return error;
+ } else if (size != sizeof...(T)) {
+ return ErrorCode::UNEXPECTED_TYPE_SIZE;
+ } else {
+ return DeserializeTuple(value, reader, start, end, Index<sizeof...(T)>());
+ }
+}
+
+// Stops template recursion when the last member of a Serializable type is
+// reached.
+template <typename Members, typename T>
+inline ErrorType DeserializeMember(T*, MessageReader*, const void*&,
+ const void*, Index<0>) {
+ return ErrorCode::NO_ERROR;
+}
+
+// Deserializes each member of a Serializable type recursively.
+template <typename Members, typename T, std::size_t index>
+inline ErrorType DeserializeMember(T* value, MessageReader* reader,
+ const void*& start, const void*& end,
+ Index<index>) {
+ if (const auto error = DeserializeMember<Members>(value, reader, start, end,
+ Index<index - 1>()))
+ return error;
+ else
+ return DeserializeObject(&Members::template At<index - 1>::Resolve(*value),
+ reader, start, end);
+}
+
+// Deserializes the members of a Serializable type using the given
+// SerializableMembersType type.
+template <typename Members, typename T>
+inline ErrorType DeserializeMembers(T* value, MessageReader* reader,
+ const void*& start, const void*& end) {
+ return DeserializeMember<Members>(value, reader, start, end,
+ Index<Members::MemberCount>());
+}
+
+// Top level deserialization function.
+template <typename T>
+inline ErrorType Deserialize(T* value, MessageReader* reader) {
+ PDX_TRACE_NAME("Deserialize");
+ MessageReader::BufferSection section = reader->GetNextReadBufferSection();
+ if (section.first == section.second)
+ return ErrorCode::INSUFFICIENT_BUFFER;
+ ErrorType error =
+ DeserializeObject(value, reader, section.first, section.second);
+ reader->ConsumeReadBufferSectionData(section.first);
+ return error;
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_SERIALIZATION_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h
new file mode 100644
index 0000000000..19fc4c1323
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h
@@ -0,0 +1,129 @@
+#ifndef ANDROID_PDX_RPC_STRING_WRAPPER_H_
+#define ANDROID_PDX_RPC_STRING_WRAPPER_H_
+
+#include <cstddef>
+#include <cstring>
+#include <string>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Wrapper class for C string buffers, providing an interface suitable for
+// SerializeObject and DeserializeObject. This class serializes to the same
+// format as std::basic_string, and may be substituted for std::basic_string
+// during serialization and deserialization. This substitution makes handling of
+// C strings more efficient by avoiding unnecessary copies when remote method
+// signatures specify std::basic_string arguments or return values.
+template <typename CharT = std::string::value_type,
+ typename Traits = std::char_traits<CharT>>
+class StringWrapper {
+ public:
+ // Define types in the style of STL strings to support STL operators.
+ typedef Traits traits_type;
+ typedef typename Traits::char_type value_type;
+ typedef std::size_t size_type;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+
+ StringWrapper() : buffer_(nullptr), capacity_(0), end_(0) {}
+
+ StringWrapper(pointer buffer, size_type capacity, size_type size)
+ : buffer_(&buffer[0]),
+ capacity_(capacity),
+ end_(capacity < size ? capacity : size) {}
+
+ StringWrapper(pointer buffer, size_type size)
+ : StringWrapper(buffer, size, size) {}
+
+ explicit StringWrapper(pointer buffer)
+ : StringWrapper(buffer, std::strlen(buffer)) {}
+
+ StringWrapper(const StringWrapper& other) { *this = other; }
+
+ StringWrapper(StringWrapper&& other) { *this = std::move(other); }
+
+ StringWrapper& operator=(const StringWrapper& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ }
+
+ return *this;
+ }
+
+ StringWrapper& operator=(StringWrapper&& other) {
+ if (&other == this) {
+ return *this;
+ } else {
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+ end_ = other.end_;
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ other.end_ = 0;
+ }
+
+ return *this;
+ }
+
+ pointer data() { return buffer_; }
+ const_pointer data() const { return buffer_; }
+
+ pointer begin() { return &buffer_[0]; }
+ pointer end() { return &buffer_[end_]; }
+ const_pointer begin() const { return &buffer_[0]; }
+ const_pointer end() const { return &buffer_[end_]; }
+
+ size_type size() const { return end_; }
+ size_type length() const { return end_; }
+ size_type max_size() const { return capacity_; }
+ size_type capacity() const { return capacity_; }
+
+ void resize(size_type size) {
+ if (size <= capacity_)
+ end_ = size;
+ else
+ end_ = capacity_;
+ }
+
+ reference operator[](size_type pos) { return buffer_[pos]; }
+ const_reference operator[](size_type pos) const { return buffer_[pos]; }
+
+ private:
+ pointer buffer_;
+ size_type capacity_;
+ size_type end_;
+};
+
+// Utility functions that infer the underlying type of the string, simplifying
+// the wrapper interface.
+
+// TODO(eieio): Wrapping std::basic_string is here for completeness, but is it
+// useful?
+template <typename T, typename... Any>
+StringWrapper<const T> WrapString(const std::basic_string<T, Any...>& s) {
+ return StringWrapper<const T>(s.c_str(), s.length());
+}
+
+template <typename T, typename SizeType = std::size_t>
+StringWrapper<T> WrapString(T* s, SizeType size) {
+ return StringWrapper<T>(s, size);
+}
+
+template <typename T>
+StringWrapper<T> WrapString(T* s) {
+ return StringWrapper<T>(s);
+}
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_STRING_WRAPPER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h
new file mode 100644
index 0000000000..e5ef2aa787
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/thread_local_buffer.h
@@ -0,0 +1,134 @@
+#ifndef ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
+#define ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/trace.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Utility class to distinguish between different thread local entries or
+// "slots" in the thread local variable table. Each slot is uniquely identified
+// by (T,Index) and is independent of any other slot.
+template <typename T, std::size_t Index>
+struct ThreadLocalSlot;
+
+// Utility class to specify thread local slots using only a type.
+template <typename T>
+struct ThreadLocalTypeSlot;
+
+// Utility class to specify thread local slots using only an index.
+template <std::size_t Index>
+struct ThreadLocalIndexSlot;
+
+// Initial capacity of thread local buffer, unless otherwise specified.
+constexpr std::size_t InitialBufferCapacity = 4096;
+
+// Thread local slots for buffers used by this library to send, receive, and
+// reply to messages.
+using SendBuffer = ThreadLocalIndexSlot<0>;
+using ReceiveBuffer = ThreadLocalIndexSlot<1>;
+using ReplyBuffer = ThreadLocalIndexSlot<2>;
+
+// Provides a simple interface to thread local buffers for large IPC messages.
+// Slot provides multiple thread local slots for a given T, Allocator, Capacity
+// combination.
+template <typename T, typename Allocator = DefaultInitializationAllocator<T>,
+ std::size_t Capacity = InitialBufferCapacity,
+ typename Slot = ThreadLocalSlot<void, 0>>
+class ThreadLocalBuffer {
+ public:
+ using BufferType = std::vector<T, Allocator>;
+ using ValueType = T;
+
+ // Reserves |capacity| number of elements of capacity in the underlying
+ // buffer. Call this during startup to avoid allocation during use.
+ static void Reserve(std::size_t capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::Reserve");
+ InitializeBuffer(capacity);
+ buffer_->reserve(capacity);
+ }
+
+ // Resizes the buffer to |size| elements.
+ static void Resize(std::size_t size) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::Resize");
+ InitializeBuffer(size);
+ buffer_->resize(size);
+ }
+
+ // Gets a reference to the underlying buffer after reserving |capacity|
+ // elements. The current size of the buffer is left intact. The returned
+ // reference is valid until FreeBuffer() is called.
+ static BufferType& GetBuffer(std::size_t capacity = Capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetBuffer");
+ Reserve(capacity);
+ return *buffer_;
+ }
+
+ // Gets a reference to the underlying buffer after reserving |Capacity|
+ // elements. The current size of the buffer is set to zero. The returned
+ // reference is valid until FreeBuffer() is called.
+ static BufferType& GetEmptyBuffer() {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetEmptyBuffer");
+ Reserve(Capacity);
+ buffer_->clear();
+ return *buffer_;
+ }
+
+ // Gets a reference to the underlying buffer after resizing it to |size|
+ // elements. The returned reference is valid until FreeBuffer() is called.
+ static BufferType& GetSizedBuffer(std::size_t size = Capacity) {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetSizedBuffer");
+ Resize(size);
+ return *buffer_;
+ }
+
+ // Frees the underlying buffer. The buffer will be reallocated if any of the
+ // methods above are called.
+ static void FreeBuffer() {
+ if (buffer_) {
+ GetBufferGuard().reset(buffer_ = nullptr);
+ }
+ }
+
+ private:
+ friend class ThreadLocalBufferTest;
+
+ static void InitializeBuffer(std::size_t capacity) {
+ if (!buffer_) {
+ GetBufferGuard().reset(buffer_ = new BufferType(capacity));
+ }
+ }
+
+ // Work around performance issues with thread-local dynamic initialization
+ // semantics by using a normal pointer in parallel with a std::unique_ptr. The
+ // std::unique_ptr is never dereferenced, only assigned, to avoid the high
+ // cost of dynamic initialization checks, while still providing automatic
+ // cleanup. The normal pointer provides fast access to the buffer object.
+ // Never dereference buffer_guard or performance could be severely impacted
+ // by slow implementations of TLS dynamic initialization.
+ static thread_local BufferType* buffer_;
+
+ static std::unique_ptr<BufferType>& GetBufferGuard() {
+ PDX_TRACE_NAME("ThreadLocalBuffer::GetBufferGuard");
+ static thread_local std::unique_ptr<BufferType> buffer_guard;
+ return buffer_guard;
+ }
+};
+
+// Instantiation of the static ThreadLocalBuffer::buffer_ member.
+template <typename T, typename Allocator, std::size_t Capacity, typename Slot>
+thread_local
+ typename ThreadLocalBuffer<T, Allocator, Capacity, Slot>::BufferType*
+ ThreadLocalBuffer<T, Allocator, Capacity, Slot>::buffer_;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/type_operators.h b/libs/vr/libpdx/private/pdx/rpc/type_operators.h
new file mode 100644
index 0000000000..811bd8733c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/type_operators.h
@@ -0,0 +1,195 @@
+#ifndef ANDROID_PDX_RPC_TYPE_OPERATORS_H_
+#define ANDROID_PDX_RPC_TYPE_OPERATORS_H_
+
+#include <array>
+#include <map>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <pdx/rpc/copy_cv_reference.h>
+#include <pdx/rpc/pointer_wrapper.h>
+#include <pdx/rpc/string_wrapper.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Simplifies type expressions.
+template <typename T>
+using Decay = typename std::decay<T>::type;
+
+// Compares the underlying type of A and B.
+template <typename A, typename B>
+using IsEquivalent = typename std::is_same<Decay<A>, Decay<B>>::type;
+
+// Logical AND over template parameter pack.
+template <typename... T>
+struct And : std::false_type {};
+template <typename A, typename B>
+struct And<A, B> : std::integral_constant<bool, A::value && B::value> {};
+template <typename A, typename B, typename... Rest>
+struct And<A, B, Rest...> : And<A, And<B, Rest...>> {};
+
+// Determines whether A is convertible to B (serializes to the same format)
+// using these rules:
+// 1. std:vector<T, Any...> is convertible to ArrayWrapper<T>.
+// 2. ArrayWrapper<T> is convertible to std:vector<T, Any...>.
+// 3. std::basic_string<T, Any...> is convertible to StringWrapper<T>.
+// 4. StringWrapper<T> is convertible to std::basic_string<T, Any...>.
+// 5. BufferWrapper<T*> is convertible to BufferWrapper<std::vector<T,
+// Any...>>.
+// 6. BufferWrapper<std::vector<T, ...>> is convertible to BufferWrapper<T*>.
+// 7. The value type T of A and B must match.
+
+// Compares A and B for convertibility. This base type determines convertibility
+// by equivalence of the underlying types of A and B. Specializations of this
+// type handle the rules for which complex types are convertible.
+template <typename A, typename B>
+struct IsConvertible : IsEquivalent<A, B> {};
+
+// Compares TT<A, ...> and TT<B, ...>; these are convertible if A and B are
+// convertible.
+template <template <typename, typename...> class TT, typename A, typename B,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<TT<A, AnyA...>, TT<B, AnyB...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+
+// Compares TT<KeyA, ValueA, ...> and TT<KeyB, ValueB, ...>; these are
+// convertible if KeyA and KeyB are
+// convertible and ValueA and ValueB are convertible.
+template <template <typename, typename, typename...> class TT, typename KeyA,
+ typename ValueA, typename KeyB, typename ValueB, typename... AnyA,
+ typename... AnyB>
+struct IsConvertible<TT<KeyA, ValueA, AnyA...>, TT<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+
+// Compares two std::pairs to see if the corresponding elements are convertible.
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::pair<A, B>, std::pair<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+
+// Compares std::pair with a two-element std::tuple to see if the corresponding
+// elements are convertible.
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::pair<A, B>, std::tuple<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+template <typename A, typename B, typename C, typename D>
+struct IsConvertible<std::tuple<A, B>, std::pair<C, D>>
+ : And<IsConvertible<Decay<A>, Decay<C>>,
+ IsConvertible<Decay<B>, Decay<D>>> {};
+
+// Compares two std::tuples to see if the corresponding elements are
+// convertible.
+template <typename... A, typename... B>
+struct IsConvertible<std::tuple<A...>, std::tuple<B...>>
+ : And<IsConvertible<Decay<A>, Decay<B>>...> {};
+
+// Compares std::vector, std::array, and ArrayWrapper; these are convertible if
+// the value types are convertible.
+template <typename A, typename B, typename... Any>
+struct IsConvertible<std::vector<A, Any...>, ArrayWrapper<B>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any>
+struct IsConvertible<ArrayWrapper<A>, std::vector<B, Any...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any, std::size_t Size>
+struct IsConvertible<std::vector<A, Any...>, std::array<B, Size>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, typename... Any, std::size_t Size>
+struct IsConvertible<std::array<A, Size>, std::vector<B, Any...>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, std::size_t Size>
+struct IsConvertible<ArrayWrapper<A>, std::array<B, Size>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+template <typename A, typename B, std::size_t Size>
+struct IsConvertible<std::array<A, Size>, ArrayWrapper<B>>
+ : IsConvertible<Decay<A>, Decay<B>> {};
+
+// Compares std::map and std::unordered_map; these are convertible if the keys
+// are convertible and the values are convertible.
+template <typename KeyA, typename ValueA, typename KeyB, typename ValueB,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<std::map<KeyA, ValueA, AnyA...>,
+ std::unordered_map<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+template <typename KeyA, typename ValueA, typename KeyB, typename ValueB,
+ typename... AnyA, typename... AnyB>
+struct IsConvertible<std::unordered_map<KeyA, ValueA, AnyA...>,
+ std::map<KeyB, ValueB, AnyB...>>
+ : And<IsConvertible<Decay<KeyA>, Decay<KeyB>>,
+ IsConvertible<Decay<ValueA>, Decay<ValueB>>> {};
+
+// Compares BufferWrapper<A*> and BufferWrapper<std::vector<B>>; these are
+// convertible if A and B are equivalent. Allocator types are not relevant to
+// convertibility.
+template <typename A, typename B, typename Allocator>
+struct IsConvertible<BufferWrapper<A*>,
+ BufferWrapper<std::vector<B, Allocator>>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B, typename Allocator>
+struct IsConvertible<BufferWrapper<std::vector<A, Allocator>>,
+ BufferWrapper<B*>> : IsEquivalent<A, B> {};
+template <typename A, typename B, typename AllocatorA, typename AllocatorB>
+struct IsConvertible<BufferWrapper<std::vector<A, AllocatorA>>,
+ BufferWrapper<std::vector<B, AllocatorB>>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B>
+struct IsConvertible<BufferWrapper<A*>, BufferWrapper<B*>>
+ : IsEquivalent<A, B> {};
+
+// Compares std::basic_string<A, ...> and StringWrapper<B>; these are
+// convertible if A and B are equivalent.
+template <typename A, typename B, typename... Any>
+struct IsConvertible<std::basic_string<A, Any...>, StringWrapper<B>>
+ : IsEquivalent<A, B> {};
+template <typename A, typename B, typename... Any>
+struct IsConvertible<StringWrapper<A>, std::basic_string<B, Any...>>
+ : IsEquivalent<A, B> {};
+
+// Compares PointerWrapper<A> and B; these are convertible if A and B are
+// convertible.
+template <typename A, typename B>
+struct IsConvertible<PointerWrapper<A>, B> : IsConvertible<Decay<A>, Decay<B>> {
+};
+template <typename A, typename B>
+struct IsConvertible<A, PointerWrapper<B>> : IsConvertible<Decay<A>, Decay<B>> {
+};
+
+// LocalHandle is convertible to RemoteHandle on the service side. This means
+// that a RemoteHandle may be supplied by a service when the protocol calls for
+// a LocalHandle return value. The other way around is not safe and can leak
+// file descriptors. The ServicePayload class enforces this policy by only
+// supporting RemoteHandle for pushed handles.
+template <>
+struct IsConvertible<LocalHandle, RemoteHandle> : std::true_type {};
+template <>
+struct IsConvertible<LocalHandle, BorrowedHandle> : std::true_type {};
+
+template <>
+struct IsConvertible<LocalChannelHandle, RemoteChannelHandle> : std::true_type {
+};
+template <>
+struct IsConvertible<LocalChannelHandle, BorrowedChannelHandle>
+ : std::true_type {};
+
+// Conditionally "rewrites" type A as type B, including cv-reference qualifiers,
+// iff A is convertible to B.
+template <typename A, typename B>
+using ConditionalRewrite =
+ typename std::conditional<IsConvertible<Decay<A>, Decay<B>>::value,
+ CopyCVReferenceType<A, B>, A>::type;
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_RPC_TYPE_OPERATORS_H_
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
new file mode 100644
index 0000000000..cb44a51b8d
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -0,0 +1,686 @@
+#ifndef ANDROID_PDX_RPC_VARIANT_H_
+#define ANDROID_PDX_RPC_VARIANT_H_
+
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+// Type tag denoting an empty variant.
+struct EmptyVariant {};
+
+namespace detail {
+
+// Type for matching tagged overloads.
+template <typename T>
+struct TypeTag {};
+
+// Determines the type of the I-th element of Types....
+template <std::size_t I, typename... Types>
+using TypeForIndex = std::tuple_element_t<I, std::tuple<Types...>>;
+
+// Determines the type tag for the I-th element of Types....
+template <std::size_t I, typename... Types>
+using TypeTagForIndex = TypeTag<TypeForIndex<I, Types...>>;
+
+// Enable if T(Args...) is well formed.
+template <typename R, typename T, typename... Args>
+using EnableIfConstructible =
+ typename std::enable_if<std::is_constructible<T, Args...>::value, R>::type;
+// Enable if T(Args...) is not well formed.
+template <typename R, typename T, typename... Args>
+using EnableIfNotConstructible =
+ typename std::enable_if<!std::is_constructible<T, Args...>::value, R>::type;
+
+// Determines whether T is an element of Types...;
+template <typename... Types>
+struct HasType : std::false_type {};
+template <typename T, typename U>
+struct HasType<T, U> : std::is_same<std::decay_t<T>, std::decay_t<U>> {};
+template <typename T, typename First, typename... Rest>
+struct HasType<T, First, Rest...>
+ : std::integral_constant<bool, HasType<T, First>::value ||
+ HasType<T, Rest...>::value> {};
+
+// Defines set operations on a set of Types...
+template <typename... Types>
+struct Set {
+ // Default specialization catches the empty set, which is always a subset.
+ template <typename...>
+ struct IsSubset : std::true_type {};
+ template <typename T>
+ struct IsSubset<T> : HasType<T, Types...> {};
+ template <typename First, typename... Rest>
+ struct IsSubset<First, Rest...>
+ : std::integral_constant<bool, IsSubset<First>::value &&
+ IsSubset<Rest...>::value> {};
+};
+
+// Determines the number of elements of Types... that are constructible from
+// From.
+template <typename... Types>
+struct ConstructibleCount;
+template <typename From, typename To>
+struct ConstructibleCount<From, To>
+ : std::integral_constant<std::size_t,
+ std::is_constructible<To, From>::value> {};
+template <typename From, typename First, typename... Rest>
+struct ConstructibleCount<From, First, Rest...>
+ : std::integral_constant<std::size_t,
+ std::is_constructible<First, From>::value +
+ ConstructibleCount<From, Rest...>::value> {};
+
+// Enable if T is an element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfElement =
+ typename std::enable_if<HasType<T, Types...>::value, R>::type;
+// Enable if T is not an element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfNotElement =
+ typename std::enable_if<!HasType<T, Types...>::value, R>::type;
+
+// Enable if T is convertible to an element of Types... T is considered
+// convertible IIF a single element of Types... is assignable from T and T is
+// not a direct element of Types...
+template <typename R, typename T, typename... Types>
+using EnableIfConvertible =
+ typename std::enable_if<!HasType<T, Types...>::value &&
+ ConstructibleCount<T, Types...>::value == 1,
+ R>::type;
+
+// Enable if T is assignable to an element of Types... T is considered
+// assignable IFF a single element of Types... is constructible from T or T is a
+// direct element of Types.... Note that T is REQUIRED to be an element of
+// Types... when multiple elements are constructible from T to prevent ambiguity
+// in conversion.
+template <typename R, typename T, typename... Types>
+using EnableIfAssignable =
+ typename std::enable_if<HasType<T, Types...>::value ||
+ ConstructibleCount<T, Types...>::value == 1,
+ R>::type;
+
+// Selects a type for SFINAE constructor selection.
+template <bool CondA, typename SelectA, typename SelectB>
+using Select = std::conditional_t<CondA, SelectA, SelectB>;
+
+// Recursive union type.
+template <typename... Types>
+union Union;
+
+// Specialization handling a singular type, terminating template recursion.
+template <typename Type>
+union Union<Type> {
+ Union() {}
+ ~Union() {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<Type>, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T, typename = EnableIfAssignable<void, T, Type>>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+
+ Type& get(TypeTag<Type>) { return first_; }
+ const Type& get(TypeTag<Type>) const { return first_; }
+ EmptyVariant get(TypeTag<EmptyVariant>) const { return {}; }
+ constexpr std::int32_t index(TypeTag<Type>) const { return 0; }
+
+ template <typename... Args>
+ std::int32_t Construct(TypeTag<Type>, Args&&... args) {
+ new (&first_) Type(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename... Args>
+ EnableIfConstructible<std::int32_t, Type, Args...> Construct(Args&&... args) {
+ new (&first_) Type(std::forward<Args>(args)...);
+ return 0;
+ }
+
+ void Destruct(std::int32_t target_index) {
+ if (target_index == index(TypeTag<Type>{})) {
+ (&get(TypeTag<Type>{}))->~Type();
+ }
+ }
+
+ template <typename T>
+ bool Assign(TypeTag<Type>, std::int32_t target_index, T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T>
+ EnableIfConstructible<bool, Type, T> Assign(std::int32_t target_index,
+ T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T>
+ EnableIfNotConstructible<bool, Type, T> Assign(std::int32_t /*target_index*/,
+ T&& /*value*/) {
+ return false;
+ }
+
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) {
+ if (target_index == index(TypeTag<Type>{}))
+ return std::forward<Op>(op)(get(TypeTag<Type>{}));
+ else
+ return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{}));
+ }
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) const {
+ if (target_index == index(TypeTag<Type>{}))
+ return std::forward<Op>(op)(get(TypeTag<Type>{}));
+ else
+ return std::forward<Op>(op)(get(TypeTag<EmptyVariant>{}));
+ }
+
+ template <typename... Args>
+ bool Become(std::int32_t target_index, Args&&... args) {
+ if (target_index == index(TypeTag<Type>{})) {
+ Construct(TypeTag<Type>{}, std::forward<Args>(args)...);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private:
+ Type first_;
+};
+
+// Specialization that recursively unions types from the paramater pack.
+template <typename First, typename... Rest>
+union Union<First, Rest...> {
+ Union() {}
+ ~Union() {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<First>, T&& value)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T, typename U>
+ Union(std::int32_t index, std::int32_t* index_out, TypeTag<T>, U&& value)
+ : rest_(index + 1, index_out, TypeTag<T>{}, std::forward<U>(value)) {}
+
+ struct FirstType {};
+ struct RestType {};
+ template <typename T>
+ using SelectConstructor =
+ Select<ConstructibleCount<T, First>::value == 1, FirstType, RestType>;
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value)
+ : Union(index, index_out, std::forward<T>(value),
+ SelectConstructor<T>{}) {}
+
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value, FirstType)
+ : first_(std::forward<T>(value)) {
+ *index_out = index;
+ }
+ template <typename T>
+ Union(std::int32_t index, std::int32_t* index_out, T&& value, RestType)
+ : rest_(index + 1, index_out, std::forward<T>(value)) {}
+
+ First& get(TypeTag<First>) { return first_; }
+ const First& get(TypeTag<First>) const { return first_; }
+ constexpr std::int32_t index(TypeTag<First>) const { return 0; }
+
+ template <typename T>
+ T& get(TypeTag<T>) {
+ return rest_.template get(TypeTag<T>{});
+ }
+ template <typename T>
+ const T& get(TypeTag<T>) const {
+ return rest_.template get(TypeTag<T>{});
+ }
+ template <typename T>
+ constexpr std::int32_t index(TypeTag<T>) const {
+ return 1 + rest_.template index(TypeTag<T>{});
+ }
+
+ template <typename... Args>
+ std::int32_t Construct(TypeTag<First>, Args&&... args) {
+ new (&first_) First(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename T, typename... Args>
+ std::int32_t Construct(TypeTag<T>, Args&&... args) {
+ return 1 +
+ rest_.template Construct(TypeTag<T>{}, std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ EnableIfConstructible<std::int32_t, First, Args...> Construct(
+ Args&&... args) {
+ new (&first_) First(std::forward<Args>(args)...);
+ return 0;
+ }
+ template <typename... Args>
+ EnableIfNotConstructible<std::int32_t, First, Args...> Construct(
+ Args&&... args) {
+ return 1 + rest_.template Construct(std::forward<Args>(args)...);
+ }
+
+ void Destruct(std::int32_t target_index) {
+ if (target_index == index(TypeTag<First>{})) {
+ (get(TypeTag<First>{})).~First();
+ } else {
+ rest_.Destruct(target_index - 1);
+ }
+ }
+
+ template <typename T>
+ bool Assign(TypeTag<First>, std::int32_t target_index, T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ template <typename T, typename U>
+ bool Assign(TypeTag<T>, std::int32_t target_index, U&& value) {
+ return rest_.Assign(TypeTag<T>{}, target_index - 1, std::forward<U>(value));
+ }
+ template <typename T>
+ EnableIfConstructible<bool, First, T> Assign(std::int32_t target_index,
+ T&& value) {
+ if (target_index == 0) {
+ first_ = std::forward<T>(value);
+ return true;
+ } else {
+ return rest_.Assign(target_index - 1, std::forward<T>(value));
+ }
+ }
+ template <typename T>
+ EnableIfNotConstructible<bool, First, T> Assign(std::int32_t target_index,
+ T&& value) {
+ return rest_.Assign(target_index - 1, std::forward<T>(value));
+ }
+
+ // Recursively traverses the union and calls Op on the active value when the
+ // active type is found. If the union is empty Op is called on EmptyVariant.
+ // TODO(eieio): This could be refactored into an array or jump table. It's
+ // unclear whether this would be more efficient for practical variant arity.
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) {
+ if (target_index == index(TypeTag<First>{}))
+ return std::forward<Op>(op)(get(TypeTag<First>{}));
+ else
+ return rest_.Visit(target_index - 1, std::forward<Op>(op));
+ }
+ template <typename Op>
+ decltype(auto) Visit(std::int32_t target_index, Op&& op) const {
+ if (target_index == index(TypeTag<First>{}))
+ return std::forward<Op>(op)(get(TypeTag<First>{}));
+ else
+ return rest_.Visit(target_index - 1, std::forward<Op>(op));
+ }
+
+ template <typename... Args>
+ bool Become(std::int32_t target_index, Args&&... args) {
+ if (target_index == index(TypeTag<First>{})) {
+ Construct(TypeTag<First>{}, std::forward<Args>(args)...);
+ return true;
+ } else {
+ return rest_.Become(target_index - 1, std::forward<Args>(args)...);
+ }
+ }
+
+ private:
+ First first_;
+ Union<Rest...> rest_;
+};
+
+} // namespace detail
+
+template <typename... Types>
+class Variant {
+ private:
+ // Convenience types.
+ template <typename T>
+ using TypeTag = detail::TypeTag<T>;
+ template <typename T>
+ using DecayedTypeTag = TypeTag<std::decay_t<T>>;
+ template <std::size_t I>
+ using TypeForIndex = detail::TypeForIndex<I, Types...>;
+ template <std::size_t I>
+ using TypeTagForIndex = detail::TypeTagForIndex<I, Types...>;
+ template <typename T>
+ using HasType = detail::HasType<T, Types...>;
+ template <typename R, typename T>
+ using EnableIfElement = detail::EnableIfElement<R, T, Types...>;
+ template <typename R, typename T>
+ using EnableIfConvertible = detail::EnableIfConvertible<R, T, Types...>;
+ template <typename R, typename T>
+ using EnableIfAssignable = detail::EnableIfAssignable<R, T, Types...>;
+
+ struct Direct {};
+ struct Convert {};
+ template <typename T>
+ using SelectConstructor = detail::Select<HasType<T>::value, Direct, Convert>;
+
+ // Constructs by type tag when T is an direct element of Types...
+ template <typename T>
+ explicit Variant(T&& value, Direct)
+ : value_(0, &index_, DecayedTypeTag<T>{}, std::forward<T>(value)) {}
+ // Conversion constructor when T is not a direct element of Types...
+ template <typename T>
+ explicit Variant(T&& value, Convert)
+ : value_(0, &index_, std::forward<T>(value)) {}
+
+ public:
+ // Variants are default construcible, regardless of whether the elements are
+ // default constructible. Default consruction yields an empty Variant.
+ Variant() {}
+ explicit Variant(EmptyVariant) {}
+ ~Variant() { Destruct(); }
+
+ // Copy and move construction from Variant types. Each element of OtherTypes
+ // must be convertible to an element of Types.
+ template <typename... OtherTypes>
+ explicit Variant(const Variant<OtherTypes...>& other) {
+ other.Visit([this](const auto& value) { Construct(value); });
+ }
+ template <typename... OtherTypes>
+ explicit Variant(Variant<OtherTypes...>&& other) {
+ other.Visit([this](auto&& value) { Construct(std::move(value)); });
+ }
+
+ // Construction from non-Variant types.
+ template <typename T, typename = EnableIfAssignable<void, T>>
+ explicit Variant(T&& value)
+ : Variant(std::forward<T>(value), SelectConstructor<T>{}) {}
+
+ // Performs assignment from type T belonging to Types. This overload takes
+ // priority to prevent implicit conversion in cases where T is implicitly
+ // convertible to multiple elements of Types.
+ template <typename T>
+ EnableIfElement<Variant&, T> operator=(T&& value) {
+ Assign(DecayedTypeTag<T>{}, std::forward<T>(value));
+ return *this;
+ }
+
+ // Performs assignment from type T not belonging to Types. This overload
+ // matches in cases where conversion is the only viable option.
+ template <typename T>
+ EnableIfConvertible<Variant&, T> operator=(T&& value) {
+ Assign(std::forward<T>(value));
+ return *this;
+ }
+
+ // Handles assignment from the empty type. This overload supports assignment
+ // in visitors using generic lambdas.
+ Variant& operator=(EmptyVariant) {
+ Assign(EmptyVariant{});
+ return *this;
+ }
+
+ // Assignment from Variant types. Each element of OtherTypes must be
+ // convertible to an element of Types. Forwards through non-Variant assignment
+ // operators to apply conversion checks.
+ template <typename... OtherTypes>
+ Variant& operator=(const Variant<OtherTypes...>& other) {
+ other.Visit([this](const auto& value) { *this = value; });
+ return *this;
+ }
+ template <typename... OtherTypes>
+ Variant& operator=(Variant<OtherTypes...>&& other) {
+ other.Visit([this](auto&& value) { *this = std::move(value); });
+ return *this;
+ }
+
+ // Becomes the target type, constructing a new element from the given
+ // arguments if necessary. No action is taken if the active element is already
+ // the target type. Otherwise the active element is destroyed and replaced by
+ // constructing an element of the new type using |Args|. An invalid target
+ // type index results in an empty Variant.
+ template <typename... Args>
+ void Become(std::int32_t target_index, Args&&... args) {
+ if (target_index != index()) {
+ Destruct();
+ index_ = value_.Become(target_index, std::forward<Args>(args)...)
+ ? target_index
+ : kEmptyIndex;
+ }
+ }
+
+ // Invokes |Op| on the active element. If the Variant is empty |Op| is invoked
+ // on EmptyVariant.
+ template <typename Op>
+ decltype(auto) Visit(Op&& op) {
+ return value_.Visit(index_, std::forward<Op>(op));
+ }
+ template <typename Op>
+ decltype(auto) Visit(Op&& op) const {
+ return value_.Visit(index_, std::forward<Op>(op));
+ }
+
+ // Index returned when the Variant is empty.
+ enum : std::int32_t { kEmptyIndex = -1 };
+
+ // Returns the index of the given type.
+ template <typename T>
+ constexpr std::int32_t index_of() const {
+ static_assert(HasType<T>::value, "T is not an element type of Variant.");
+ return value_.template index(DecayedTypeTag<T>{});
+ }
+
+ // Returns the index of the active type. If the Variant is empty -1 is
+ // returned.
+ std::int32_t index() const { return index_; }
+
+ // Returns true if the given type is active, false otherwise.
+ template <typename T>
+ bool is() const {
+ static_assert(HasType<T>::value, "T is not an element type of Variant.");
+ return index() == index_of<T>();
+ }
+
+ // Returns true if the Variant is empty, false otherwise.
+ bool empty() const { return index() == kEmptyIndex; }
+
+ // Element accessors. Returns a pointer to the active value if the given
+ // type/index is active, otherwise nullptr is returned.
+ template <typename T>
+ T* get() {
+ if (is<T>())
+ return &value_.template get(DecayedTypeTag<T>{});
+ else
+ return nullptr;
+ }
+ template <typename T>
+ const T* get() const {
+ if (is<T>())
+ return &value_.template get(DecayedTypeTag<T>{});
+ else
+ return nullptr;
+ }
+ template <std::size_t I>
+ TypeForIndex<I>* get() {
+ if (is<TypeForIndex<I>>())
+ return &value_.template get(TypeTagForIndex<I>{});
+ else
+ return nullptr;
+ }
+ template <std::size_t I>
+ const TypeForIndex<I>* get() const {
+ if (is<TypeForIndex<I>>())
+ return &value_.template get(TypeTagForIndex<I>{});
+ else
+ return nullptr;
+ }
+
+ private:
+ std::int32_t index_ = kEmptyIndex;
+ detail::Union<std::decay_t<Types>...> value_;
+
+ // Constructs an element from the given arguments and sets the Variant to the
+ // resulting type.
+ template <typename... Args>
+ void Construct(Args&&... args) {
+ index_ = value_.template Construct(std::forward<Args>(args)...);
+ }
+ void Construct(EmptyVariant) {}
+
+ // Destroys the active element of the Variant.
+ void Destruct() { value_.Destruct(index_); }
+
+ // Assigns the Variant when non-empty and the current type matches the target
+ // type, otherwise destroys the current value and constructs a element of the
+ // new type. Tagged assignment is used when T is an element of the Variant to
+ // prevent implicit conversion in cases where T is implicitly convertible to
+ // multiple element types.
+ template <typename T, typename U>
+ void Assign(TypeTag<T>, U&& value) {
+ if (!value_.template Assign(TypeTag<T>{}, index_, std::forward<U>(value))) {
+ Destruct();
+ Construct(TypeTag<T>{}, std::forward<U>(value));
+ }
+ }
+ template <typename T>
+ void Assign(T&& value) {
+ if (!value_.template Assign(index_, std::forward<T>(value))) {
+ Destruct();
+ Construct(std::forward<T>(value));
+ }
+ }
+ // Handles assignment from an empty Variant.
+ void Assign(EmptyVariant) { Destruct(); }
+};
+
+// Utility type to extract/convert values from a variant. This class simplifies
+// conditional logic to get/move/swap/action values from a variant when one or
+// more elements are compatible with the destination type.
+//
+// Example:
+// Variant<int, bool, std::string> v(10);
+// bool bool_value;
+// if (IfAnyOf<int, bool>::Get(v, &bool_value)) {
+// DoSomething(bool_value);
+// } else {
+// HandleInvalidType();
+// }
+// IfAnyOf<int>::Call(v, [](const auto& value) { DoSomething(value); });
+//
+template <typename... ValidTypes>
+struct IfAnyOf {
+ // Calls Op on the underlying value of the variant and returns true when the
+ // variant is a valid type, otherwise does nothing and returns false.
+ template <typename Op, typename... Types>
+ static bool Call(Variant<Types...>* variant, Op&& op) {
+ static_assert(
+ detail::Set<Types...>::template IsSubset<ValidTypes...>::value,
+ "ValidTypes may only contain element types from the Variant.");
+ return variant->Visit(CallOp<Op>{std::forward<Op>(op)});
+ }
+ template <typename Op, typename... Types>
+ static bool Call(const Variant<Types...>* variant, Op&& op) {
+ static_assert(
+ detail::Set<Types...>::template IsSubset<ValidTypes...>::value,
+ "ValidTypes may only contain element types from the Variant.");
+ return variant->Visit(CallOp<Op>{std::forward<Op>(op)});
+ }
+
+ // Gets/converts the underlying value of the variant to type T and returns
+ // true when the variant is a valid type, otherwise does nothing and returns
+ // false.
+ template <typename T, typename... Types>
+ static bool Get(const Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](const auto& value) { *value_out = value; });
+ }
+
+ // Moves the underlying value of the variant and returns true when the variant
+ // is a valid type, otherwise does nothing and returns false.
+ template <typename T, typename... Types>
+ static bool Take(Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](auto&& value) { *value_out = std::move(value); });
+ }
+
+ // Swaps the underlying value of the variant with |*value_out| and returns
+ // true when the variant is a valid type, otherwise does nothing and returns
+ // false.
+ template <typename T, typename... Types>
+ static bool Swap(Variant<Types...>* variant, T* value_out) {
+ return Call(variant,
+ [value_out](auto&& value) { std::swap(*value_out, value); });
+ }
+
+ private:
+ template <typename Op>
+ struct CallOp {
+ Op&& op;
+ template <typename U>
+ detail::EnableIfNotElement<bool, U, ValidTypes...> operator()(U&&) {
+ return false;
+ }
+ template <typename U>
+ detail::EnableIfElement<bool, U, ValidTypes...> operator()(const U& value) {
+ std::forward<Op>(op)(value);
+ return true;
+ }
+ template <typename U>
+ detail::EnableIfElement<bool, U, ValidTypes...> operator()(U&& value) {
+ std::forward<Op>(op)(std::forward<U>(value));
+ return true;
+ }
+ };
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+// Overloads of std::get<T> and std::get<I> for android::pdx::rpc::Variant.
+namespace std {
+
+template <typename T, typename... Types>
+inline T& get(::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<T>();
+}
+template <typename T, typename... Types>
+inline T&& get(::android::pdx::rpc::Variant<Types...>&& v) {
+ return std::move(*v.template get<T>());
+}
+template <typename T, typename... Types>
+inline const T& get(const ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<T>();
+}
+template <std::size_t I, typename... Types>
+inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get(
+ ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<I>();
+}
+template <std::size_t I, typename... Types>
+inline ::android::pdx::rpc::detail::TypeForIndex<I, Types...>&& get(
+ ::android::pdx::rpc::Variant<Types...>&& v) {
+ return std::move(*v.template get<I>());
+}
+template <std::size_t I, typename... Types>
+inline const ::android::pdx::rpc::detail::TypeForIndex<I, Types...>& get(
+ const ::android::pdx::rpc::Variant<Types...>& v) {
+ return *v.template get<I>();
+}
+
+} // namespace std
+
+#endif // ANDROID_PDX_RPC_VARIANT_H_
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
new file mode 100644
index 0000000000..0d30614562
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -0,0 +1,742 @@
+#ifndef ANDROID_PDX_SERVICE_H_
+#define ANDROID_PDX_SERVICE_H_
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "pdx/channel_handle.h"
+#include "pdx/file_handle.h"
+#include "pdx/message_reader.h"
+#include "pdx/message_writer.h"
+#include "pdx/service_endpoint.h"
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+namespace opcodes {
+
+/*
+ * Reserved message opcodes used by libpdx. The reserved opcodes start at the
+ * max positive signed integer for the system and go down.
+ * In contrast, service opcodes start at zero and go up. This scheme leaves
+ * most of the positive integer space for services, a tiny fraction of the
+ * positive integer space for the framework, and the entire negative integer
+ * space for the kernel.
+ */
+#define PDX_OPCODE(name, n) name = ((-1U >> 1) - (n)) // 0x7fff..ffff - n
+
+enum {
+ // System message sent when a new client channel is open.
+ CHANNEL_OPEN = -1,
+ // System message sent when a channel is closed.
+ CHANNEL_CLOSE = -2,
+ // Request the service to reload system properties.
+ PDX_OPCODE(REPORT_SYSPROP_CHANGE, 0),
+ // Request the service to dump state and return it in a text buffer.
+ PDX_OPCODE(DUMP_STATE, 1),
+};
+
+} // namespace opcodes
+
+/*
+ * Base class of service-side per-channel context classes.
+ */
+class Channel : public std::enable_shared_from_this<Channel> {
+ public:
+ Channel() {}
+ virtual ~Channel() {}
+
+ /*
+ * Utility to get a shared_ptr reference from the channel context pointer.
+ */
+ static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info);
+};
+
+/*
+ * Message class represents an RPC message, and implicitly the blocked sender
+ * waiting for a response. Every message should get a reply, at some point
+ * (unless the endpoint is closed), to prevent clients from blocking
+ * indefinitely. In order to enforce this and prevent leaking message ids,
+ * Message automatically replies with an error to the client on destruction,
+ * unless one of two things happens:
+ *
+ * 1. The service calls one of the reply methods before the Message is
+ * destroyed.
+ * 2. The responsibility for the message is moved to another instance of
+ * Message, using either move construction or move assignment.
+ *
+ * The second case is useful for services that need to delay responding to a
+ * sender until a later time. In this situation the service can move the
+ * Message to another instance in a suitable data structure for later use. The
+ * moved-to Message then takes on the same behavior and responsibilities
+ * described above.
+ */
+class Message : public OutputResourceMapper, public InputResourceMapper {
+ public:
+ Message();
+ Message(const MessageInfo& info);
+ ~Message();
+
+ /*
+ * Message objects support move construction and assignment.
+ */
+ Message(Message&& other);
+ Message& operator=(Message&& other);
+
+ /*
+ * Read/write payload, in either single buffer or iovec form.
+ */
+ Status<size_t> ReadVector(const iovec* vector, size_t vector_length);
+ Status<size_t> Read(void* buffer, size_t length);
+ Status<size_t> WriteVector(const iovec* vector, size_t vector_length);
+ Status<size_t> Write(const void* buffer, size_t length);
+
+ template <size_t N>
+ inline Status<size_t> ReadVector(const iovec (&vector)[N]) {
+ return ReadVector(vector, N);
+ }
+
+ template <size_t N>
+ inline Status<size_t> WriteVector(const iovec (&vector)[N]) {
+ return WriteVector(vector, N);
+ }
+
+ // Helper functions to read/write all requested bytes, and return EIO if not
+ // all were read/written.
+ Status<void> ReadVectorAll(const iovec* vector, size_t vector_length);
+ Status<void> WriteVectorAll(const iovec* vector, size_t vector_length);
+
+ inline Status<void> ReadAll(void* buffer, size_t length) {
+ Status<size_t> status = Read(buffer, length);
+ if (status && status.get() < length)
+ status.SetError(EIO);
+ Status<void> ret;
+ ret.PropagateError(status);
+ return ret;
+ }
+ inline Status<void> WriteAll(const void* buffer, size_t length) {
+ Status<size_t> status = Write(buffer, length);
+ if (status && status.get() < length)
+ status.SetError(EIO);
+ Status<void> ret;
+ ret.PropagateError(status);
+ return ret;
+ }
+
+ template <size_t N>
+ inline Status<void> ReadVectorAll(const iovec (&vector)[N]) {
+ return ReadVectorAll(vector, N);
+ }
+
+ template <size_t N>
+ inline Status<void> WriteVectorAll(const iovec (&vector)[N]) {
+ return WriteVectorAll(vector, N);
+ }
+
+ // OutputResourceMapper
+ Status<FileReference> PushFileHandle(const LocalHandle& handle) override;
+ Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override;
+ Status<FileReference> PushFileHandle(const RemoteHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const LocalChannelHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const BorrowedChannelHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const RemoteChannelHandle& handle) override;
+
+ // InputResourceMapper
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override;
+
+ /*
+ * Various ways to reply to a message.
+ */
+ Status<void> Reply(int return_code);
+ Status<void> ReplyError(unsigned int error);
+ Status<void> ReplyFileDescriptor(unsigned int fd);
+ Status<void> Reply(const LocalHandle& handle);
+ Status<void> Reply(const BorrowedHandle& handle);
+ Status<void> Reply(const RemoteHandle& handle);
+ Status<void> Reply(const LocalChannelHandle& handle);
+ Status<void> Reply(const BorrowedChannelHandle& handle);
+ Status<void> Reply(const RemoteChannelHandle& handle);
+
+ template <typename T>
+ inline Status<void> Reply(const Status<T>& status) {
+ return status ? Reply(status.get()) : ReplyError(status.error());
+ }
+
+ inline Status<void> Reply(const Status<void>& status) {
+ return status ? Reply(0) : ReplyError(status.error());
+ }
+
+ /*
+ * Update the channel event bits with the given clear and set masks.
+ */
+ Status<void> ModifyChannelEvents(int clear_mask, int set_mask);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the client. See
+ * Service::PushChannel() for a detail description of this method's operation.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ int flags, const std::shared_ptr<Channel>& channel, int* channel_id);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the client. See
+ * Service::PushChannel() for a detail description of this method's operation.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ Service* service, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id);
+
+ /*
+ * Check whether the |ref| is a reference to channel to this service.
+ * If the channel reference in question is valid, the Channel object is
+ * returned in |channel| when non-nullptr.
+ *
+ * Return values:
+ * channel_id - id of the channel if the |ref| is a valid reference to
+ * this service's channel.
+ * Errors:
+ * EOPNOTSUPP - the file descriptor is not a channel or is a channel to
+ * another service.
+ * EBADF - the file descriptor is invalid.
+ * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid
+ * memory addresses.
+ * EINVAL - the value of |ref| is invalid or the message id for this
+ * message is no longer valid.
+ */
+ Status<int> CheckChannel(ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that checks whether the channel reference is for
+ * a channel to the service |service|.
+ */
+ Status<int> CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that automatically converts to shared pointers
+ * to types derived from Channel.
+ */
+ template <class C>
+ Status<int> CheckChannel(ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ template <class C>
+ Status<int> CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(service, ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ /*
+ * MessageInfo accessors.
+ */
+ pid_t GetProcessId() const;
+ pid_t GetThreadId() const;
+ uid_t GetEffectiveUserId() const;
+ gid_t GetEffectiveGroupId() const;
+ int GetChannelId() const;
+ int GetMessageId() const;
+ int GetOp() const;
+ int GetFlags() const;
+ size_t GetSendLength() const;
+ size_t GetReceiveLength() const;
+ size_t GetFileDescriptorCount() const;
+
+ /*
+ * Impulses are asynchronous and cannot be replied to. All impulses have this
+ * invalid message id.
+ */
+ enum { IMPULSE_MESSAGE_ID = -1 };
+
+ /*
+ * Returns true if this Message describes an asynchronous "impulse" message.
+ */
+ bool IsImpulse() const { return GetMessageId() == IMPULSE_MESSAGE_ID; }
+
+ /*
+ * Returns a pointer to the impulse payload. Impulses are a maximum of 32
+ * bytes in size and the start of the impulse payload is guaranteed to be
+ * 8-byte aligned. Use GetSendLength() to determine the payload size.
+ */
+ const std::uint8_t* ImpulseBegin() const;
+
+ /*
+ * Returns one byte past the end of the impulse payload, as conventional for
+ * STL iterators.
+ */
+ const std::uint8_t* ImpulseEnd() const;
+
+ /*
+ * Get/set the Channel object for the channel associated
+ * with this message. It is up to the caller to synchronize
+ * these in multi-threaded services.
+ */
+ std::shared_ptr<Channel> GetChannel() const;
+ Status<void> SetChannel(const std::shared_ptr<Channel>& channnel);
+
+ /*
+ * Get the Channel object for the channel associated with this message,
+ * automatically converted to the desired subclass of Channel.
+ */
+ template <class C>
+ std::shared_ptr<C> GetChannel() const {
+ return std::static_pointer_cast<C>(GetChannel());
+ }
+
+ /*
+ * Gets the service this message was received on. Returns nullptr if the
+ * service was destroyed.
+ */
+ std::shared_ptr<Service> GetService() const;
+
+ /*
+ * Raw access to the MessageInfo for this message.
+ */
+ const MessageInfo& GetInfo() const;
+
+ bool replied() const { return replied_; }
+ bool IsChannelExpired() const { return channel_.expired(); }
+ bool IsServiceExpired() const { return service_.expired(); }
+
+ /*
+ * Returns true if the message is non-empty; that is the message can be
+ * replied to using this instance.
+ */
+ explicit operator bool() const { return !replied_; }
+
+ const void* GetState() const { return state_; }
+ void* GetState() { return state_; }
+
+ private:
+ friend class Service;
+
+ Message(const Message&) = delete;
+ void operator=(const Message&) = delete;
+ void Destroy();
+
+ std::weak_ptr<Service> service_;
+ std::weak_ptr<Channel> channel_;
+ MessageInfo info_;
+ void* state_{nullptr};
+ bool replied_;
+};
+
+// Base class for RPC services.
+class Service : public std::enable_shared_from_this<Service> {
+ public:
+ Service(const std::string& name, std::unique_ptr<Endpoint> endpoint);
+ virtual ~Service();
+
+ /*
+ * Utility to get a shared_ptr reference from the service context pointer.
+ */
+ static std::shared_ptr<Service> GetFromMessageInfo(const MessageInfo& info);
+
+ /*
+ * Returns whether initialization was successful. Subclasses that override
+ * this must call this base method and AND the results with their own. This
+ * method is not intended to do any initialization work itself, only to
+ * signal success or failure.
+ */
+ virtual bool IsInitialized() const;
+
+ /*
+ * Called by defaultHandleMessage in response to a CHANNEL_OPEN message.
+ * This gives subclasses of Service a convenient hook to create per-channel
+ * context in the form of a Channel subclass.
+ *
+ * The Channel instance returned by this is used to set the channel context
+ * pointer for the channel that was just opened.
+ */
+ virtual std::shared_ptr<Channel> OnChannelOpen(Message& message);
+
+ /*
+ * Called by defaultHandleMessage in response to a CHANNEL_CLOSE message.
+ * This give subclasses of Service a convenient hook to clean up per-channel
+ * context.
+ */
+ virtual void OnChannelClose(Message& message,
+ const std::shared_ptr<Channel>& channel);
+
+ /*
+ * Set the channel context for the given channel. This keeps a reference to
+ * the Channel object until the channel is closed or another call replaces
+ * the current value.
+ */
+ Status<void> SetChannel(int channel_id,
+ const std::shared_ptr<Channel>& channel);
+
+ /*
+ * Get the channel context for the given channel id. This method should be
+ * used sparingly because of the performance characteristics of the underlying
+ * map; it is intended for limited, non-critical path access from outside of
+ * message dispatch. In most cases lookup by id should be unnecessary in a
+ * properly designed service; Message::GetChannel() should be used instead
+ * whenever an operation is in the context of a message.
+ *
+ * Again, if you lookup a channel context object for a service by id in a
+ * message handling path for the same service, you're probably doing something
+ * wrong.
+ */
+ std::shared_ptr<Channel> GetChannel(int channel_id) const;
+
+ /*
+ * Get a snapshot of the active channels for this service. This is the
+ * preferred way to access the set of channels because it avoids potential
+ * deadlocks and race conditions that may occur when operating on the channel
+ * map directly. However, it is more expensive than direct iteration because
+ * of dynamic memory allocation and shared pointer reference costs.
+ *
+ * Automatically converts returned items to shared pointers of the type
+ * std::shared_ptr<C>, where C is the subclass of Channel used by the service.
+ */
+ template <class C>
+ std::vector<std::shared_ptr<C>> GetChannels() const {
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+ std::vector<std::shared_ptr<C>> items;
+ items.reserve(channels_.size());
+
+ for (const auto& pair : channels_) {
+ items.push_back(std::static_pointer_cast<C>(pair.second));
+ }
+
+ return items;
+ }
+
+ /*
+ * Close a channel, signaling the client file object and freeing the channel
+ * id. Once closed, the client side of the channel always returns the error
+ * ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+ *
+ * The internal reference to the Channel instance associated with the channel
+ * is removed, which may result in the Channel object being freed.
+ *
+ * OnChannelClosed is not called in response to this method call.
+ */
+ Status<void> CloseChannel(int channel_id);
+
+ /*
+ * Update the event bits for the given channel (given by id), using the
+ * given clear and set masks.
+ *
+ * This is useful for asynchronously signaling events that clients may be
+ * waiting for using select/poll/epoll.
+ */
+ Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask);
+
+ /*
+ * Create a new channel and push it as a file descriptor to the process
+ * sending the |message|. |flags| may be set to O_NONBLOCK and/or
+ * O_CLOEXEC to control the initial behavior of the new file descriptor (the
+ * sending process may change these later using fcntl()). The internal Channel
+ * instance associated with this channel is set to |channel|, which may be
+ * nullptr. The new channel id allocated for this channel is returned in
+ * |channel_id|, which may also be nullptr if not needed.
+ *
+ * On success, returns the remote channel handle for the new channel in the
+ * sending process' handle space. This MUST be returned to the sender via
+ * Message::Reply(), Message::Write(), or Message::WriteVector().
+ *
+ * On error, returns an errno code describing the cause of the error.
+ *
+ * Service::OnChannelCreate() is not called in response to the creation of the
+ * new channel.
+ */
+ Status<RemoteChannelHandle> PushChannel(
+ Message* message, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id);
+
+ /*
+ * Check whether the |ref| is a reference to a channel to this service.
+ * If the channel reference in question is valid, the Channel object is
+ * returned in |channel| when non-nullptr.
+ *
+ * Return values:
+ * channel_id - id of the channel if the channel reference.
+ * Errors:
+ * EOPNOTSUPP - the file descriptor is not a channel or is a channel to
+ * another service.
+ * EBADF - the file descriptor is invalid.
+ * FAULT - |channel_id| or |channel| are non-nullptr and point to invalid
+ * memory addresses.
+ * EINVAL - the value of |ref| is invalid or the message id for this
+ * message is no longer valid.
+ */
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const;
+
+ /*
+ * Overload of CheckChannel() that automatically converts to shared pointers
+ * of types derived from Channel.
+ */
+ template <class C>
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<C>* channel) const {
+ std::shared_ptr<Channel> base_pointer;
+ const Status<int> ret =
+ CheckChannel(message, ref, channel ? &base_pointer : nullptr);
+ if (channel)
+ *channel = std::static_pointer_cast<C>(base_pointer);
+ return ret;
+ }
+
+ /*
+ * Handle a message. Subclasses override this to receive messages and decide
+ * how to dispatch them.
+ *
+ * The default implementation simply calls defaultHandleMessage().
+ * Subclasses should call the same for any unrecognized message opcodes.
+ */
+ virtual Status<void> HandleMessage(Message& message);
+
+ /*
+ * Handle an asynchronous message. Subclasses override this to receive
+ * asynchronous "impulse" messages. Impulses have a limited-size payload that
+ * is transferred upfront with the message description.
+ */
+ virtual void HandleImpulse(Message& impulse);
+
+ /*
+ * The default message handler. It is important that all messages
+ * (eventually) get a reply. This method should be called by subclasses for
+ * any unrecognized opcodes or otherwise unhandled messages to prevent
+ * erroneous requests from blocking indefinitely.
+ *
+ * Provides default handling of CHANNEL_OPEN and CHANNEL_CLOSE, calling
+ * OnChannelOpen() and OnChannelClose(), respectively.
+ *
+ * For all other message opcodes, this method replies with ENOTSUP.
+ */
+ Status<void> DefaultHandleMessage(Message& message);
+
+ /*
+ * Called when system properties have changed. Subclasses should implement
+ * this method if they need to handle when system properties change.
+ */
+ virtual void OnSysPropChange();
+
+ /*
+ * Get the endpoint for the service.
+ */
+ Endpoint* endpoint() const { return endpoint_.get(); }
+
+ /*
+ * Cancels the endpoint, unblocking any receiver threads waiting in
+ * ReceiveAndDispatch().
+ */
+ Status<void> Cancel();
+
+ /*
+ * Iterator type for Channel map iterators.
+ */
+ using ChannelIterator =
+ std::unordered_map<int, std::shared_ptr<Channel>>::iterator;
+
+ /*
+ * Iterates over the Channel map and performs the action given by |action| on
+ * each channel map item (const ChannelIterator::value_type).
+ * |channels_mutex_| is not held; it is the responsibility of the caller to
+ * ensure serialization between threads that modify or iterate over the
+ * Channel map.
+ */
+ template <class A>
+ void ForEachChannelUnlocked(A action) const {
+ std::for_each(channels_.begin(), channels_.end(), action);
+ }
+
+ /*
+ * Iterates over the Channel map and performs the action given by |action| on
+ * each channel map item (const ChannelIterator::value_type).
+ * |channels_mutex_| is held to serialize access to the map; care must be
+ * taken to avoid recursively acquiring the mutex, for example, by calling
+ * Service::{GetChannel,SetChannel,CloseChannel,PushChannel}() or
+ * Message::SetChannel() in the action.
+ */
+ template <class A>
+ void ForEachChannel(A action) const {
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+ ForEachChannelUnlocked(action);
+ }
+
+ /*
+ * Subclasses of Service may override this method to provide a text string
+ * describing the state of the service. This method is called by
+ * HandleSystemMessage in response to the standard
+ * DUMP_STATE message. The string returned to the dump state client is
+ * truncated to |max_length| and reflects the maximum size the client can
+ * handle.
+ */
+ virtual std::string DumpState(size_t max_length);
+
+ /*
+ * Receives a message on this Service instance's endpoint and dispatches it.
+ * If the endpoint is in blocking mode this call blocks until a message is
+ * received, a signal is delivered to this thread, or the service is canceled.
+ * If the endpoint is in non-blocking mode and a message is not pending this
+ * call returns immediately with ETIMEDOUT.
+ */
+ Status<void> ReceiveAndDispatch();
+
+ private:
+ friend class Message;
+
+ Status<void> HandleSystemMessage(Message& message);
+
+ Service(const Service&);
+ void operator=(const Service&) = delete;
+
+ const std::string name_;
+ std::unique_ptr<Endpoint> endpoint_;
+
+ /*
+ * Maintains references to active channels.
+ */
+ mutable std::mutex channels_mutex_;
+ std::unordered_map<int, std::shared_ptr<Channel>> channels_;
+};
+
+/*
+ * Utility base class for services. This template handles allocation and
+ * initialization checks, reducing boiler plate code.
+ */
+template <typename TYPE>
+class ServiceBase : public Service {
+ public:
+ /*
+ * Static service allocation method that check for initialization errors.
+ * If errors are encountered these automatically clean up and return
+ * nullptr.
+ */
+ template <typename... Args>
+ static inline std::shared_ptr<TYPE> Create(Args&&... args) {
+ std::shared_ptr<TYPE> service(new TYPE(std::forward<Args>(args)...));
+ if (service->IsInitialized())
+ return service;
+ else
+ return nullptr;
+ }
+
+ protected:
+ /*
+ * Shorthand for subclasses to refer to this base, particularly
+ * to call the base class constructor.
+ */
+ typedef ServiceBase<TYPE> BASE;
+
+ ServiceBase(const std::string& name, std::unique_ptr<Endpoint> endpoint)
+ : Service(name, std::move(endpoint)) {}
+};
+
+#ifndef STRINGIFY
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+#endif
+
+#define PDX_ERROR_PREFIX "[" __FILE__ ":" STRINGIFY(__LINE__) "]"
+
+/*
+ * Macros for replying to messages. Error handling can be tedious;
+ * these macros make things a little cleaner.
+ */
+#define CHECK_ERROR(cond, error, fmt, ...) \
+ do { \
+ if ((cond)) { \
+ ALOGE(fmt, ##__VA_ARGS__); \
+ goto error; \
+ } \
+ } while (0)
+
+#define REPLY_ERROR(message, error, error_label) \
+ do { \
+ auto __status = message.ReplyError(error); \
+ CHECK_ERROR(!__status, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ __status.GetErrorMessage().c_str()); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_ERROR_RETURN(message, error, ...) \
+ do { \
+ auto __status = message.ReplyError(error); \
+ ALOGE_IF(!__status, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ __status.GetErrorMessage().c_str()); \
+ return __VA_ARGS__; \
+ } while (0)
+
+#define REPLY_MESSAGE(message, message_return_code, error_label) \
+ do { \
+ auto __status = message.Reply(message_return_code); \
+ CHECK_ERROR(!__status, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ __status.GetErrorMessage().c_str()); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_SUCCESS(message, message_return_code, error_label) \
+ REPLY_MESSAGE(message, message_return_code, error_label)
+
+#define REPLY_MESSAGE_RETURN(message, message_return_code, ...) \
+ do { \
+ auto __status = message.Reply(message_return_code); \
+ ALOGE_IF(!__status, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ __status.GetErrorMessage().c_str()); \
+ return __VA_ARGS__; \
+ } while (0)
+
+#define REPLY_SUCCESS_RETURN(message, message_return_code, ...) \
+ REPLY_MESSAGE_RETURN(message, message_return_code, __VA_ARGS__)
+
+#define REPLY_FD(message, push_fd, error_label) \
+ do { \
+ auto __status = message.ReplyFileDescriptor(push_fd); \
+ CHECK_ERROR(!__status, error_label, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+ __status.GetErrorMessage().c_str()); \
+ goto error_label; \
+ } while (0)
+
+#define REPLY_FD_RETURN(message, push_fd, ...) \
+ do { \
+ auto __status = message.ReplyFileDescriptor(push_fd); \
+ ALOGE_IF(__status < 0, \
+ PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+ __status.GetErrorMessage().c_str()); \
+ return __VA_ARGS__; \
+ } while (0)
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_SERVICE_H_
diff --git a/libs/vr/libpdx/private/pdx/service_dispatcher.h b/libs/vr/libpdx/private/pdx/service_dispatcher.h
new file mode 100644
index 0000000000..c5e342af0c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_dispatcher.h
@@ -0,0 +1,79 @@
+#ifndef ANDROID_PDX_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_SERVICE_DISPATCHER_H_
+
+#include <memory>
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+/*
+ * ServiceDispatcher manages a list of Service instances and handles message
+ * reception and dispatch to the services. This makes repetitive dispatch tasks
+ * easier to implement.
+ */
+class ServiceDispatcher {
+ public:
+ virtual ~ServiceDispatcher() = default;
+
+ /*
+ * Adds a service to the list of services handled by this dispatcher. This
+ * will fail if any threads are blocked waiting for messages in this
+ * dispatcher.
+ *
+ * Returns 0 on success; -EEXIST if the service was already added.
+ */
+ virtual int AddService(const std::shared_ptr<Service>& service) = 0;
+
+ /*
+ * Removes a service from this dispatcher. This will fail if any threads are
+ * blocked waiting for messages in this dispatcher.
+ *
+ * Returns 0 on success; -ENOENT if the service was not previously added;
+ * -EBUSY if there are threads in the dispatcher.
+ */
+ virtual int RemoveService(const std::shared_ptr<Service>& service) = 0;
+
+ /*
+ * Receive and dispatch one set of messages. Multiple threads may enter this
+ * method to create an implicit thread pool, as described for
+ * enterDispatchLoop() below, however this method exits after one dispatch
+ * cycle, requiring an external loop. This is useful when other work needs
+ * to be done in the service dispatch loop.
+ */
+ virtual int ReceiveAndDispatch() = 0;
+
+ /*
+ * Same as above with timeout in milliseconds. A negative value means
+ * infinite timeout, while a value of 0 means return immediately if no
+ * messages are available to receive.
+ */
+ virtual int ReceiveAndDispatch(int timeout) = 0;
+
+ /*
+ * Receive and dispatch messages until canceled. When more than one thread
+ * enters this method it creates an implicit thread pool to dispatch messages.
+ * Explicit thread pools may be created by using a single dispatch thread that
+ * hands Message instances (via move assignment) over to a queue of threads
+ * (or perhaps one of several) to handle.
+ */
+ virtual int EnterDispatchLoop() = 0;
+
+ /*
+ * Sets the canceled state of the dispatcher. When canceled is true, any
+ * threads blocked waiting for messages will return. This method waits until
+ * all dispatch threads have exited the dispatcher.
+ */
+ virtual void SetCanceled(bool cancel) = 0;
+
+ /*
+ * Gets the canceled state of the dispatcher.
+ */
+ virtual bool IsCanceled() const = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx/private/pdx/service_endpoint.h b/libs/vr/libpdx/private/pdx/service_endpoint.h
new file mode 100644
index 0000000000..28bd6bc454
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_endpoint.h
@@ -0,0 +1,144 @@
+#ifndef ANDROID_PDX_ENDPOINT_H_
+#define ANDROID_PDX_ENDPOINT_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+struct iovec;
+
+namespace android {
+namespace pdx {
+
+class Service;
+class Channel;
+class Message;
+
+struct MessageInfo {
+ int pid{0};
+ int tid{0};
+ int cid{0};
+ int mid{0};
+ int euid{0};
+ int egid{0};
+ int32_t op{0};
+ uint32_t flags{0};
+ Service* service{nullptr};
+ Channel* channel{nullptr};
+ size_t send_len{0};
+ size_t recv_len{0};
+ size_t fd_count{0};
+ uint64_t impulse[4] = {};
+};
+
+// Wrapper around transport endpoint. Abstracts the underlying transport APIs in
+// a way, that the underlying IPC can be substituted for another technology
+// without changing the Service, Client and Message classes of this library.
+class Endpoint {
+ public:
+ virtual ~Endpoint() = default;
+
+ // Returns a tag that uniquely identifies a specific underlying IPC transport.
+ virtual uint32_t GetIpcTag() const = 0;
+
+ // Associates a Service instance with an endpoint by setting the service
+ // context pointer to the address of the Service. Only one Service may be
+ // associated with a given endpoint.
+ virtual Status<void> SetService(Service* service) = 0;
+
+ // Set the channel context for the given channel.
+ virtual Status<void> SetChannel(int channel_id, Channel* channel) = 0;
+
+ // Close a channel, signaling the client file object and freeing the channel
+ // id. Once closed, the client side of the channel always returns the error
+ // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+ virtual Status<void> CloseChannel(int channel_id) = 0;
+
+ // Update the event bits for the given channel (given by id), using the
+ // given clear and set masks.
+ virtual Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) = 0;
+
+ // Create a new channel and push it as a file descriptor to the process
+ // sending the |message|. |flags| may be set to O_NONBLOCK and/or
+ // O_CLOEXEC to control the initial behavior of the new file descriptor (the
+ // sending process may change these later using fcntl()). The internal Channel
+ // instance associated with this channel is set to |channel|, which may be
+ // nullptr. The new channel id allocated for this channel is returned in
+ // |channel_id|, which may also be nullptr if not needed.
+ virtual Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+ Channel* channel,
+ int* channel_id) = 0;
+
+ // Check whether the |ref| is a reference to a channel to the service
+ // represented by the |endpoint|. If the channel reference in question is
+ // valid, the Channel object is returned in |channel| when non-nullptr and
+ // the channel ID is returned through the Status object.
+ virtual Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ Channel** channel) = 0;
+
+ // Receives a message on the given endpoint file descriptor.
+ virtual Status<void> MessageReceive(Message* message) = 0;
+
+ // Replies to the message with a return code.
+ virtual Status<void> MessageReply(Message* message, int return_code) = 0;
+
+ // Replies to the message with a file descriptor.
+ virtual Status<void> MessageReplyFd(Message* message,
+ unsigned int push_fd) = 0;
+
+ // Replies to the message with a local channel handle.
+ virtual Status<void> MessageReplyChannelHandle(
+ Message* message, const LocalChannelHandle& handle) = 0;
+
+ // Replies to the message with a borrowed local channel handle.
+ virtual Status<void> MessageReplyChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) = 0;
+
+ // Replies to the message with a remote channel handle.
+ virtual Status<void> MessageReplyChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) = 0;
+
+ // Reads message data into an array of memory buffers.
+ virtual Status<size_t> ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) = 0;
+
+ // Sends reply data for message.
+ virtual Status<size_t> WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) = 0;
+
+ // Records a file descriptor into the message buffer and returns the remapped
+ // reference to be sent to the remote process.
+ virtual Status<FileReference> PushFileHandle(Message* message,
+ const LocalHandle& handle) = 0;
+ virtual Status<FileReference> PushFileHandle(
+ Message* message, const BorrowedHandle& handle) = 0;
+ virtual Status<FileReference> PushFileHandle(Message* message,
+ const RemoteHandle& handle) = 0;
+ virtual Status<ChannelReference> PushChannelHandle(
+ Message* message, const LocalChannelHandle& handle) = 0;
+ virtual Status<ChannelReference> PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) = 0;
+ virtual Status<ChannelReference> PushChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) = 0;
+
+ // Obtains a file descriptor/channel handle from a message for the given
+ // reference.
+ virtual LocalHandle GetFileHandle(Message* message,
+ FileReference ref) const = 0;
+ virtual LocalChannelHandle GetChannelHandle(Message* message,
+ ChannelReference ref) const = 0;
+
+ // Transport-specific message state management.
+ virtual void* AllocateMessageState() = 0;
+ virtual void FreeMessageState(void* state) = 0;
+
+ // Cancels the endpoint, unblocking any receiver threads waiting for a
+ // message.
+ virtual Status<void> Cancel() = 0;
+};
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_ENDPOINT_H_
diff --git a/libs/vr/libpdx/private/pdx/status.h b/libs/vr/libpdx/private/pdx/status.h
new file mode 100644
index 0000000000..067fe25e9c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/status.h
@@ -0,0 +1,180 @@
+#ifndef ANDROID_PDX_STATUS_H_
+#define ANDROID_PDX_STATUS_H_
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+namespace android {
+namespace pdx {
+
+// This is a helper class for constructing Status<T> with an error code.
+struct ErrorStatus {
+ public:
+ ErrorStatus(int error) : error_{error} {}
+ int error() const { return error_; }
+
+ static std::string ErrorToString(int error_code);
+
+ private:
+ int error_;
+};
+
+// Status<T> is a container class that can be used to return a value of type T
+// or error code to the caller.
+template <typename T>
+class Status {
+ public:
+ // Default constructor so an empty Status object can be created.
+ Status() : error_{-1} {}
+
+ // Value copy/move constructors. These are intentionally not marked as
+ // explicit to allow direct value returns from functions without having
+ // to explicitly wrap them into Status<T>().
+ Status(const T& value) : value_{value} {} // NOLINT(runtime/explicit)
+ Status(T&& value) : value_{std::move(value)} {} // NOLINT(runtime/explicit)
+
+ // Constructor for storing an error code inside the Status object.
+ Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit)
+ : error_{error_status.error()} {}
+
+ // Copy/move constructors. Move constructor leaves |other| object in empty
+ // state.
+ Status(const Status& other) = default;
+ Status(Status&& other)
+ : value_{std::move(other.value_)}, error_{other.error_} {
+ other.error_ = -1;
+ }
+
+ // Assignment operators.
+ Status& operator=(const Status& other) = default;
+ Status& operator=(Status&& other) {
+ error_ = other.error_;
+ value_ = std::move(other.value_);
+ other.error_ = -1;
+ T empty;
+ std::swap(other.value_, empty);
+ return *this;
+ }
+
+ // Change the value/error code of the status object directly.
+ void SetValue(T value) {
+ error_ = 0;
+ value_ = std::move(value);
+ }
+ void SetError(int error) {
+ error_ = error;
+ T empty;
+ std::swap(value_, empty);
+ }
+
+ // If |other| is in error state, copy the error code to this object.
+ // Returns true if error was propagated
+ template<typename U>
+ bool PropagateError(const Status<U>& other) {
+ if (!other.ok() && !other.empty()) {
+ SetError(other.error());
+ return true;
+ }
+ return false;
+ }
+
+ // Returns true if the status object contains valid value for type T.
+ // This means, the object is not empty and does not contain an error code.
+ bool ok() const { return error_ == 0; }
+
+ // Checks if the object is empty (doesn't contain a valid value nor an error).
+ bool empty() const { return error_ < 0; }
+
+ // Explicit bool conversion, equivalent to invoking ok().
+ explicit operator bool() const { return ok(); }
+
+ // Accessors for the value stored in Status. Calling when ok() is false leads
+ // to undefined behavior.
+ const T& get() const { return value_; }
+ T&& take() {
+ error_ = -1;
+ return std::move(value_);
+ }
+
+ // Returns the error code stored in the object. These codes are positive
+ // non-zero values.
+ // Can be called only when an error is actually stored (that is, the object
+ // is not empty nor containing a valid value).
+ int error() const { return std::max(error_, 0); }
+
+ // Returns the error code as ErrorStatus object. This is a helper method
+ // to aid in propagation of error codes between Status<T> of different types
+ // as in the following example:
+ // Status<int> foo() {
+ // Status<void> status = bar();
+ // if(!status)
+ // return status.error_status();
+ // return 12;
+ // }
+ inline ErrorStatus error_status() const { return ErrorStatus{error()}; }
+
+ // Returns the error message associated with error code stored in the object.
+ // The message is the same as the string returned by strerror(status.error()).
+ // Can be called only when an error is actually stored (that is, the object
+ // is not empty nor containing a valid value).
+ std::string GetErrorMessage() const {
+ std::string message;
+ if (error_ > 0)
+ message = ErrorStatus::ErrorToString(error_);
+ return message;
+ }
+
+ private:
+ T value_{};
+ int error_{0};
+};
+
+// Specialization for status containing no other value but the error code.
+template <>
+class Status<void> {
+ public:
+ Status() = default;
+ Status(const ErrorStatus& error_status) // NOLINT(runtime/explicit)
+ : error_{error_status.error()} {}
+ void SetValue() { error_ = 0; }
+ void SetError(int error) { error_ = error; }
+
+ template<typename U>
+ bool PropagateError(const Status<U>& other) {
+ if (!other.ok() && !other.empty()) {
+ SetError(other.error());
+ return true;
+ }
+ return false;
+ }
+
+ bool ok() const { return error_ == 0; }
+ bool empty() const { return false; }
+ explicit operator bool() const { return ok(); }
+ int error() const { return std::max(error_, 0); }
+ inline ErrorStatus error_status() const { return ErrorStatus{error()}; }
+ std::string GetErrorMessage() const {
+ std::string message;
+ if (error_ > 0)
+ message = ErrorStatus::ErrorToString(error_);
+ return message;
+ }
+
+ private:
+ int error_{0};
+};
+
+// TODO(avakulenko): Remove these function once all callers of it are gone.
+inline int ReturnStatusOrError(const Status<void>& status) {
+ return status ? 0 : -status.error();
+}
+
+inline int ReturnStatusOrError(const Status<int>& status) {
+ return status ? status.get() : -status.error();
+}
+
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_STATUS_H_
diff --git a/libs/vr/libpdx/private/pdx/trace.h b/libs/vr/libpdx/private/pdx/trace.h
new file mode 100644
index 0000000000..ebe8491ebc
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/trace.h
@@ -0,0 +1,35 @@
+#ifndef ANDROID_PDX_TRACE_H_
+#define ANDROID_PDX_TRACE_H_
+
+// Tracing utilities for libpdx. Tracing in the service framework is enabled
+// under these conditions:
+// 1. ATRACE_TAG is defined, AND
+// 2. ATRACE_TAG does not equal ATRACE_TAG_NEVER, AND
+// 3. PDX_TRACE_ENABLED is defined, AND
+// 4. PDX_TRACE_ENABLED is equal to logical true.
+//
+// If any of these conditions are not met tracing is completely removed from the
+// library and headers.
+
+// If ATRACE_TAG is not defined, default to never.
+#ifndef ATRACE_TAG
+#define ATRACE_TAG ATRACE_TAG_NEVER
+#endif
+
+// Include tracing functions after the trace tag is defined.
+#include <utils/Trace.h>
+
+// If PDX_TRACE_ENABLED is not defined, default to off.
+#ifndef PDX_TRACE_ENABLED
+#define PDX_TRACE_ENABLED 0
+#endif
+
+#if (ATRACE_TAG) != (ATRACE_TAG_NEVER) && (PDX_TRACE_ENABLED)
+#define PDX_TRACE_NAME ATRACE_NAME
+#else
+#define PDX_TRACE_NAME(name) \
+ do { \
+ } while (0)
+#endif
+
+#endif // ANDROID_PDX_TRACE_H_
diff --git a/libs/vr/libpdx/private/pdx/utility.h b/libs/vr/libpdx/private/pdx/utility.h
new file mode 100644
index 0000000000..305c3b87a4
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/utility.h
@@ -0,0 +1,367 @@
+#ifndef ANDROID_PDX_UTILITY_H_
+#define ANDROID_PDX_UTILITY_H_
+
+#include <cstdint>
+#include <iterator>
+
+#include <pdx/rpc/sequence.h>
+
+// Utilities for testing object serialization.
+
+namespace android {
+namespace pdx {
+
+class ByteBuffer {
+ public:
+ using iterator = uint8_t*;
+ using const_iterator = const uint8_t*;
+ using size_type = size_t;
+
+ ByteBuffer() = default;
+ ByteBuffer(const ByteBuffer& other) {
+ resize(other.size());
+ if (other.size())
+ memcpy(data_, other.data(), other.size());
+ }
+
+ ByteBuffer& operator=(const ByteBuffer& other) {
+ resize(other.size());
+ if (other.size())
+ memcpy(data_, other.data(), other.size());
+ return *this;
+ }
+
+ ByteBuffer& operator=(ByteBuffer&& other) {
+ std::swap(data_, other.data_);
+ std::swap(size_, other.size_);
+ std::swap(capacity_, other.capacity_);
+ other.clear();
+ return *this;
+ }
+
+ inline const uint8_t* data() const { return data_; }
+ inline uint8_t* data() { return data_; }
+ inline size_t size() const { return size_; }
+ inline size_t capacity() const { return capacity_; }
+
+ iterator begin() { return data_; }
+ const_iterator begin() const { return data_; }
+ iterator end() { return data_ + size_; }
+ const_iterator end() const { return data_ + size_; }
+
+ inline bool operator==(const ByteBuffer& other) const {
+ return size_ == other.size_ &&
+ (size_ == 0 || memcmp(data_, other.data_, size_) == 0);
+ }
+
+ inline bool operator!=(const ByteBuffer& other) const {
+ return !operator==(other);
+ }
+
+ inline void reserve(size_t size) {
+ if (size <= capacity_)
+ return;
+ // Find next power of 2 (assuming the size is 32 bits for now).
+ size--;
+ size |= size >> 1;
+ size |= size >> 2;
+ size |= size >> 4;
+ size |= size >> 8;
+ size |= size >> 16;
+ size++;
+ void* new_data = data_ ? realloc(data_, size) : malloc(size);
+ // TODO(avakulenko): Check for allocation failures.
+ data_ = static_cast<uint8_t*>(new_data);
+ capacity_ = size;
+ }
+
+ inline void resize(size_t size) {
+ reserve(size);
+ size_ = size;
+ }
+
+ inline uint8_t* grow_by(size_t size_delta) {
+ size_t old_size = size_;
+ resize(old_size + size_delta);
+ return data_ + old_size;
+ }
+
+ inline void clear() { size_ = 0; }
+
+ private:
+ uint8_t* data_{nullptr};
+ size_t size_{0};
+ size_t capacity_{0};
+};
+
+// Utility functions to increment/decrement void pointers to data buffers.
+template <typename OFFSET_T>
+inline const void* AdvancePointer(const void* ptr, OFFSET_T offset) {
+ return static_cast<const uint8_t*>(ptr) + offset;
+}
+
+template <typename OFFSET_T>
+inline void* AdvancePointer(void* ptr, OFFSET_T offset) {
+ return static_cast<uint8_t*>(ptr) + offset;
+}
+
+inline ptrdiff_t PointerDistance(const void* end, const void* begin) {
+ return static_cast<const uint8_t*>(end) - static_cast<const uint8_t*>(begin);
+}
+
+// Utility to build sequences of types.
+template <typename, typename>
+struct AppendTypeSequence;
+
+template <typename T, typename... S, template <typename...> class TT>
+struct AppendTypeSequence<T, TT<S...>> {
+ using type = TT<S..., T>;
+};
+
+// Utility to generate repeated types.
+template <typename T, std::size_t N, template <typename...> class TT>
+struct RepeatedType {
+ using type = typename AppendTypeSequence<
+ T, typename RepeatedType<T, N - 1, TT>::type>::type;
+};
+
+template <typename T, template <typename...> class TT>
+struct RepeatedType<T, 0, TT> {
+ using type = TT<>;
+};
+
+template <typename V, typename S>
+inline V ReturnValueHelper(V value, S /*ignore*/) {
+ return value;
+}
+
+template <typename R, typename V, size_t... S>
+inline R GetNTupleHelper(V value, rpc::IndexSequence<S...>) {
+ return std::make_tuple(ReturnValueHelper(value, S)...);
+}
+
+// Returns an N-tuple of type std::tuple<T,...T> containing |value| in each
+// element.
+template <size_t N, typename T,
+ typename R = typename RepeatedType<T, N, std::tuple>::type>
+inline R GetNTuple(T value) {
+ return GetNTupleHelper<R>(value, rpc::MakeIndexSequence<N>{});
+}
+
+class NoOpOutputResourceMapper : public OutputResourceMapper {
+ public:
+ Status<FileReference> PushFileHandle(const LocalHandle& handle) override {
+ return handle.Get();
+ }
+
+ Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override {
+ return handle.Get();
+ }
+
+ Status<FileReference> PushFileHandle(const RemoteHandle& handle) override {
+ return handle.Get();
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ const LocalChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ const BorrowedChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ const RemoteChannelHandle& handle) override {
+ return handle.value();
+ }
+};
+
+class NoOpInputResourceMapper : public InputResourceMapper {
+ public:
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override {
+ *handle = LocalHandle{ref};
+ return true;
+ }
+
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override {
+ *handle = LocalChannelHandle{nullptr, ref};
+ return true;
+ }
+};
+
+class NoOpResourceMapper : public NoOpOutputResourceMapper,
+ public NoOpInputResourceMapper {};
+
+// Simple implementation of the payload interface, required by
+// Serialize/Deserialize. This is intended for test cases, where compatibility
+// with std::vector is helpful.
+class Payload : public MessageWriter,
+ public MessageReader,
+ public OutputResourceMapper {
+ public:
+ using BaseType = ByteBuffer;
+ using iterator = typename BaseType::iterator;
+ using const_iterator = typename BaseType::const_iterator;
+ using size_type = typename BaseType::size_type;
+
+ Payload() = default;
+ explicit Payload(size_type count, uint8_t value = 0) { Append(count, value); }
+ Payload(const Payload& other) : buffer_(other.buffer_) {}
+ Payload(const std::initializer_list<uint8_t>& initializer) {
+ buffer_.resize(initializer.size());
+ std::copy(initializer.begin(), initializer.end(), buffer_.begin());
+ }
+
+ Payload& operator=(const Payload& other) {
+ buffer_ = other.buffer_;
+ read_pos_ = 0;
+ return *this;
+ }
+ Payload& operator=(const std::initializer_list<uint8_t>& initializer) {
+ buffer_.resize(initializer.size());
+ std::copy(initializer.begin(), initializer.end(), buffer_.begin());
+ read_pos_ = 0;
+ return *this;
+ }
+
+ // Compares Payload with Payload.
+ bool operator==(const Payload& other) const {
+ return buffer_ == other.buffer_;
+ }
+ bool operator!=(const Payload& other) const {
+ return buffer_ != other.buffer_;
+ }
+
+ // Compares Payload with std::vector.
+ template <typename Type, typename AllocatorType>
+ typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type
+ operator==(const std::vector<Type, AllocatorType>& other) const {
+ return buffer_.size() == other.size() &&
+ memcmp(buffer_.data(), other.data(), other.size()) == 0;
+ }
+ template <typename Type, typename AllocatorType>
+ typename std::enable_if<sizeof(Type) == sizeof(uint8_t), bool>::type
+ operator!=(const std::vector<Type, AllocatorType>& other) const {
+ return !operator!=(other);
+ }
+
+ iterator begin() { return buffer_.begin(); }
+ const_iterator begin() const { return buffer_.begin(); }
+ iterator end() { return buffer_.end(); }
+ const_iterator end() const { return buffer_.end(); }
+
+ void Append(size_type count, uint8_t value) {
+ auto* data = buffer_.grow_by(count);
+ std::fill(data, data + count, value);
+ }
+
+ void Clear() {
+ buffer_.clear();
+ file_handles_.clear();
+ read_pos_ = 0;
+ }
+
+ void Rewind() { read_pos_ = 0; }
+
+ uint8_t* Data() { return buffer_.data(); }
+ const uint8_t* Data() const { return buffer_.data(); }
+ size_type Size() const { return buffer_.size(); }
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override {
+ return buffer_.grow_by(size);
+ }
+
+ OutputResourceMapper* GetOutputResourceMapper() override { return this; }
+
+ // OutputResourceMapper
+ Status<FileReference> PushFileHandle(const LocalHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+ }
+
+ Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+ }
+
+ Status<FileReference> PushFileHandle(const RemoteHandle& handle) override {
+ return handle.Get();
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ const LocalChannelHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.value());
+ return ref;
+ } else {
+ return handle.value();
+ }
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ const BorrowedChannelHandle& handle) override {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.value());
+ return ref;
+ } else {
+ return handle.value();
+ }
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ const RemoteChannelHandle& handle) override {
+ return handle.value();
+ }
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override {
+ return {buffer_.data() + read_pos_, &*buffer_.end()};
+ }
+
+ void ConsumeReadBufferSectionData(const void* new_start) override {
+ read_pos_ = PointerDistance(new_start, buffer_.data());
+ }
+
+ InputResourceMapper* GetInputResourceMapper() override {
+ return &input_resource_mapper_;
+ }
+
+ const int* FdArray() const { return file_handles_.data(); }
+ std::size_t FdCount() const { return file_handles_.size(); }
+
+ private:
+ NoOpInputResourceMapper input_resource_mapper_;
+ ByteBuffer buffer_;
+ std::vector<int> file_handles_;
+ size_t read_pos_{0};
+};
+
+} // namespace pdx
+} // namespace android
+
+// Helper macros for branch prediction hinting.
+#ifdef __GNUC__
+#define PDX_LIKELY(x) __builtin_expect(!!(x), true)
+#define PDX_UNLIKELY(x) __builtin_expect(!!(x), false)
+#else
+#define PDX_LIKELY(x) (x)
+#define PDX_UNLIKELY(x) (x)
+#endif
+
+#endif // ANDROID_PDX_UTILITY_H_
diff --git a/libs/vr/libpdx/serialization_tests.cpp b/libs/vr/libpdx/serialization_tests.cpp
new file mode 100644
index 0000000000..5ad104764d
--- /dev/null
+++ b/libs/vr/libpdx/serialization_tests.cpp
@@ -0,0 +1,2505 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/argument_encoder.h>
+#include <pdx/rpc/array_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/payload.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <pdx/utility.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+// Tests the serialization/deserialization of all supported types, verifying all
+// reasonable boundary conditions for types with multiple encodings.
+//
+// NOTE: Sometimes this file uses the construct "var = decltype(var)({...})"
+// instead of the equivalent "var = {...}" to construct vectors. This is to
+// prevent clang-format from producing annoyingly vertical code from long
+// initializers.
+
+// TODO(eieio): Automatically generate some of these tests?
+
+namespace {
+
+// Test data for serialization/deserialization of floats.
+const float kZeroFloat = 0.0f;
+const float kOneFloat = 1.0f;
+const auto kZeroFloatBytes = reinterpret_cast<const std::uint8_t*>(&kZeroFloat);
+const auto kOneFloatBytes = reinterpret_cast<const std::uint8_t*>(&kOneFloat);
+const double kZeroDouble = 0.0;
+const double kOneDouble = 1.0;
+const auto kZeroDoubleBytes =
+ reinterpret_cast<const std::uint8_t*>(&kZeroDouble);
+const auto kOneDoubleBytes = reinterpret_cast<const std::uint8_t*>(&kOneDouble);
+
+struct TestType {
+ enum class Foo { kFoo, kBar, kBaz };
+
+ int a;
+ float b;
+ std::string c;
+ Foo d;
+
+ TestType() {}
+ TestType(int a, float b, const std::string& c, Foo d)
+ : a(a), b(b), c(c), d(d) {}
+
+ // Make gtest expressions simpler by defining equality operator. This is not
+ // needed for serialization.
+ bool operator==(const TestType& other) const {
+ return a == other.a && b == other.b && c == other.c && d == other.d;
+ }
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c, d);
+};
+
+template <typename FileHandleType>
+struct TestTemplateType {
+ FileHandleType fd;
+
+ TestTemplateType() {}
+ TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {}
+
+ bool operator==(const TestTemplateType& other) const {
+ return fd.Get() == other.fd.Get();
+ }
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd);
+};
+
+// Utilities to generate test maps and payloads.
+template <typename MapType>
+MapType MakeMap(std::size_t size) {
+ MapType result;
+ for (std::size_t i = 0; i < size; i++) {
+ result.emplace(i, i);
+ }
+ return result;
+}
+
+template <typename MapType>
+void InsertKeyValue(MessageWriter* writer, std::size_t size) {
+ MapType map;
+ for (std::size_t i = 0; i < size; i++) {
+ map.emplace(i, i);
+ }
+ for (const auto& element : map) {
+ Serialize(element.first, writer);
+ Serialize(element.second, writer);
+ }
+}
+
+} // anonymous namespace
+
+TEST(SerializableTypes, Constructor) {
+ TestType tt(1, 2.0, "three", TestType::Foo::kBar);
+ EXPECT_EQ(1, tt.a);
+ EXPECT_EQ(2.0, tt.b);
+ EXPECT_EQ("three", tt.c);
+ EXPECT_EQ(TestType::Foo::kBar, tt.d);
+}
+
+TEST(SerializationTest, bool) {
+ Payload result;
+ Payload expected;
+ bool value;
+
+ // True.
+ value = true;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_TRUE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // False.
+ value = false;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FALSE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint8_t) {
+ Payload result;
+ Payload expected;
+ uint8_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint16_t) {
+ Payload result;
+ Payload expected;
+ uint16_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint32_t) {
+ Payload result;
+ Payload expected;
+ uint32_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT32.
+ value = (1 << 16);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT32.
+ value = 0xffffffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, uint64_t) {
+ Payload result;
+ Payload expected;
+ uint64_t value;
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = (1 << 7) - 1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT8.
+ value = (1 << 7);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, (1 << 7)};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT8.
+ value = 0xff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT8, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT16.
+ value = (1 << 8);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0, 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT16.
+ value = 0xffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT32.
+ value = (1 << 16);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0, 0, 1, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT32.
+ value = 0xffffffff;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min UINT64.
+ value = (1ULL << 32);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_UINT64, 0, 0, 0, 0, 1, 0, 0, 0};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max UINT64.
+ value = 0xffffffffffffffffULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int8_t) {
+ Payload result;
+ Payload expected;
+ int8_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int16_t) {
+ Payload result;
+ Payload expected;
+ int16_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int32_t) {
+ Payload result;
+ Payload expected;
+ int32_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT32.
+ value = -2147483648;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT32.
+ value = 2147483647;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, int64_t) {
+ Payload result;
+ Payload expected;
+ int64_t value;
+
+ // Min NEGATIVE FIXINT.
+ value = -32;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max NEGATIVE FIXINT.
+ value = -1;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min FIXINT.
+ value = 0;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXINT.
+ value = 127;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT8.
+ value = -128;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT8.
+ value = -33;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT8, 0xdf};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT16.
+ value = -32768;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT16.
+ value = 32767;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT32.
+ value = -2147483648;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT32.
+ value = 2147483647;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min INT64.
+ value = -9223372036854775808ULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max INT64.
+ value = 9223372036854775807ULL;
+ Serialize(value, &result);
+ expected = {
+ ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, float) {
+ Payload result;
+ Payload expected;
+ float value;
+
+ value = 0.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ value = 1.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, double) {
+ Payload result;
+ Payload expected;
+ double value;
+
+ value = 0.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1],
+ kZeroDoubleBytes[2], kZeroDoubleBytes[3], kZeroDoubleBytes[4],
+ kZeroDoubleBytes[5], kZeroDoubleBytes[6], kZeroDoubleBytes[7]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ value = 1.0f;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1],
+ kOneDoubleBytes[2], kOneDoubleBytes[3], kOneDoubleBytes[4],
+ kOneDoubleBytes[5], kOneDoubleBytes[6], kOneDoubleBytes[7]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, Enum) {
+ Payload result;
+ Payload expected;
+
+ enum Foo { kFoo, kBar, kBaz };
+ Foo value = kBar;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, EnumClass) {
+ Payload result;
+ Payload expected;
+
+ enum class Foo { kFoo, kBar, kBaz };
+ Foo value = Foo::kBaz;
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, LocalHandle) {
+ Payload result;
+ Payload expected;
+ LocalHandle fd1;
+ LocalHandle fd2;
+
+ fd1 = LocalHandle(100);
+ Serialize(fd1, &result);
+ expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0};
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(1u, result.FdCount());
+ EXPECT_EQ(100, result.FdArray()[0]);
+ result.Clear();
+
+ fd2 = LocalHandle(200);
+ Serialize(std::forward_as_tuple(fd1, fd2), &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0});
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(2u, result.FdCount());
+ EXPECT_EQ(100, result.FdArray()[0]);
+ EXPECT_EQ(200, result.FdArray()[1]);
+ result.Clear();
+
+ fd1.Release(); // Don't try to close fd 100.
+ fd2.Release(); // Don't try to close fd 200.
+
+ fd1 = LocalHandle(-2);
+ Serialize(fd1, &result);
+ expected = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe,
+ 0xff};
+ EXPECT_EQ(expected, result);
+ EXPECT_EQ(0u, result.FdCount());
+ result.Clear();
+}
+
+TEST(SerializationTest, string) {
+ Payload result;
+ Payload expected;
+ std::string value;
+
+ // Min FIXSTR.
+ value = "";
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXSTR_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXSTR.
+ value = std::string((1 << 5) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXSTR_MAX};
+ expected.Append((1 << 5) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR8.
+ value = std::string((1 << 5), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 5)};
+ expected.Append((1 << 5), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR8.
+ value = std::string((1 << 8) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 8) - 1};
+ expected.Append((1 << 8) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR16.
+ value = std::string((1 << 8), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR16, 0x00, 0x01};
+ expected.Append((1 << 8), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR16.
+ value = std::string((1 << 16) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR32.
+ value = std::string((1 << 16), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, StringWrapper) {
+ Payload result;
+ Payload expected;
+ std::string value;
+
+ // Min FIXSTR.
+ value = "";
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_FIXSTR_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXSTR.
+ value = std::string((1 << 5) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_FIXSTR_MAX};
+ expected.Append((1 << 5) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR8.
+ value = std::string((1 << 5), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 5)};
+ expected.Append((1 << 5), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR8.
+ value = std::string((1 << 8) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR8, (1 << 8) - 1};
+ expected.Append((1 << 8) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR16.
+ value = std::string((1 << 8), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR16, 0x00, 0x01};
+ expected.Append((1 << 8), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max STR16.
+ value = std::string((1 << 16) - 1, 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min STR32.
+ value = std::string((1 << 16), 'x');
+ Serialize(WrapString(value), &result);
+ expected = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, vector) {
+ Payload result;
+ Payload expected;
+ std::vector<uint8_t> value;
+
+ // Min FIXARRAY.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ value = decltype(value)((1 << 4) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ value = decltype(value)((1 << 4), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ value = decltype(value)((1 << 16) - 1, 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ value = decltype(value)((1 << 16), 'x');
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, map) {
+ Payload result;
+ Payload expected;
+ std::map<std::uint32_t, std::uint32_t> value;
+
+ // Min FIXMAP.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXMAP.
+ value = MakeMap<decltype(value)>((1 << 4) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP16.
+ value = MakeMap<decltype(value)>((1 << 4));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0x10, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max MAP16.
+ value = MakeMap<decltype(value)>((1 << 16) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP32.
+ value = MakeMap<decltype(value)>((1 << 16));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, unordered_map) {
+ Payload result;
+ Payload expected;
+ std::unordered_map<std::uint32_t, std::uint32_t> value;
+
+ // Min FIXMAP.
+ value = {};
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXMAP.
+ value = MakeMap<decltype(value)>((1 << 4) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP16.
+ value = MakeMap<decltype(value)>((1 << 4));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0x10, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 4));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max MAP16.
+ value = MakeMap<decltype(value)>((1 << 16) - 1);
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16) - 1);
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min MAP32.
+ value = MakeMap<decltype(value)>((1 << 16));
+ Serialize(value, &result);
+ expected = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(value)>(&expected, (1 << 16));
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, array) {
+ Payload result;
+ Payload expected;
+
+ // Min FIXARRAY.
+ std::array<std::uint8_t, 0> a0;
+ Serialize(a0, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ std::array<std::uint8_t, (1 << 4) - 1> a1;
+ for (auto& element : a1)
+ element = 'x';
+ Serialize(a1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ std::array<std::uint8_t, (1 << 4)> a2;
+ for (auto& element : a2)
+ element = 'x';
+ Serialize(a2, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ std::array<std::uint8_t, (1 << 16) - 1> a3;
+ for (auto& element : a3)
+ element = 'x';
+ Serialize(a3, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ std::array<std::uint8_t, (1 << 16)> a4;
+ for (auto& element : a4)
+ element = 'x';
+ Serialize(a4, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, ArrayWrapper) {
+ Payload result;
+ Payload expected;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>> value;
+ ArrayWrapper<std::uint8_t> wrapper;
+
+ // Min FIXARRAY.
+ value = {};
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ value = decltype(value)((1 << 4) - 1, 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ value = decltype(value)((1 << 4), 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max ARRAY16.
+ value = decltype(value)((1 << 16) - 1, 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ value = decltype(value)((1 << 16), 'x');
+ wrapper = decltype(wrapper)(value.data(), value.capacity(), value.size());
+ Serialize(wrapper, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, pair) {
+ Payload result;
+ Payload expected;
+
+ auto p1 = std::make_pair(1, 2);
+ Serialize(p1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto p2 = std::make_pair('x', std::string("12345"));
+ Serialize(p2, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x',
+ ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3',
+ '4', '5'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, tuple) {
+ Payload result;
+ Payload expected;
+
+ // Min FIXARRAY.
+ auto t1 = std::make_tuple();
+ Serialize(t1, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MIN};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Max FIXARRAY.
+ auto t2 = GetNTuple<15>('x');
+ Serialize(t2, &result);
+ expected = {ENCODING_TYPE_FIXARRAY_MAX};
+ expected.Append((1 << 4) - 1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY16.
+ auto t3 = GetNTuple<(1 << 4)>('x');
+ Serialize(t3, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ expected.Append((1 << 4), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+// Template instantiation depth is an issue for these tests. They are commented
+// out to document the expected behavior, even though tuples of this order are
+// not expected in practice.
+#if 0
+ // Max ARRAY16.
+ auto t4 = GetNTuple<(1 << 16)-1>('x');
+ Serialize(t4, &result);
+ expected = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ expected.Append((1 << 16)-1, 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // Min ARRAY32.
+ auto t5 = GetNTuple<(1 << 16)>('x');
+ Serialize(t5, &result);
+ expected = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ expected.Append((1 << 16), 'x');
+ EXPECT_EQ(expected, result);
+ result.Clear();
+#endif
+}
+
+// TODO(eieio): More exhaustive testing of type nesting.
+TEST(SerializationTest, NestedTuple) {
+ Payload result;
+ Payload expected;
+
+ auto t1 = std::make_tuple('x', std::make_tuple<int, int>(1, 2));
+ Serialize(t1, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 2, 'x',
+ ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto t2 = std::make_tuple('x', std::make_tuple<int, int>(1, 2),
+ std::string("0123456789"));
+ Serialize(t2, &result);
+ expected = decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 3, 'x',
+ ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2,
+ ENCODING_TYPE_FIXSTR | 10, '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ auto t3 = std::make_tuple(0.0f, std::uint64_t(10ULL),
+ std::vector<char>{'a', 'b', 'c'});
+ Serialize(t3, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 3, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10,
+ ENCODING_TYPE_FIXARRAY_MIN + 3, 'a', 'b', 'c'});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, NestedMap) {
+ Payload result;
+ Payload expected;
+
+ std::map<int, std::pair<std::string, int>> m1 = {{0, {"a", 2}},
+ {1, {"b", 10}}};
+ Serialize(m1, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXMAP_MIN + 2, 0, ENCODING_TYPE_FIXARRAY_MIN + 2,
+ ENCODING_TYPE_FIXSTR_MIN + 1, 'a', 2, 1, ENCODING_TYPE_FIXARRAY_MIN + 2,
+ ENCODING_TYPE_FIXSTR_MIN + 1, 'b', 10});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+}
+
+TEST(SerializationTest, Serializable) {
+ Payload result;
+ Payload expected;
+
+ TestType t1{10, 0.0, "12345", TestType::Foo::kBaz};
+ Serialize(t1, &result);
+ expected = decltype(expected)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4',
+ '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2});
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ TestTemplateType<LocalHandle> tt{LocalHandle(-1)};
+ Serialize(tt, &result);
+ expected =
+ decltype(expected)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff});
+ EXPECT_EQ(expected, result);
+}
+
+TEST(SerializationTest, Variant) {
+ Payload result;
+ Payload expected;
+
+ Variant<int, bool, float> v;
+
+ // Empty variant.
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX,
+ ENCODING_TYPE_NIL};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = 10;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = true;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_TRUE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = false;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1, ENCODING_TYPE_FALSE};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ v = 1.0f;
+ Serialize(v, &result);
+ expected = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2,
+ ENCODING_TYPE_FLOAT32,
+ kOneFloatBytes[0],
+ kOneFloatBytes[1],
+ kOneFloatBytes[2],
+ kOneFloatBytes[3]};
+ EXPECT_EQ(expected, result);
+ result.Clear();
+
+ // TODO(eieio): Add more serialization tests for Variant.
+}
+
+TEST(DeserializationTest, bool) {
+ Payload buffer;
+ bool result = false;
+ ErrorType error;
+
+ // True.
+ buffer = {ENCODING_TYPE_TRUE};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(1, result); // Gtest generates warning from bool literals.
+
+ // False.
+ buffer = {ENCODING_TYPE_FALSE};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result); // Gtest generates warning from bool literals.
+}
+
+TEST(DeserializationTest, uint8_t) {
+ Payload buffer;
+ std::uint8_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // UINT16 out of range.
+ buffer = {ENCODING_TYPE_UINT16};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT16, error.encoding_type());
+
+ // UINT32 out of range.
+ buffer = {ENCODING_TYPE_UINT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type());
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint16_t) {
+ Payload buffer;
+ std::uint16_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // UINT32 out of range.
+ buffer = {ENCODING_TYPE_UINT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT32, error.encoding_type());
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint32_t) {
+ Payload buffer;
+ std::uint32_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // Min UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffU, result);
+
+ // UINT64 out of range.
+ buffer = {ENCODING_TYPE_UINT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_UINT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, uint64_t) {
+ Payload buffer;
+ std::uint64_t result = 0;
+ ErrorType error;
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127U, result);
+
+ // Min UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT8.
+ buffer = {ENCODING_TYPE_UINT8, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffU, result);
+
+ // Min UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT16.
+ buffer = {ENCODING_TYPE_UINT16, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffU, result);
+
+ // Min UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT32.
+ buffer = {ENCODING_TYPE_UINT32, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffU, result);
+
+ // Min UINT64.
+ buffer = {
+ ENCODING_TYPE_UINT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0U, result);
+
+ // Max UINT64.
+ buffer = {
+ ENCODING_TYPE_UINT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0xffffffffffffffffUL, result);
+}
+
+TEST(DeserializationTest, int8_t) {
+ Payload buffer;
+ std::int8_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // INT16 out of range.
+ buffer = {ENCODING_TYPE_INT16};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT16, error.encoding_type());
+
+ // INT32 out of range.
+ buffer = {ENCODING_TYPE_INT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type());
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int16_t) {
+ Payload buffer;
+ std::int16_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // INT32 out of range.
+ buffer = {ENCODING_TYPE_INT32};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT32, error.encoding_type());
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int32_t) {
+ Payload buffer;
+ std::int32_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // Min INT32.
+ buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2147483648, result);
+
+ // Max INT32.
+ buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(2147483647, result);
+
+ // INT64 out of range.
+ buffer = {ENCODING_TYPE_INT64};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_INT64, error.encoding_type());
+}
+
+TEST(DeserializationTest, int64_t) {
+ Payload buffer;
+ std::int64_t result = 0;
+ ErrorType error;
+
+ // Min NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32, result);
+
+ // Max NEGATIVE FIXINT.
+ buffer = {ENCODING_TYPE_NEGATIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-1, result);
+
+ // Min FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result);
+
+ // Max FIXINT.
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MAX};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-128, result);
+
+ // Max INT8.
+ buffer = {ENCODING_TYPE_INT8, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(127, result);
+
+ // Min INT16.
+ buffer = {ENCODING_TYPE_INT16, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-32768, result);
+
+ // Max INT16.
+ buffer = {ENCODING_TYPE_INT16, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(32767, result);
+
+ // Min INT32.
+ buffer = {ENCODING_TYPE_INT32, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2147483648, result);
+
+ // Max INT32.
+ buffer = {ENCODING_TYPE_INT32, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(2147483647, result);
+
+ // Min INT64.
+ buffer = {
+ ENCODING_TYPE_INT64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ // Believe it or not, this is actually the correct way to specify the most
+ // negative signed long long.
+ EXPECT_EQ(-9223372036854775807LL - 1, result);
+
+ // Max INT64.
+ buffer = {
+ ENCODING_TYPE_INT64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(9223372036854775807LL, result);
+}
+
+TEST(DeserializationTest, float) {
+ Payload buffer;
+ float result;
+ ErrorType error;
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroFloat, result);
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneFloat, result);
+}
+
+TEST(DeserializationTest, double) {
+ Payload buffer;
+ double result;
+ ErrorType error;
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kZeroFloatBytes[0], kZeroFloatBytes[1],
+ kZeroFloatBytes[2], kZeroFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroDouble, result);
+
+ // FLOAT64.
+ buffer = {ENCODING_TYPE_FLOAT64, kZeroDoubleBytes[0], kZeroDoubleBytes[1],
+ kZeroDoubleBytes[2], kZeroDoubleBytes[3], kZeroDoubleBytes[4],
+ kZeroDoubleBytes[5], kZeroDoubleBytes[6], kZeroDoubleBytes[7]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kZeroDouble, result);
+
+ // FLOAT32.
+ buffer = {ENCODING_TYPE_FLOAT32, kOneFloatBytes[0], kOneFloatBytes[1],
+ kOneFloatBytes[2], kOneFloatBytes[3]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneDouble, result);
+
+ // FLOAT64.
+ buffer = {ENCODING_TYPE_FLOAT64, kOneDoubleBytes[0], kOneDoubleBytes[1],
+ kOneDoubleBytes[2], kOneDoubleBytes[3], kOneDoubleBytes[4],
+ kOneDoubleBytes[5], kOneDoubleBytes[6], kOneDoubleBytes[7]};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kOneDouble, result);
+}
+
+TEST(DeserializationTest, Enum) {
+ Payload buffer;
+ enum Foo { kFoo, kBar, kBaz } result;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(kBar, result);
+}
+
+TEST(DeserializationTest, EnumClass) {
+ Payload buffer;
+ enum Foo { kFoo, kBar, kBaz } result;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(Foo::kBaz, result);
+}
+
+TEST(DeserializationTest, LocalHandle) {
+ Payload buffer;
+ LocalHandle result1;
+ LocalHandle result2;
+ ErrorType error;
+
+ buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0};
+ error = Deserialize(&result1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result1.Get());
+ result1.Release(); // Don't close fd 0.
+
+ std::tuple<LocalHandle&, LocalHandle&> t1(result1, result2);
+ buffer = decltype(buffer)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 2, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0, 0, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 1, 0});
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(0, result1.Get());
+ EXPECT_EQ(1, result2.Get());
+ result1.Release(); // Don't close fd 0.
+ result2.Release(); // Don't close fd 1.
+
+ buffer = {ENCODING_TYPE_FIXEXT2, ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xfe,
+ 0xff};
+ error = Deserialize(&result1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(-2, result1.Get());
+}
+
+TEST(DeserializationTest, string) {
+ Payload buffer;
+ std::string result = "";
+ ErrorType error;
+
+ // Min FIXSTR.
+ buffer = {ENCODING_TYPE_FIXSTR_MIN};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max FIXSTR.
+ buffer = {ENCODING_TYPE_FIXSTR_MAX};
+ buffer.Append((1 << 5) - 1, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string((1 << 5) - 1, 'x'), result);
+
+ // Min STR8.
+ buffer = {ENCODING_TYPE_STR8, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max STR8.
+ buffer = {ENCODING_TYPE_STR8, 0xff};
+ buffer.Append(0xff, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0xff, 'x'), result);
+
+ // Min STR16.
+ buffer = {ENCODING_TYPE_STR16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Max STR16.
+ buffer = {ENCODING_TYPE_STR16, 0xff, 0xff};
+ buffer.Append(0xffff, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0xffff, 'x'), result);
+
+ // Min STR32.
+ buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ("", result);
+
+ // Test STR32 with max STR16 + 1 bytes. It's not practical to test max
+ // STR32.
+ buffer = {ENCODING_TYPE_STR32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 'x');
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::string(0x10000, 'x'), result);
+}
+
+TEST(DeserializationTest, vector) {
+ Payload buffer;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ result;
+ Payload expected;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)((1 << 4) - 1, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append(0xffff, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)(0xffff, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 1);
+ error = Deserialize(&result, &buffer);
+ expected = decltype(expected)(0x10000, 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, map) {
+ Payload buffer;
+ std::map<std::uint32_t, std::uint32_t> result;
+ std::map<std::uint32_t, std::uint32_t> expected;
+ ErrorType error;
+
+ // Min FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error);
+
+ // Max FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 4) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error) << std::string(error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // MAP32 with max MAP16 + 1. It's not practical to test max MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16));
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16));
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, unordered_map) {
+ Payload buffer;
+ std::unordered_map<std::uint32_t, std::uint32_t> result;
+ std::unordered_map<std::uint32_t, std::uint32_t> expected;
+ ErrorType error;
+
+ // Min FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1};
+ error = Deserialize(&result, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_BUFFER, error);
+
+ // Max FIXMAP.
+ buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 4) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 4) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max MAP16.
+ buffer = {ENCODING_TYPE_MAP16, 0xff, 0xff};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16) - 1);
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16) - 1);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&result, &buffer);
+ expected = {};
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // MAP32 with max MAP16 + 1. It's not practical to test max MAP32.
+ buffer = {ENCODING_TYPE_MAP32, 0x00, 0x00, 0x01, 0x00};
+ InsertKeyValue<decltype(result)>(&buffer, (1 << 16));
+ error = Deserialize(&result, &buffer);
+ expected = MakeMap<decltype(expected)>((1 << 16));
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, array) {
+ Payload buffer;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ std::array<std::uint8_t, 0> a0;
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // Size mismatch.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::INSUFFICIENT_DESTINATION_SIZE, error);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 'x');
+ std::array<std::uint8_t, (1 << 4) - 1> a1, expected1;
+ for (auto& element : expected1)
+ element = 'x';
+ error = Deserialize(&a1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected1, a1);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append((1 << 16) - 1, 'x');
+ std::array<std::uint8_t, (1 << 16) - 1> a3, expected3;
+ for (auto& element : expected3)
+ element = 'x';
+ error = Deserialize(&a3, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected3, a3);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&a0, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append((1 << 16), 'x');
+ std::array<std::uint8_t, (1 << 16)> a4, expected4;
+ for (auto& element : expected4)
+ element = 'x';
+ error = Deserialize(&a4, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected4, a4);
+}
+
+TEST(DeserializationTest, ArrayWrapper) {
+ Payload buffer;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ result;
+ std::vector<std::uint8_t, DefaultInitializationAllocator<std::uint8_t>>
+ expected;
+ ErrorType error;
+
+ result.reserve(0x10000);
+ ArrayWrapper<std::uint8_t> wrapper(result.data(), result.capacity());
+
+ // Min FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max FIXARRAY.
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)((1 << 4) - 1, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Max ARRAY16.
+ buffer = {ENCODING_TYPE_ARRAY16, 0xff, 0xff};
+ buffer.Append(0xffff, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)(0xffff, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // Min ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&wrapper, &buffer);
+ expected = {};
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+
+ // ARRAY32 with max ARRAY16 + 1. It's not practical to test max ARRAY32.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x01, 0x00};
+ buffer.Append(0x10000, 1);
+ error = Deserialize(&wrapper, &buffer);
+ expected = decltype(expected)(0x10000, 1);
+ result.resize(wrapper.size());
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(DeserializationTest, pair) {
+ Payload buffer;
+ ErrorType error;
+
+ std::pair<int, int> p1;
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 1, 2};
+ error = Deserialize(&p1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_pair(1, 2), p1);
+}
+
+TEST(DeserializationTest, tuple) {
+ Payload buffer;
+ ErrorType error;
+
+ // Min FIXARRAY.
+ std::tuple<> t1;
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1); // Superfluous.
+
+ // Max FIXARRAY.
+ auto t2 = GetNTuple<15, int>(0);
+ buffer = {ENCODING_TYPE_FIXARRAY_MAX};
+ buffer.Append((1 << 4) - 1, 1);
+ error = Deserialize(&t2, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<15, int>(1)), t2);
+
+ // Min ARRAY16.
+ // Using t1 above.
+ buffer = {ENCODING_TYPE_ARRAY16, 0x00, 0x00};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1);
+
+ // ARRAY16 at Max FIXARRAY + 1
+ auto t3 = GetNTuple<(1 << 4), int>(0);
+ buffer = {ENCODING_TYPE_ARRAY16, 0x10, 0x00};
+ buffer.Append((1 << 4), 1);
+ error = Deserialize(&t3, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t3);
+
+ // Min ARRAY32.
+ // Using t1 from above.
+ buffer = {ENCODING_TYPE_ARRAY32, 0x00, 0x00, 0x00, 0x00};
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(std::make_tuple(), t1);
+
+ // ARRAY32 at Max FIXARRAY + 1
+ auto t4 = GetNTuple<(1 << 4), int>(0);
+ buffer = {ENCODING_TYPE_ARRAY32, 0x10, 0x00, 0x00, 0x00};
+ buffer.Append((1 << 4), 1);
+ error = Deserialize(&t4, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ((GetNTuple<(1 << 4), int>(1)), t4);
+
+ // Template instantiation depth is an issue for tuples with large numbers of
+ // elements. As these are not expected in practice, the limits of ARRAY16
+ // and ARRAY32 are not tested.
+}
+
+TEST(DeserializationTest, Serializable) {
+ Payload buffer;
+ ErrorType error;
+
+ buffer = decltype(buffer)(
+ {ENCODING_TYPE_FIXARRAY_MIN + 4, 10, ENCODING_TYPE_FLOAT32,
+ kZeroFloatBytes[0], kZeroFloatBytes[1], kZeroFloatBytes[2],
+ kZeroFloatBytes[3], ENCODING_TYPE_FIXSTR_MIN + 5, '1', '2', '3', '4',
+ '5', ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1});
+ TestType t1;
+ error = Deserialize(&t1, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(TestType(10, 0.f, "12345", TestType::Foo::kBar), t1);
+
+ buffer =
+ decltype(buffer)({ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_FIXEXT2,
+ ENCODING_EXT_TYPE_FILE_DESCRIPTOR, 0xff, 0xff});
+ TestTemplateType<LocalHandle> tt;
+ error = Deserialize(&tt, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_EQ(TestTemplateType<LocalHandle>(LocalHandle(-1)), tt);
+}
+
+TEST(DeserializationTest, Variant) {
+ Payload buffer;
+ ErrorType error;
+
+ Variant<int, bool, float> v;
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_NEGATIVE_FIXINT_MAX,
+ ENCODING_TYPE_NIL};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ EXPECT_TRUE(v.empty());
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 0,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 10};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(10, std::get<int>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1,
+ ENCODING_TYPE_TRUE};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1, ENCODING_TYPE_POSITIVE_FIXINT_MIN + 1,
+ ENCODING_TYPE_FALSE};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(false, std::get<bool>(v));
+
+ buffer = {ENCODING_TYPE_FIXMAP_MIN + 1,
+ ENCODING_TYPE_POSITIVE_FIXINT_MIN + 2,
+ ENCODING_TYPE_FLOAT32,
+ kOneFloatBytes[0],
+ kOneFloatBytes[1],
+ kOneFloatBytes[2],
+ kOneFloatBytes[3]};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::NO_ERROR, error);
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.0, std::get<float>(v));
+
+ // TODO(eieio): Add more deserialization tests for Variant.
+}
+
+TEST(DeserializationTest, ErrorType) {
+ Payload buffer;
+ ErrorType error;
+
+ std::uint8_t u8;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u8, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint16_t u16;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u16, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint32_t u32;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u32, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::uint64_t u64;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&u64, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int8_t i8;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i8, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int16_t i16;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i16, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int32_t i32;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i32, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::int64_t i64;
+ buffer = {ENCODING_TYPE_STR8};
+ error = Deserialize(&i64, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_INT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ std::string s;
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT};
+ error = Deserialize(&s, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_STRING, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type());
+
+ std::vector<std::uint8_t> v;
+ buffer = {ENCODING_TYPE_POSITIVE_FIXINT};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_ARRAY, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_POSITIVE_FIXINT, error.encoding_type());
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 1, ENCODING_TYPE_STR8};
+ error = Deserialize(&v, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_ENCODING, error);
+ EXPECT_EQ(ENCODING_CLASS_UINT, error.encoding_class());
+ EXPECT_EQ(ENCODING_TYPE_STR8, error.encoding_type());
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 2, 0, 1};
+ std::tuple<int> t;
+ error = Deserialize(&t, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error);
+
+ buffer = {ENCODING_TYPE_FIXARRAY_MIN + 3, 0, 1, 2};
+ std::pair<int, int> p;
+ error = Deserialize(&p, &buffer);
+ EXPECT_EQ(ErrorCode::UNEXPECTED_TYPE_SIZE, error);
+}
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
new file mode 100644
index 0000000000..fab4770b7f
--- /dev/null
+++ b/libs/vr/libpdx/service.cpp
@@ -0,0 +1,684 @@
+#define LOG_TAG "ServiceFramework"
+#include "pdx/service.h"
+
+#include <fcntl.h>
+#include <log/log.h>
+#include <utils/misc.h>
+
+#include <algorithm>
+#include <cstdint>
+
+#include <pdx/trace.h>
+
+#define TRACE 0
+
+namespace android {
+namespace pdx {
+
+std::shared_ptr<Channel> Channel::GetFromMessageInfo(const MessageInfo& info) {
+ return info.channel ? info.channel->shared_from_this()
+ : std::shared_ptr<Channel>();
+}
+
+Message::Message() : replied_(true) {}
+
+Message::Message(const MessageInfo& info)
+ : service_{Service::GetFromMessageInfo(info)},
+ channel_{Channel::GetFromMessageInfo(info)},
+ info_{info},
+ replied_{IsImpulse()} {
+ auto svc = service_.lock();
+ if (svc)
+ state_ = svc->endpoint()->AllocateMessageState();
+}
+
+// C++11 specifies the move semantics for shared_ptr but not weak_ptr. This
+// means we have to manually implement the desired move semantics for Message.
+Message::Message(Message&& other) { *this = std::move(other); }
+
+Message& Message::operator=(Message&& other) {
+ Destroy();
+ auto base = reinterpret_cast<std::uint8_t*>(&info_);
+ std::fill(&base[0], &base[sizeof(info_)], 0);
+ replied_ = true;
+ std::swap(service_, other.service_);
+ std::swap(channel_, other.channel_);
+ std::swap(info_, other.info_);
+ std::swap(state_, other.state_);
+ std::swap(replied_, other.replied_);
+ return *this;
+}
+
+Message::~Message() { Destroy(); }
+
+void Message::Destroy() {
+ auto svc = service_.lock();
+ if (svc) {
+ if (!replied_) {
+ ALOGE(
+ "ERROR: Service \"%s\" failed to reply to message: op=%d pid=%d "
+ "cid=%d\n",
+ svc->name_.c_str(), info_.op, info_.pid, info_.cid);
+ svc->DefaultHandleMessage(*this);
+ }
+ svc->endpoint()->FreeMessageState(state_);
+ }
+ state_ = nullptr;
+ service_.reset();
+ channel_.reset();
+}
+
+const std::uint8_t* Message::ImpulseBegin() const {
+ return reinterpret_cast<const std::uint8_t*>(info_.impulse);
+}
+
+const std::uint8_t* Message::ImpulseEnd() const {
+ return ImpulseBegin() + (IsImpulse() ? GetSendLength() : 0);
+}
+
+Status<size_t> Message::ReadVector(const struct iovec* vector,
+ size_t vector_length) {
+ PDX_TRACE_NAME("Message::ReadVector");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->ReadMessageData(this, vector, vector_length);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<void> Message::ReadVectorAll(const struct iovec* vector,
+ size_t vector_length) {
+ PDX_TRACE_NAME("Message::ReadVectorAll");
+ if (auto svc = service_.lock()) {
+ const auto status =
+ svc->endpoint()->ReadMessageData(this, vector, vector_length);
+ if (!status)
+ return status.error_status();
+ size_t size_to_read = 0;
+ for (size_t i = 0; i < vector_length; i++)
+ size_to_read += vector[i].iov_len;
+ if (status.get() < size_to_read)
+ return ErrorStatus{EIO};
+ return {};
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<size_t> Message::Read(void* buffer, size_t length) {
+ PDX_TRACE_NAME("Message::Read");
+ if (auto svc = service_.lock()) {
+ const struct iovec vector = {buffer, length};
+ return svc->endpoint()->ReadMessageData(this, &vector, 1);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<size_t> Message::WriteVector(const struct iovec* vector,
+ size_t vector_length) {
+ PDX_TRACE_NAME("Message::WriteVector");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->WriteMessageData(this, vector, vector_length);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<void> Message::WriteVectorAll(const struct iovec* vector,
+ size_t vector_length) {
+ PDX_TRACE_NAME("Message::WriteVector");
+ if (auto svc = service_.lock()) {
+ const auto status =
+ svc->endpoint()->WriteMessageData(this, vector, vector_length);
+ if (!status)
+ return status.error_status();
+ size_t size_to_write = 0;
+ for (size_t i = 0; i < vector_length; i++)
+ size_to_write += vector[i].iov_len;
+ if (status.get() < size_to_write)
+ return ErrorStatus{EIO};
+ return {};
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<size_t> Message::Write(const void* buffer, size_t length) {
+ PDX_TRACE_NAME("Message::Write");
+ if (auto svc = service_.lock()) {
+ const struct iovec vector = {const_cast<void*>(buffer), length};
+ return svc->endpoint()->WriteMessageData(this, &vector, 1);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<FileReference> Message::PushFileHandle(const LocalHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->PushFileHandle(this, handle);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<FileReference> Message::PushFileHandle(const BorrowedHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->PushFileHandle(this, handle);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<FileReference> Message::PushFileHandle(const RemoteHandle& handle) {
+ PDX_TRACE_NAME("Message::PushFileHandle");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->PushFileHandle(this, handle);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<ChannelReference> Message::PushChannelHandle(
+ const LocalChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->PushChannelHandle(this, handle);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<ChannelReference> Message::PushChannelHandle(
+ const BorrowedChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->PushChannelHandle(this, handle);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<ChannelReference> Message::PushChannelHandle(
+ const RemoteChannelHandle& handle) {
+ PDX_TRACE_NAME("Message::PushChannelHandle");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->PushChannelHandle(this, handle);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+bool Message::GetFileHandle(FileReference ref, LocalHandle* handle) {
+ PDX_TRACE_NAME("Message::GetFileHandle");
+ auto svc = service_.lock();
+ if (!svc)
+ return false;
+
+ if (ref >= 0) {
+ *handle = svc->endpoint()->GetFileHandle(this, ref);
+ if (!handle->IsValid())
+ return false;
+ } else {
+ *handle = LocalHandle{ref};
+ }
+ return true;
+}
+
+bool Message::GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) {
+ PDX_TRACE_NAME("Message::GetChannelHandle");
+ auto svc = service_.lock();
+ if (!svc)
+ return false;
+
+ if (ref >= 0) {
+ *handle = svc->endpoint()->GetChannelHandle(this, ref);
+ if (!handle->valid())
+ return false;
+ } else {
+ *handle = LocalChannelHandle{nullptr, ref};
+ }
+ return true;
+}
+
+Status<void> Message::Reply(int return_code) {
+ PDX_TRACE_NAME("Message::Reply");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ const auto ret = svc->endpoint()->MessageReply(this, return_code);
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::ReplyFileDescriptor(unsigned int fd) {
+ PDX_TRACE_NAME("Message::ReplyFileDescriptor");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ const auto ret = svc->endpoint()->MessageReplyFd(this, fd);
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::ReplyError(unsigned int error) {
+ PDX_TRACE_NAME("Message::ReplyError");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ const auto ret =
+ svc->endpoint()->MessageReply(this, -static_cast<int>(error));
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::Reply(const LocalHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ Status<void> ret;
+
+ if (handle)
+ ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+ else
+ ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::Reply(const BorrowedHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ Status<void> ret;
+
+ if (handle)
+ ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+ else
+ ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::Reply(const RemoteHandle& handle) {
+ PDX_TRACE_NAME("Message::ReplyFileHandle");
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ Status<void> ret;
+
+ if (handle)
+ ret = svc->endpoint()->MessageReply(this, handle.Get());
+ else
+ ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::Reply(const LocalChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::Reply(const BorrowedChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::Reply(const RemoteChannelHandle& handle) {
+ auto svc = service_.lock();
+ if (!replied_ && svc) {
+ const auto ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+ replied_ = ret.ok();
+ return ret;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+}
+
+Status<void> Message::ModifyChannelEvents(int clear_mask, int set_mask) {
+ PDX_TRACE_NAME("Message::ModifyChannelEvents");
+ if (auto svc = service_.lock()) {
+ return svc->endpoint()->ModifyChannelEvents(info_.cid, clear_mask,
+ set_mask);
+ } else {
+ return ErrorStatus{ESHUTDOWN};
+ }
+}
+
+Status<RemoteChannelHandle> Message::PushChannel(
+ int flags, const std::shared_ptr<Channel>& channel, int* channel_id) {
+ PDX_TRACE_NAME("Message::PushChannel");
+ if (auto svc = service_.lock()) {
+ return svc->PushChannel(this, flags, channel, channel_id);
+ } else {
+ return ErrorStatus(ESHUTDOWN);
+ }
+}
+
+Status<RemoteChannelHandle> Message::PushChannel(
+ Service* service, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id) {
+ PDX_TRACE_NAME("Message::PushChannel");
+ return service->PushChannel(this, flags, channel, channel_id);
+}
+
+Status<int> Message::CheckChannel(ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Message::CheckChannel");
+ if (auto svc = service_.lock()) {
+ return svc->CheckChannel(this, ref, channel);
+ } else {
+ return ErrorStatus(ESHUTDOWN);
+ }
+}
+
+Status<int> Message::CheckChannel(const Service* service, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Message::CheckChannel");
+ return service->CheckChannel(this, ref, channel);
+}
+
+pid_t Message::GetProcessId() const { return info_.pid; }
+
+pid_t Message::GetThreadId() const { return info_.tid; }
+
+uid_t Message::GetEffectiveUserId() const { return info_.euid; }
+
+gid_t Message::GetEffectiveGroupId() const { return info_.egid; }
+
+int Message::GetChannelId() const { return info_.cid; }
+
+int Message::GetMessageId() const { return info_.mid; }
+
+int Message::GetOp() const { return info_.op; }
+
+int Message::GetFlags() const { return info_.flags; }
+
+size_t Message::GetSendLength() const { return info_.send_len; }
+
+size_t Message::GetReceiveLength() const { return info_.recv_len; }
+
+size_t Message::GetFileDescriptorCount() const { return info_.fd_count; }
+
+std::shared_ptr<Channel> Message::GetChannel() const { return channel_.lock(); }
+
+Status<void> Message::SetChannel(const std::shared_ptr<Channel>& chan) {
+ channel_ = chan;
+ Status<void> status;
+ if (auto svc = service_.lock())
+ status = svc->SetChannel(info_.cid, chan);
+ return status;
+}
+
+std::shared_ptr<Service> Message::GetService() const { return service_.lock(); }
+
+const MessageInfo& Message::GetInfo() const { return info_; }
+
+Service::Service(const std::string& name, std::unique_ptr<Endpoint> endpoint)
+ : name_(name), endpoint_{std::move(endpoint)} {
+ if (!endpoint_)
+ return;
+
+ const auto status = endpoint_->SetService(this);
+ ALOGE_IF(!status, "Failed to set service context because: %s",
+ status.GetErrorMessage().c_str());
+}
+
+Service::~Service() {
+ if (endpoint_) {
+ const auto status = endpoint_->SetService(nullptr);
+ ALOGE_IF(!status, "Failed to clear service context because: %s",
+ status.GetErrorMessage().c_str());
+ }
+}
+
+std::shared_ptr<Service> Service::GetFromMessageInfo(const MessageInfo& info) {
+ return info.service ? info.service->shared_from_this()
+ : std::shared_ptr<Service>();
+}
+
+bool Service::IsInitialized() const { return endpoint_.get() != nullptr; }
+
+std::shared_ptr<Channel> Service::OnChannelOpen(Message& /*message*/) {
+ return nullptr;
+}
+
+void Service::OnChannelClose(Message& /*message*/,
+ const std::shared_ptr<Channel>& /*channel*/) {}
+
+Status<void> Service::SetChannel(int channel_id,
+ const std::shared_ptr<Channel>& channel) {
+ PDX_TRACE_NAME("Service::SetChannel");
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ const auto status = endpoint_->SetChannel(channel_id, channel.get());
+ if (!status) {
+ ALOGE("%s::SetChannel: Failed to set channel context: %s\n", name_.c_str(),
+ status.GetErrorMessage().c_str());
+
+ // It's possible someone mucked with things behind our back by calling the C
+ // API directly. Since we know the channel id isn't valid, make sure we
+ // don't have it in the channels map.
+ if (status.error() == ENOENT)
+ channels_.erase(channel_id);
+ } else {
+ if (channel != nullptr)
+ channels_[channel_id] = channel;
+ else
+ channels_.erase(channel_id);
+ }
+ return status;
+}
+
+std::shared_ptr<Channel> Service::GetChannel(int channel_id) const {
+ PDX_TRACE_NAME("Service::GetChannel");
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ auto search = channels_.find(channel_id);
+ if (search != channels_.end())
+ return search->second;
+ else
+ return nullptr;
+}
+
+Status<void> Service::CloseChannel(int channel_id) {
+ PDX_TRACE_NAME("Service::CloseChannel");
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ const auto status = endpoint_->CloseChannel(channel_id);
+
+ // Always erase the map entry, in case someone mucked with things behind our
+ // back using the C API directly.
+ channels_.erase(channel_id);
+
+ return status;
+}
+
+Status<void> Service::ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) {
+ PDX_TRACE_NAME("Service::ModifyChannelEvents");
+ return endpoint_->ModifyChannelEvents(channel_id, clear_mask, set_mask);
+}
+
+Status<RemoteChannelHandle> Service::PushChannel(
+ Message* message, int flags, const std::shared_ptr<Channel>& channel,
+ int* channel_id) {
+ PDX_TRACE_NAME("Service::PushChannel");
+
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ int channel_id_temp = -1;
+ Status<RemoteChannelHandle> ret =
+ endpoint_->PushChannel(message, flags, channel.get(), &channel_id_temp);
+ ALOGE_IF(!ret.ok(), "%s::PushChannel: Failed to push channel: %s",
+ name_.c_str(), strerror(ret.error()));
+
+ if (channel && channel_id_temp != -1)
+ channels_[channel_id_temp] = channel;
+ if (channel_id)
+ *channel_id = channel_id_temp;
+
+ return ret;
+}
+
+Status<int> Service::CheckChannel(const Message* message, ChannelReference ref,
+ std::shared_ptr<Channel>* channel) const {
+ PDX_TRACE_NAME("Service::CheckChannel");
+
+ // Synchronization to maintain consistency between the kernel's channel
+ // context pointer and the userspace channels_ map. Other threads may attempt
+ // to modify the map at the same time, which could cause the channel context
+ // pointer returned by the kernel to be invalid.
+ std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+ Channel* channel_context = nullptr;
+ Status<int> ret = endpoint_->CheckChannel(
+ message, ref, channel ? &channel_context : nullptr);
+ if (ret && channel) {
+ if (channel_context)
+ *channel = channel_context->shared_from_this();
+ else
+ *channel = nullptr;
+ }
+
+ return ret;
+}
+
+std::string Service::DumpState(size_t /*max_length*/) { return ""; }
+
+Status<void> Service::HandleMessage(Message& message) {
+ return DefaultHandleMessage(message);
+}
+
+void Service::HandleImpulse(Message& /*impulse*/) {}
+
+Status<void> Service::HandleSystemMessage(Message& message) {
+ const MessageInfo& info = message.GetInfo();
+
+ switch (info.op) {
+ case opcodes::CHANNEL_OPEN: {
+ ALOGD("%s::OnChannelOpen: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ message.SetChannel(OnChannelOpen(message));
+ return message.Reply(0);
+ }
+
+ case opcodes::CHANNEL_CLOSE: {
+ ALOGD("%s::OnChannelClose: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ OnChannelClose(message, Channel::GetFromMessageInfo(info));
+ message.SetChannel(nullptr);
+ return message.Reply(0);
+ }
+
+ case opcodes::REPORT_SYSPROP_CHANGE:
+ ALOGD("%s:REPORT_SYSPROP_CHANGE: pid=%d cid=%d\n", name_.c_str(),
+ info.pid, info.cid);
+ OnSysPropChange();
+ android::report_sysprop_change();
+ return message.Reply(0);
+
+ case opcodes::DUMP_STATE: {
+ ALOGD("%s:DUMP_STATE: pid=%d cid=%d\n", name_.c_str(), info.pid,
+ info.cid);
+ auto response = DumpState(message.GetReceiveLength());
+ const size_t response_size = response.size() < message.GetReceiveLength()
+ ? response.size()
+ : message.GetReceiveLength();
+ const Status<size_t> status =
+ message.Write(response.data(), response_size);
+ if (status && status.get() < response_size)
+ return message.ReplyError(EIO);
+ else
+ return message.Reply(status);
+ }
+
+ default:
+ return ErrorStatus{EOPNOTSUPP};
+ }
+}
+
+Status<void> Service::DefaultHandleMessage(Message& message) {
+ const MessageInfo& info = message.GetInfo();
+
+ ALOGD_IF(TRACE, "Service::DefaultHandleMessage: pid=%d cid=%d op=%d\n",
+ info.pid, info.cid, info.op);
+
+ switch (info.op) {
+ case opcodes::CHANNEL_OPEN:
+ case opcodes::CHANNEL_CLOSE:
+ case opcodes::REPORT_SYSPROP_CHANGE:
+ case opcodes::DUMP_STATE:
+ return HandleSystemMessage(message);
+
+ default:
+ return message.ReplyError(EOPNOTSUPP);
+ }
+}
+
+void Service::OnSysPropChange() {}
+
+Status<void> Service::ReceiveAndDispatch() {
+ Message message;
+ const auto status = endpoint_->MessageReceive(&message);
+ if (!status) {
+ ALOGE("Failed to receive message: %s\n", status.GetErrorMessage().c_str());
+ return status;
+ }
+
+ std::shared_ptr<Service> service = message.GetService();
+
+ if (!service) {
+ ALOGE("Service::ReceiveAndDispatch: service context is NULL!!!\n");
+ // Don't block the sender indefinitely in this error case.
+ endpoint_->MessageReply(&message, -EINVAL);
+ return ErrorStatus{EINVAL};
+ }
+
+ if (message.IsImpulse()) {
+ service->HandleImpulse(message);
+ return {};
+ } else if (service->HandleSystemMessage(message)) {
+ return {};
+ } else {
+ return service->HandleMessage(message);
+ }
+}
+
+Status<void> Service::Cancel() { return endpoint_->Cancel(); }
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/service_tests.cpp b/libs/vr/libpdx/service_tests.cpp
new file mode 100644
index 0000000000..c7412b7aae
--- /dev/null
+++ b/libs/vr/libpdx/service_tests.cpp
@@ -0,0 +1,807 @@
+#include <pdx/service.h>
+
+#include <memory>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <pdx/mock_service_endpoint.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::ChannelReference;
+using android::pdx::ErrorStatus;
+using android::pdx::FileReference;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::MockEndpoint;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::Service;
+using android::pdx::Status;
+
+using testing::A;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Matcher;
+using testing::Ref;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::WithArg;
+using testing::WithoutArgs;
+using testing::_;
+
+namespace {
+
+// Helper functions to construct fake void pointers for tests.
+inline void* IntToPtr(intptr_t addr) { return reinterpret_cast<void*>(addr); }
+
+// Helper matchers for working with iovec structures in tests.
+// Simple matcher for one element iovec array:
+// EXPECT_CALL(mock, method(IoVecMatcher(ptr, size)));
+MATCHER_P2(IoVecMatcher, ptr, size, "") {
+ return arg->iov_base == ptr && arg->iov_len == size;
+}
+
+// Matcher for an array of iovecs:
+// EXPECT_CALL(mock,
+// method(IoVecMatcher(IoVecArray{{ptr1, size1}, {ptr2, size2}})));
+using IoVecArray = std::vector<iovec>;
+MATCHER_P(IoVecMatcher, iovec_array, "") {
+ for (const iovec& item : iovec_array) {
+ if (arg->iov_base != item.iov_base || arg->iov_len != item.iov_len)
+ return false;
+ arg++;
+ }
+ return true;
+}
+
+using IoVecData = std::vector<std::string>;
+MATCHER_P(IoVecDataMatcher, iovec_data, "") {
+ for (const std::string& item : iovec_data) {
+ std::string data{reinterpret_cast<const char*>(arg->iov_base),
+ arg->iov_len};
+ if (data != item)
+ return false;
+ arg++;
+ }
+ return true;
+}
+
+MATCHER_P(FileHandleMatcher, value, "") { return arg.Get() == value; }
+MATCHER_P(ChannelHandleMatcher, value, "") { return arg.value() == value; }
+
+enum : int {
+ kTestPid = 1,
+ kTestTid,
+ kTestCid,
+ kTestMid,
+ kTestEuid,
+ kTestEgid,
+ kTestOp,
+};
+
+class MockService : public Service {
+ public:
+ using Service::Service;
+ MOCK_METHOD1(OnChannelOpen, std::shared_ptr<Channel>(Message& message));
+ MOCK_METHOD2(OnChannelClose,
+ void(Message& message, const std::shared_ptr<Channel>& channel));
+ MOCK_METHOD1(HandleMessage, Status<void>(Message& message));
+ MOCK_METHOD1(HandleImpulse, void(Message& impulse));
+ MOCK_METHOD0(OnSysPropChange, void());
+ MOCK_METHOD1(DumpState, std::string(size_t max_length));
+};
+
+class ServiceTest : public testing::Test {
+ public:
+ ServiceTest() {
+ auto endpoint = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint, SetService(_))
+ .Times(2)
+ .WillRepeatedly(Return(Status<void>{}));
+ service_ = std::make_shared<MockService>("MockSvc", std::move(endpoint));
+ }
+
+ MockEndpoint* endpoint() {
+ return static_cast<MockEndpoint*>(service_->endpoint());
+ }
+
+ void SetupMessageInfo(MessageInfo* info, int32_t op, bool impulse = false) {
+ info->pid = kTestPid;
+ info->tid = kTestTid;
+ info->cid = kTestCid;
+ info->mid = impulse ? Message::IMPULSE_MESSAGE_ID : kTestMid;
+ info->euid = kTestEuid;
+ info->egid = kTestEgid;
+ info->op = op;
+ info->flags = 0;
+ info->service = service_.get();
+ info->channel = nullptr;
+ info->send_len = 0;
+ info->recv_len = 0;
+ info->fd_count = 0;
+ memset(info->impulse, 0, sizeof(info->impulse));
+ }
+
+ void SetupMessageInfoAndDefaultExpectations(MessageInfo* info, int32_t op,
+ bool impulse = false) {
+ SetupMessageInfo(info, op, impulse);
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState));
+ EXPECT_CALL(*endpoint(), FreeMessageState(kState));
+ }
+
+ void ExpectDefaultHandleMessage() {
+ EXPECT_CALL(*endpoint(), MessageReply(_, -EOPNOTSUPP))
+ .WillOnce(Return(Status<void>{}));
+ }
+
+ std::shared_ptr<MockService> service_;
+ void* kState = IntToPtr(123456);
+};
+
+class ServiceMessageTest : public ServiceTest {
+ public:
+ ServiceMessageTest() {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ message_ = std::make_unique<Message>(info);
+ }
+
+ std::unique_ptr<Message> message_;
+};
+
+} // anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// Service class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceTest, IsInitialized) {
+ EXPECT_TRUE(service_->IsInitialized());
+ service_ = std::make_shared<MockService>("MockSvc2", nullptr);
+ EXPECT_FALSE(service_->IsInitialized());
+}
+
+TEST_F(ServiceTest, ConstructMessage) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp);
+ auto test_channel = std::make_shared<Channel>();
+ info.channel = test_channel.get();
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(kState));
+
+ Message message{info};
+
+ EXPECT_FALSE(message.IsImpulse());
+ EXPECT_EQ(kTestPid, message.GetProcessId());
+ EXPECT_EQ(kTestTid, message.GetThreadId());
+ EXPECT_EQ(kTestCid, message.GetChannelId());
+ EXPECT_EQ(kTestMid, message.GetMessageId());
+ EXPECT_EQ(kTestEuid, message.GetEffectiveUserId());
+ EXPECT_EQ(kTestEgid, message.GetEffectiveGroupId());
+ EXPECT_EQ(kTestOp, message.GetOp());
+ EXPECT_EQ(service_, message.GetService());
+ EXPECT_EQ(test_channel, message.GetChannel());
+ EXPECT_FALSE(message.replied());
+ EXPECT_FALSE(message.IsChannelExpired());
+ EXPECT_FALSE(message.IsServiceExpired());
+ EXPECT_EQ(kState, message.GetState());
+
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), FreeMessageState(kState));
+}
+
+TEST_F(ServiceTest, ConstructImpulseMessage) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp, true);
+ auto test_channel = std::make_shared<Channel>();
+ info.channel = test_channel.get();
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr));
+
+ Message message{info};
+
+ EXPECT_TRUE(message.IsImpulse());
+ EXPECT_EQ(kTestOp, message.GetOp());
+ EXPECT_EQ(service_, message.GetService());
+ EXPECT_EQ(test_channel, message.GetChannel());
+ EXPECT_TRUE(message.replied());
+ EXPECT_FALSE(message.IsChannelExpired());
+ EXPECT_FALSE(message.IsServiceExpired());
+
+ // DefaultHandleMessage should not be called here.
+ EXPECT_CALL(*endpoint(), FreeMessageState(nullptr));
+}
+
+TEST_F(ServiceTest, HandleMessageChannelOpen) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::CHANNEL_OPEN);
+ Message message{info};
+
+ auto channel = std::make_shared<Channel>();
+ EXPECT_CALL(*service_, OnChannelOpen(Ref(message))).WillOnce(Return(channel));
+ EXPECT_CALL(*endpoint(), SetChannel(kTestCid, channel.get()))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0))
+ .WillOnce(Return(Status<void>{}));
+
+ EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageChannelClose) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::CHANNEL_CLOSE);
+ auto channel = std::make_shared<Channel>();
+ info.channel = channel.get();
+ Message message{info};
+
+ EXPECT_CALL(*service_, OnChannelClose(Ref(message), channel));
+ EXPECT_CALL(*endpoint(), SetChannel(kTestCid, nullptr))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0))
+ .WillOnce(Return(Status<void>{}));
+
+ EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnSysPropChange) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(
+ &info, android::pdx::opcodes::REPORT_SYSPROP_CHANGE);
+ Message message{info};
+
+ EXPECT_CALL(*service_, OnSysPropChange());
+ EXPECT_CALL(*endpoint(), MessageReply(&message, 0))
+ .WillOnce(Return(Status<void>{}));
+
+ EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpState) {
+ const size_t kRecvBufSize = 1000;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "foo";
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1))
+ .WillOnce(Return(kReply.size()));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, kReply.size()))
+ .WillOnce(Return(Status<void>{}));
+
+ EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpStateTooLarge) {
+ const size_t kRecvBufSize = 3;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "0123456789";
+ const std::string kActualReply = kReply.substr(0, kRecvBufSize);
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kActualReply}), 1))
+ .WillOnce(Return(kActualReply.size()));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, kActualReply.size()))
+ .WillOnce(Return(Status<void>{}));
+
+ EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageOnDumpStateFail) {
+ const size_t kRecvBufSize = 1000;
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info,
+ android::pdx::opcodes::DUMP_STATE);
+ info.recv_len = kRecvBufSize;
+ Message message{info};
+
+ const std::string kReply = "foo";
+ EXPECT_CALL(*service_, DumpState(kRecvBufSize)).WillOnce(Return(kReply));
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(&message, IoVecDataMatcher(IoVecData{kReply}), 1))
+ .WillOnce(Return(1));
+ EXPECT_CALL(*endpoint(), MessageReply(&message, -EIO))
+ .WillOnce(Return(Status<void>{}));
+
+ EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageCustom) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ Message message{info};
+
+ EXPECT_CALL(*endpoint(), MessageReply(&message, -EOPNOTSUPP))
+ .WillOnce(Return(Status<void>{}));
+
+ EXPECT_TRUE(service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, ReplyMessageWithoutService) {
+ MessageInfo info;
+ SetupMessageInfo(&info, kTestOp);
+ EXPECT_CALL(*endpoint(), AllocateMessageState()).WillOnce(Return(nullptr));
+
+ Message message{info};
+
+ EXPECT_FALSE(message.IsServiceExpired());
+ service_.reset();
+ EXPECT_TRUE(message.IsServiceExpired());
+
+ EXPECT_EQ(EINVAL, message.Reply(12).error());
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchMessage) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+ ExpectDefaultHandleMessage();
+
+ auto on_receive = [&info](Message* message) -> Status<void> {
+ *message = Message{info};
+ return {};
+ };
+ EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+ EXPECT_CALL(*service_, HandleMessage(_)).WillOnce(Return(Status<void>{}));
+
+ EXPECT_TRUE(service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchImpulse) {
+ MessageInfo info;
+ SetupMessageInfoAndDefaultExpectations(&info, kTestOp, true);
+
+ auto on_receive = [&info](Message* message) -> Status<void> {
+ *message = Message{info};
+ return {};
+ };
+ EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+ EXPECT_CALL(*service_, HandleImpulse(_));
+
+ EXPECT_TRUE(service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, Cancel) {
+ EXPECT_CALL(*endpoint(), Cancel()).WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(service_->Cancel());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Message class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceMessageTest, Reply) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_FALSE(message_->replied());
+ EXPECT_TRUE(message_->Reply(12));
+ EXPECT_TRUE(message_->replied());
+
+ EXPECT_EQ(EINVAL, message_->Reply(12).error()); // Already replied.
+}
+
+TEST_F(ServiceMessageTest, ReplyFail) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_EQ(EIO, message_->Reply(12).error());
+
+ ExpectDefaultHandleMessage();
+}
+
+TEST_F(ServiceMessageTest, ReplyError) {
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -12))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->ReplyError(12));
+}
+
+TEST_F(ServiceMessageTest, ReplyFileDescriptor) {
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), 5))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->ReplyFileDescriptor(5));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandle) {
+ const int kFakeFd = 12345;
+ LocalHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandleError) {
+ LocalHandle handle{-EINVAL};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EINVAL))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandle) {
+ const int kFakeFd = 12345;
+ BorrowedHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandleError) {
+ BorrowedHandle handle{-EACCES};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EACCES))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandle) {
+ RemoteHandle handle{123};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), handle.Get()))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandleError) {
+ RemoteHandle handle{-EIO};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EIO))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalChannelHandle) {
+ LocalChannelHandle handle{nullptr, 12345};
+ EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+ message_.get(), A<const LocalChannelHandle&>()))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedChannelHandle) {
+ BorrowedChannelHandle handle{12345};
+ EXPECT_CALL(*endpoint(),
+ MessageReplyChannelHandle(message_.get(),
+ A<const BorrowedChannelHandle&>()))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteChannelHandle) {
+ RemoteChannelHandle handle{12345};
+ EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+ message_.get(), A<const RemoteChannelHandle&>()))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusInt) {
+ Status<int> status{123};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), status.get()))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusError) {
+ Status<int> status{ErrorStatus{EIO}};
+ EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -status.error()))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, Read) {
+ ExpectDefaultHandleMessage();
+ void* const kDataBuffer = IntToPtr(12345);
+ const size_t kDataSize = 100;
+ EXPECT_CALL(
+ *endpoint(),
+ ReadMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1))
+ .WillOnce(Return(50))
+ .WillOnce(Return(ErrorStatus{EACCES}));
+ EXPECT_EQ(50u, message_->Read(kDataBuffer, kDataSize).get());
+ EXPECT_EQ(EACCES, message_->Read(kDataBuffer, kDataSize).error());
+}
+
+TEST_F(ServiceMessageTest, ReadVector) {
+ ExpectDefaultHandleMessage();
+ char buffer1[10];
+ char buffer2[20];
+ iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}};
+ EXPECT_CALL(*endpoint(),
+ ReadMessageData(
+ message_.get(),
+ IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2))
+ .WillOnce(Return(30))
+ .WillOnce(Return(15))
+ .WillOnce(Return(ErrorStatus{EBADF}));
+ EXPECT_EQ(30u, message_->ReadVector(vec, 2).get());
+ EXPECT_EQ(15u, message_->ReadVector(vec).get());
+ EXPECT_EQ(EBADF, message_->ReadVector(vec).error());
+}
+
+TEST_F(ServiceMessageTest, Write) {
+ ExpectDefaultHandleMessage();
+ void* const kDataBuffer = IntToPtr(12345);
+ const size_t kDataSize = 100;
+ EXPECT_CALL(
+ *endpoint(),
+ WriteMessageData(message_.get(), IoVecMatcher(kDataBuffer, kDataSize), 1))
+ .WillOnce(Return(50))
+ .WillOnce(Return(ErrorStatus{EBADMSG}));
+ EXPECT_EQ(50u, message_->Write(kDataBuffer, kDataSize).get());
+ EXPECT_EQ(EBADMSG, message_->Write(kDataBuffer, kDataSize).error());
+}
+
+TEST_F(ServiceMessageTest, WriteVector) {
+ ExpectDefaultHandleMessage();
+ char buffer1[10];
+ char buffer2[20];
+ iovec vec[] = {{buffer1, sizeof(buffer1)}, {buffer2, sizeof(buffer2)}};
+ EXPECT_CALL(*endpoint(),
+ WriteMessageData(
+ message_.get(),
+ IoVecMatcher(IoVecArray{std::begin(vec), std::end(vec)}), 2))
+ .WillOnce(Return(30))
+ .WillOnce(Return(15))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_EQ(30u, message_->WriteVector(vec, 2).get());
+ EXPECT_EQ(15u, message_->WriteVector(vec).get());
+ EXPECT_EQ(EIO, message_->WriteVector(vec, 2).error());
+}
+
+TEST_F(ServiceMessageTest, PushLocalFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ LocalHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const LocalHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(12))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_EQ(12, message_->PushFileHandle(handle).get());
+ EXPECT_EQ(EIO, message_->PushFileHandle(handle).error());
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, PushBorrowedFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ BorrowedHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const BorrowedHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(13))
+ .WillOnce(Return(ErrorStatus{EACCES}));
+ EXPECT_EQ(13, message_->PushFileHandle(handle).get());
+ EXPECT_EQ(EACCES, message_->PushFileHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, PushRemoteFileHandle) {
+ ExpectDefaultHandleMessage();
+ const int kFakeFd = 12345;
+ RemoteHandle handle{kFakeFd};
+ EXPECT_CALL(*endpoint(),
+ PushFileHandle(message_.get(), Matcher<const RemoteHandle&>(
+ FileHandleMatcher(kFakeFd))))
+ .WillOnce(Return(kFakeFd))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_EQ(kFakeFd, message_->PushFileHandle(handle).get());
+ EXPECT_EQ(EIO, message_->PushFileHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, PushLocalChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ LocalChannelHandle handle{nullptr, kValue};
+ EXPECT_CALL(*endpoint(), PushChannelHandle(message_.get(),
+ Matcher<const LocalChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(7))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_EQ(7, message_->PushChannelHandle(handle).get());
+ EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, PushBorrowedChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ BorrowedChannelHandle handle{kValue};
+ EXPECT_CALL(
+ *endpoint(),
+ PushChannelHandle(message_.get(), Matcher<const BorrowedChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(8))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_EQ(8, message_->PushChannelHandle(handle).get());
+ EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, PushRemoteChannelHandle) {
+ ExpectDefaultHandleMessage();
+ int32_t kValue = 12345;
+ RemoteChannelHandle handle{kValue};
+ EXPECT_CALL(
+ *endpoint(),
+ PushChannelHandle(message_.get(), Matcher<const RemoteChannelHandle&>(
+ ChannelHandleMatcher(kValue))))
+ .WillOnce(Return(kValue))
+ .WillOnce(Return(ErrorStatus{EIO}));
+ EXPECT_EQ(kValue, message_->PushChannelHandle(handle).get());
+ EXPECT_EQ(EIO, message_->PushChannelHandle(handle).error());
+}
+
+TEST_F(ServiceMessageTest, GetFileHandle) {
+ ExpectDefaultHandleMessage();
+ auto make_file_handle = [](FileReference ref) { return LocalHandle{ref}; };
+ EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _))
+ .WillOnce(WithArg<1>(Invoke(make_file_handle)));
+ LocalHandle handle;
+ FileReference kRef = 12345;
+ EXPECT_TRUE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.Get());
+ handle.Release(); // Make sure we do not close the fake file descriptor.
+}
+
+TEST_F(ServiceMessageTest, GetFileHandleInvalid) {
+ ExpectDefaultHandleMessage();
+ LocalHandle handle;
+ FileReference kRef = -12;
+ EXPECT_TRUE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.Get());
+}
+
+TEST_F(ServiceMessageTest, GetFileHandleError) {
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), GetFileHandle(message_.get(), _))
+ .WillOnce(WithoutArgs(Invoke([] { return LocalHandle{-EIO}; })));
+ LocalHandle handle;
+ FileReference kRef = 12345;
+ EXPECT_FALSE(message_->GetFileHandle(kRef, &handle));
+ EXPECT_EQ(-EIO, handle.Get());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandle) {
+ ExpectDefaultHandleMessage();
+ auto make_channel_handle = [](ChannelReference ref) {
+ return LocalChannelHandle{nullptr, ref};
+ };
+ EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _))
+ .WillOnce(WithArg<1>(Invoke(make_channel_handle)));
+ LocalChannelHandle handle;
+ ChannelReference kRef = 12345;
+ EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(kRef, handle.value());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandleInvalid) {
+ ExpectDefaultHandleMessage();
+ LocalChannelHandle handle;
+ ChannelReference kRef = -12;
+ EXPECT_TRUE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(-12, handle.value());
+}
+
+TEST_F(ServiceMessageTest, GetChannelHandleError) {
+ ExpectDefaultHandleMessage();
+ EXPECT_CALL(*endpoint(), GetChannelHandle(message_.get(), _))
+ .WillOnce(WithoutArgs(Invoke([] {
+ return LocalChannelHandle{nullptr, -EIO};
+ })));
+ LocalChannelHandle handle;
+ ChannelReference kRef = 12345;
+ EXPECT_FALSE(message_->GetChannelHandle(kRef, &handle));
+ EXPECT_EQ(-EIO, handle.value());
+}
+
+TEST_F(ServiceMessageTest, ModifyChannelEvents) {
+ ExpectDefaultHandleMessage();
+ int kClearMask = 1;
+ int kSetMask = 2;
+ EXPECT_CALL(*endpoint(), ModifyChannelEvents(kTestCid, kClearMask, kSetMask))
+ .WillOnce(Return(Status<void>{}));
+ EXPECT_TRUE(message_->ModifyChannelEvents(kClearMask, kSetMask));
+}
+
+TEST_F(ServiceMessageTest, PushChannelSameService) {
+ ExpectDefaultHandleMessage();
+ int kFlags = 123;
+ int32_t kValue = 12;
+ EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(DoAll(SetArgPointee<3>(kTestCid),
+ Return(ByMove(RemoteChannelHandle{kValue}))));
+ int channel_id = -1;
+ auto status = message_->PushChannel(kFlags, nullptr, &channel_id);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kValue, status.get().value());
+ EXPECT_EQ(kTestCid, channel_id);
+}
+
+TEST_F(ServiceMessageTest, PushChannelFailure) {
+ ExpectDefaultHandleMessage();
+ int kFlags = 123;
+ EXPECT_CALL(*endpoint(), PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(Return(ByMove(ErrorStatus{EIO})));
+ int channel_id = -1;
+ auto status = message_->PushChannel(kFlags, nullptr, &channel_id);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(ServiceMessageTest, PushChannelDifferentService) {
+ ExpectDefaultHandleMessage();
+ auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint2, SetService(_))
+ .Times(2)
+ .WillRepeatedly(Return(Status<void>{}));
+ auto service2 =
+ std::make_shared<MockService>("MockSvc2", std::move(endpoint2));
+
+ int kFlags = 123;
+ int32_t kValue = 12;
+ EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()),
+ PushChannel(message_.get(), kFlags, nullptr, _))
+ .WillOnce(DoAll(SetArgPointee<3>(kTestCid),
+ Return(ByMove(RemoteChannelHandle{kValue}))));
+ int channel_id = -1;
+ auto status =
+ message_->PushChannel(service2.get(), kFlags, nullptr, &channel_id);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kValue, status.get().value());
+ EXPECT_EQ(kTestCid, channel_id);
+}
+
+TEST_F(ServiceMessageTest, CheckChannelSameService) {
+ ExpectDefaultHandleMessage();
+
+ auto test_channel = std::make_shared<Channel>();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _))
+ .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid)));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(kRef, &channel);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kTestCid, status.get());
+ EXPECT_EQ(test_channel, channel);
+}
+
+TEST_F(ServiceMessageTest, CheckChannelFailure) {
+ ExpectDefaultHandleMessage();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*endpoint(), CheckChannel(message_.get(), kRef, _))
+ .WillOnce(Return(ByMove(ErrorStatus{EOPNOTSUPP})));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(kRef, &channel);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EOPNOTSUPP, status.error());
+}
+
+TEST_F(ServiceMessageTest, CheckChannelDifferentService) {
+ ExpectDefaultHandleMessage();
+ auto endpoint2 = std::make_unique<testing::StrictMock<MockEndpoint>>();
+ EXPECT_CALL(*endpoint2, SetService(_))
+ .Times(2)
+ .WillRepeatedly(Return(Status<void>{}));
+ auto service2 =
+ std::make_shared<MockService>("MockSvc2", std::move(endpoint2));
+
+ auto test_channel = std::make_shared<Channel>();
+ ChannelReference kRef = 123;
+ EXPECT_CALL(*static_cast<MockEndpoint*>(service2->endpoint()),
+ CheckChannel(message_.get(), kRef, _))
+ .WillOnce(DoAll(SetArgPointee<2>(test_channel.get()), Return(kTestCid)));
+ std::shared_ptr<Channel> channel;
+ auto status = message_->CheckChannel(service2.get(), kRef, &channel);
+ ASSERT_TRUE(status);
+ EXPECT_EQ(kTestCid, status.get());
+ EXPECT_EQ(test_channel, channel);
+}
diff --git a/libs/vr/libpdx/status.cpp b/libs/vr/libpdx/status.cpp
new file mode 100644
index 0000000000..c275dafc5f
--- /dev/null
+++ b/libs/vr/libpdx/status.cpp
@@ -0,0 +1,15 @@
+#include "pdx/status.h"
+
+#include <pdx/rpc/serialization.h>
+#include <string.h>
+
+namespace android {
+namespace pdx {
+
+std::string ErrorStatus::ErrorToString(int error_code) {
+ char message[1024] = {};
+ return strerror_r(error_code, message, sizeof(message));
+}
+
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx/status_tests.cpp b/libs/vr/libpdx/status_tests.cpp
new file mode 100644
index 0000000000..d4e697caee
--- /dev/null
+++ b/libs/vr/libpdx/status_tests.cpp
@@ -0,0 +1,125 @@
+#include <pdx/status.h>
+
+#include <gtest/gtest.h>
+
+using android::pdx::ErrorStatus;
+using android::pdx::Status;
+
+TEST(Status, DefaultInit) {
+ Status<int> status;
+ EXPECT_FALSE(status.ok());
+ EXPECT_TRUE(status.empty());
+ EXPECT_EQ(0, status.get());
+ EXPECT_EQ(0, status.error());
+}
+
+TEST(Status, InitalizeSuccess) {
+ Status<int> status_int{0};
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(0, status_int.get());
+ status_int = Status<int>(3);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(3, status_int.get());
+ status_int = Status<int>(-3);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_TRUE(status_int.ok());
+ EXPECT_EQ(-3, status_int.get());
+
+ Status<std::string> status_str{"foo"};
+ EXPECT_FALSE(status_str.empty());
+ EXPECT_TRUE(status_str.ok());
+ EXPECT_EQ("foo", status_str.get());
+}
+
+TEST(Status, InitalizeError) {
+ Status<int> status_int = ErrorStatus(12);
+ EXPECT_FALSE(status_int.empty());
+ EXPECT_FALSE(status_int.ok());
+ EXPECT_EQ(0, status_int.get());
+ EXPECT_EQ(12, status_int.error());
+
+ Status<std::string> status_str = ErrorStatus(EIO);
+ EXPECT_FALSE(status_str.empty());
+ EXPECT_FALSE(status_str.ok());
+ EXPECT_EQ(EIO, status_str.error());
+}
+
+TEST(Status, ErrorMessage) {
+ Status<int> status = ErrorStatus(EIO);
+ EXPECT_EQ(status.GetErrorMessage(), strerror(EIO));
+
+ status = ErrorStatus(EINVAL);
+ EXPECT_EQ(status.GetErrorMessage(), strerror(EINVAL));
+}
+
+TEST(Status, Copy) {
+ Status<int> status1;
+ Status<int> status2;
+
+ status1 = Status<int>{12};
+ status2 = ErrorStatus(13);
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_EQ(12, status1.get());
+ EXPECT_EQ(0, status1.error());
+ EXPECT_EQ(0, status2.get());
+ EXPECT_EQ(13, status2.error());
+
+ status1 = status2;
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_FALSE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_EQ(0, status1.get());
+ EXPECT_EQ(13, status1.error());
+ EXPECT_EQ(0, status2.get());
+ EXPECT_EQ(13, status2.error());
+}
+
+TEST(Status, Move) {
+ Status<std::unique_ptr<int>> status1;
+ Status<std::unique_ptr<int>> status2;
+
+ status1 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{11}}};
+ status2 = Status<std::unique_ptr<int>>{std::unique_ptr<int>{new int{12}}};
+ EXPECT_FALSE(status1.empty());
+ EXPECT_FALSE(status2.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_TRUE(status2.ok());
+ EXPECT_EQ(11, *status1.get());
+ EXPECT_EQ(12, *status2.get());
+
+ Status<std::unique_ptr<int>> status3 = std::move(status2);
+ EXPECT_FALSE(status1.empty());
+ EXPECT_TRUE(status2.empty());
+ EXPECT_FALSE(status3.empty());
+ EXPECT_TRUE(status1.ok());
+ EXPECT_FALSE(status2.ok());
+ EXPECT_TRUE(status3.ok());
+ EXPECT_EQ(11, *status1.get());
+ EXPECT_EQ(nullptr, status2.get());
+ EXPECT_EQ(12, *status3.get());
+
+ std::swap(status1, status3);
+ EXPECT_EQ(12, *status1.get());
+ EXPECT_EQ(11, *status3.get());
+
+ status3 = std::move(status1);
+ EXPECT_TRUE(status1.empty());
+ EXPECT_EQ(12, *status3.get());
+}
+
+TEST(Status, Take) {
+ Status<std::unique_ptr<int>> status{std::unique_ptr<int>{new int{123}}};
+ EXPECT_FALSE(status.empty());
+ EXPECT_NE(nullptr, status.get());
+
+ auto data = status.take();
+ EXPECT_TRUE(status.empty());
+ EXPECT_EQ(nullptr, status.get());
+ EXPECT_EQ(123, *data);
+}
diff --git a/libs/vr/libpdx/thread_local_buffer_tests.cpp b/libs/vr/libpdx/thread_local_buffer_tests.cpp
new file mode 100644
index 0000000000..6cdaf1071a
--- /dev/null
+++ b/libs/vr/libpdx/thread_local_buffer_tests.cpp
@@ -0,0 +1,117 @@
+#include <memory>
+#include <string>
+#include <thread>
+#include <utility>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/message_buffer.h>
+
+namespace android {
+namespace pdx {
+namespace rpc {
+
+class ThreadLocalBufferTest {
+ public:
+ // Returns the unique address of the thread-local buffer. Used to test the
+ // correct behavior of the type-based thread local storage slot mapping
+ // mechanism.
+ template <typename Slot>
+ static std::uintptr_t GetSlotAddress() {
+ return reinterpret_cast<std::uintptr_t>(&MessageBuffer<Slot>::buffer_);
+ }
+
+ // Returns the raw value of the thread local buffer. Used to test the behavior
+ // of backing buffer initialization.
+ template <typename Slot>
+ static std::uintptr_t GetSlotValue() {
+ return reinterpret_cast<std::uintptr_t>(MessageBuffer<Slot>::buffer_);
+ }
+};
+
+} // namespace rpc
+} // namespace pdx
+} // namespace android
+
+using namespace android::pdx::rpc;
+
+namespace {
+
+struct TypeTagA;
+struct TypeTagB;
+
+constexpr std::size_t kSendBufferIndex = 0;
+constexpr std::size_t kReceiveBufferIndex = 1;
+
+using SendSlotA = ThreadLocalSlot<TypeTagA, kSendBufferIndex>;
+using SendSlotB = ThreadLocalSlot<TypeTagB, kSendBufferIndex>;
+using ReceiveSlotA = ThreadLocalSlot<TypeTagA, kReceiveBufferIndex>;
+using ReceiveSlotB = ThreadLocalSlot<TypeTagB, kReceiveBufferIndex>;
+
+} // anonymous namespace
+
+// Tests that index and type-based thread-local slot addressing works by
+// checking that the slot address is the same when the same index/type
+// combination is used and different when different combinations are used.
+TEST(ThreadLocalBufferTest, TypeSlots) {
+ auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>();
+ auto id2 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>();
+ auto id3 = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>();
+ auto id4 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>();
+
+ EXPECT_NE(id1, id2);
+ EXPECT_NE(id3, id4);
+ EXPECT_NE(id1, id3);
+ EXPECT_NE(id2, id4);
+
+ auto id1_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>();
+ auto id2_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>();
+ auto id3_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>();
+ auto id4_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>();
+
+ EXPECT_EQ(id1, id1_alias);
+ EXPECT_EQ(id2, id2_alias);
+ EXPECT_EQ(id3, id3_alias);
+ EXPECT_EQ(id4, id4_alias);
+}
+
+// Tests that different threads get different buffers for the same slot address.
+TEST(ThreadLocalBufferTest, ThreadSlots) {
+ auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>();
+ std::uintptr_t id2 = 0U;
+
+ std::thread thread([&id2]() mutable {
+ id2 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>();
+ });
+ thread.join();
+
+ EXPECT_NE(0U, id1);
+ EXPECT_NE(0U, id2);
+ EXPECT_NE(id1, id2);
+}
+
+// Tests that thread-local buffers are allocated at the first buffer request.
+TEST(ThreadLocalBufferTest, InitialValue) {
+ struct TypeTagX;
+ using SendSlotX = ThreadLocalSlot<TypeTagX, kSendBufferIndex>;
+
+ auto value1 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>();
+ MessageBuffer<SendSlotX>::GetBuffer();
+ auto value2 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>();
+
+ EXPECT_EQ(0U, value1);
+ EXPECT_NE(0U, value2);
+}
+
+// Tests that the underlying buffers are the same for a given index/type pair
+// and different across index/type combinations.
+TEST(ThreadLocalBufferTest, BackingBuffer) {
+ auto& buffer1 = MessageBuffer<SendSlotA>::GetBuffer();
+ auto& buffer2 = MessageBuffer<SendSlotA>::GetBuffer();
+ auto& buffer3 = MessageBuffer<SendSlotB>::GetBuffer();
+ auto& buffer4 = MessageBuffer<SendSlotB>::GetBuffer();
+
+ EXPECT_EQ(buffer1.data(), buffer2.data());
+ EXPECT_EQ(buffer3.data(), buffer4.data());
+ EXPECT_NE(buffer1.data(), buffer3.data());
+ EXPECT_NE(buffer2.data(), buffer4.data());
+}
diff --git a/libs/vr/libpdx/variant_tests.cpp b/libs/vr/libpdx/variant_tests.cpp
new file mode 100644
index 0000000000..325f33f3c5
--- /dev/null
+++ b/libs/vr/libpdx/variant_tests.cpp
@@ -0,0 +1,1101 @@
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <gtest/gtest.h>
+#include <pdx/rpc/variant.h>
+
+using namespace android::pdx;
+using namespace android::pdx::rpc;
+
+namespace {
+
+struct BaseType {
+ BaseType(int value) : value(value) {}
+ int value;
+};
+
+struct DerivedType : BaseType {
+ DerivedType(int value) : BaseType{value} {};
+};
+
+template <typename T>
+class TestType {
+ public:
+ TestType(const T& value) : value_(value) {}
+ TestType(T&& value) : value_(std::move(value)) {}
+ TestType(const TestType&) = default;
+ TestType(TestType&&) = default;
+
+ TestType& operator=(const TestType&) = default;
+ TestType& operator=(TestType&&) = default;
+
+ const T& get() const { return value_; }
+ T&& take() { return std::move(value_); }
+
+ private:
+ T value_;
+};
+
+template <typename T>
+class InstrumentType {
+ public:
+ InstrumentType(const T& value) : value_(value) { constructor_count_++; }
+ InstrumentType(T&& value) : value_(std::move(value)) { constructor_count_++; }
+ InstrumentType(const InstrumentType& other) : value_(other.value_) {
+ constructor_count_++;
+ }
+ InstrumentType(InstrumentType&& other) : value_(std::move(other.value_)) {
+ constructor_count_++;
+ }
+ InstrumentType(const TestType<T>& other) : value_(other.get()) {
+ constructor_count_++;
+ }
+ InstrumentType(TestType<T>&& other) : value_(other.take()) {
+ constructor_count_++;
+ }
+ ~InstrumentType() { destructor_count_++; }
+
+ InstrumentType& operator=(const InstrumentType& other) {
+ copy_assignment_count_++;
+ value_ = other.value_;
+ return *this;
+ }
+ InstrumentType& operator=(InstrumentType&& other) {
+ move_assignment_count_++;
+ value_ = std::move(other.value_);
+ return *this;
+ }
+
+ InstrumentType& operator=(const TestType<T>& other) {
+ copy_assignment_count_++;
+ value_ = other.get();
+ return *this;
+ }
+ InstrumentType& operator=(TestType<T>&& other) {
+ move_assignment_count_++;
+ value_ = other.take();
+ return *this;
+ }
+
+ static std::size_t constructor_count() { return constructor_count_; }
+ static std::size_t destructor_count() { return destructor_count_; }
+ static std::size_t move_assignment_count() { return move_assignment_count_; }
+ static std::size_t copy_assignment_count() { return copy_assignment_count_; }
+
+ const T& get() const { return value_; }
+ T&& take() { return std::move(value_); }
+
+ static void clear() {
+ constructor_count_ = 0;
+ destructor_count_ = 0;
+ move_assignment_count_ = 0;
+ copy_assignment_count_ = 0;
+ }
+
+ private:
+ T value_;
+
+ static std::size_t constructor_count_;
+ static std::size_t destructor_count_;
+ static std::size_t move_assignment_count_;
+ static std::size_t copy_assignment_count_;
+};
+
+template <typename T>
+std::size_t InstrumentType<T>::constructor_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::destructor_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::move_assignment_count_ = 0;
+template <typename T>
+std::size_t InstrumentType<T>::copy_assignment_count_ = 0;
+
+} // anonymous namespace
+
+TEST(Variant, Assignment) {
+ // Assert basic type properties.
+ {
+ Variant<int, bool, float> v;
+ ASSERT_EQ(-1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = 10;
+ ASSERT_EQ(0, v.index());
+ ASSERT_TRUE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ EXPECT_EQ(10, std::get<int>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = false;
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<bool>());
+ ASSERT_FALSE(v.is<float>());
+ EXPECT_EQ(false, std::get<bool>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ v = 1.0f;
+ ASSERT_EQ(2, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.0f, std::get<float>(v));
+ }
+
+ {
+ Variant<int, bool, float> v;
+ // ERROR: More than one type is implicitly convertible from double.
+ // v = 1.0;
+ v = static_cast<float>(1.0);
+ }
+
+ {
+ Variant<int, bool, float> v;
+
+ double x = 1.1;
+ v = static_cast<float>(x);
+ ASSERT_EQ(2, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<bool>());
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ ASSERT_EQ(-1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_FALSE(v.is<std::string>());
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = 20;
+ ASSERT_EQ(0, v.index());
+ ASSERT_TRUE(v.is<int>());
+ ASSERT_FALSE(v.is<std::string>());
+ EXPECT_EQ(20, std::get<int>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = std::string("test");
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<int, std::string> v;
+ v = "test";
+ ASSERT_EQ(1, v.index());
+ ASSERT_FALSE(v.is<int>());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<const char*> v1;
+ Variant<std::string> v2;
+
+ v1 = "test";
+ ASSERT_TRUE(v1.is<const char*>());
+ v2 = v1;
+ ASSERT_TRUE(v2.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v2));
+ }
+
+ {
+ Variant<int> a(1);
+ Variant<int> b;
+ ASSERT_TRUE(!a.empty());
+ ASSERT_TRUE(b.empty());
+
+ a = b;
+ ASSERT_TRUE(a.empty());
+ ASSERT_TRUE(b.empty());
+ }
+
+ {
+ Variant<int*, char*> v;
+
+ // ERROR: More than one type is implicitly convertible from nullptr.
+ // v = nullptr;
+
+ v = static_cast<int*>(nullptr);
+ EXPECT_TRUE(v.is<int*>());
+
+ v = static_cast<char*>(nullptr);
+ EXPECT_TRUE(v.is<char*>());
+ }
+
+ {
+ Variant<int*, char*> v;
+ int a = 10;
+ char b = 20;
+
+ v = &b;
+ ASSERT_TRUE(v.is<char*>());
+ EXPECT_EQ(&b, std::get<char*>(v));
+ EXPECT_EQ(b, *std::get<char*>(v));
+
+ v = &a;
+ ASSERT_TRUE(v.is<int*>());
+ EXPECT_EQ(&a, std::get<int*>(v));
+ EXPECT_EQ(a, *std::get<int*>(v));
+ }
+
+ {
+ using IntRef = std::reference_wrapper<int>;
+ Variant<IntRef> v;
+ int a = 10;
+
+ v = a;
+ ASSERT_TRUE(v.is<IntRef>());
+ EXPECT_EQ(a, std::get<IntRef>(v));
+
+ a = 20;
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ }
+}
+
+TEST(Variant, MoveAssignment) {
+ {
+ Variant<std::string> v;
+ std::string s = "test";
+ v = std::move(s);
+
+ EXPECT_TRUE(s.empty());
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<std::string> v("test");
+ std::string s = "fizz";
+ s = std::move(std::get<std::string>(v));
+
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ EXPECT_EQ("test", s);
+ }
+
+ {
+ Variant<std::string> a("test");
+ Variant<std::string> b;
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<std::string> a("test");
+ Variant<std::string> b("fizz");
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a("test");
+ Variant<int, std::string> b(10);
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(a).empty());
+ EXPECT_EQ("test", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a(10);
+ Variant<int, std::string> b("test");
+
+ b = std::move(a);
+ ASSERT_TRUE(a.is<int>());
+ ASSERT_TRUE(b.is<int>());
+ EXPECT_EQ(10, std::get<int>(a));
+ EXPECT_EQ(10, std::get<int>(b));
+ }
+}
+
+TEST(Variant, Constructor) {
+ {
+ Variant<int, bool, float> v(true);
+ EXPECT_TRUE(v.is<bool>());
+ }
+
+ {
+ Variant<int, bool, float> v(10);
+ EXPECT_TRUE(v.is<int>());
+ }
+
+ {
+ Variant<int, bool, float> v(10.1f);
+ EXPECT_TRUE(v.is<float>());
+ }
+
+ {
+ Variant<float, std::string> v(10.);
+ EXPECT_TRUE(v.is<float>());
+ }
+
+ {
+ TestType<int> i(1);
+ Variant<int, bool, float> v(i.take());
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(1, std::get<int>(v));
+ }
+
+ {
+ TestType<int> i(1);
+ Variant<int, bool, float> v(i.get());
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(1, std::get<int>(v));
+ }
+
+ {
+ TestType<bool> b(true);
+ Variant<int, bool, float> v(b.take());
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+ }
+
+ {
+ TestType<bool> b(true);
+ Variant<int, bool, float> v(b.get());
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+ }
+
+ {
+ Variant<const char*> c("test");
+ Variant<std::string> s(c);
+ ASSERT_TRUE(s.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(s));
+ }
+
+ {
+ Variant<int, bool, float> a(true);
+ Variant<int, bool, float> b(a);
+
+ ASSERT_TRUE(b.is<bool>());
+ }
+
+ {
+ using IntRef = std::reference_wrapper<int>;
+ int a = 10;
+ Variant<IntRef> v(a);
+ TestType<IntRef> t(a);
+
+ ASSERT_TRUE(v.is<IntRef>());
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ EXPECT_EQ(a, t.get());
+
+ a = 20;
+ EXPECT_EQ(a, std::get<IntRef>(v));
+ EXPECT_EQ(a, t.get());
+ }
+}
+
+// Verify correct ctor/dtor and assignment behavior used an instrumented type.
+TEST(Variant, CopyMoveConstructAssign) {
+ {
+ InstrumentType<int>::clear();
+
+ // Default construct to empty, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v;
+ ASSERT_EQ(0u, InstrumentType<int>::constructor_count());
+ ASSERT_EQ(0u, InstrumentType<int>::destructor_count());
+ ASSERT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ ASSERT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from int type, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v;
+ v = 10;
+ EXPECT_EQ(0u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from int type, no InstrumentType activity.
+ Variant<int, InstrumentType<int>> v(10);
+ EXPECT_EQ(0u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v;
+ v = InstrumentType<int>(25);
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ // Assign from temporary, temporary ctor/dtor.
+ v = InstrumentType<int>(35);
+ EXPECT_EQ(3u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ // dtor.
+ v = 10;
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from temporary, temporary ctor/dtor.
+ Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+ EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ // Assign from other temporary.
+ v = TestType<int>(11);
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(10));
+ // Assign from empty Variant.
+ v = Variant<int, InstrumentType<int>>();
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(1u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ TestType<int> other(10);
+ // Construct from other.
+ Variant<int, InstrumentType<int>> v(other);
+
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+ }
+
+ {
+ InstrumentType<int>::clear();
+
+ // Construct from other temporary.
+ Variant<int, InstrumentType<int>> v(TestType<int>(0));
+ TestType<int> other(10);
+ // Assign from other.
+ v = other;
+ EXPECT_EQ(1u, InstrumentType<int>::constructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::destructor_count());
+ EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+ EXPECT_EQ(1u, InstrumentType<int>::copy_assignment_count());
+ }
+}
+
+TEST(Variant, MoveConstructor) {
+ {
+ std::unique_ptr<int> pointer = std::make_unique<int>(10);
+ Variant<std::unique_ptr<int>> v(std::move(pointer));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) != nullptr);
+ EXPECT_TRUE(pointer == nullptr);
+ }
+
+ {
+ Variant<std::unique_ptr<int>> a(std::make_unique<int>(10));
+ Variant<std::unique_ptr<int>> b(std::move(a));
+
+ ASSERT_TRUE(a.is<std::unique_ptr<int>>());
+ ASSERT_TRUE(b.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(a) == nullptr);
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(b) != nullptr);
+ }
+}
+
+TEST(Variant, IndexOf) {
+ Variant<int, bool, float> v1;
+
+ EXPECT_EQ(0, v1.index_of<int>());
+ EXPECT_EQ(1, v1.index_of<bool>());
+ EXPECT_EQ(2, v1.index_of<float>());
+
+ Variant<int, bool, float, int> v2;
+
+ EXPECT_EQ(0, v2.index_of<int>());
+ EXPECT_EQ(1, v2.index_of<bool>());
+ EXPECT_EQ(2, v2.index_of<float>());
+}
+
+struct Visitor {
+ int int_value = 0;
+ bool bool_value = false;
+ float float_value = 0.0;
+ bool empty_value = false;
+
+ void Visit(int value) { int_value = value; }
+ void Visit(bool value) { bool_value = value; }
+ void Visit(float value) { float_value = value; }
+ void Visit(EmptyVariant) { empty_value = true; }
+};
+
+TEST(Variant, Visit) {
+ {
+ Variant<int, bool, float> v(10);
+ EXPECT_TRUE(v.is<int>());
+
+ Visitor visitor;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_EQ(10, visitor.int_value);
+
+ visitor = {};
+ v = true;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_EQ(true, visitor.bool_value);
+ }
+
+ {
+ Variant<int, bool, float> v;
+ EXPECT_EQ(-1, v.index());
+
+ Visitor visitor;
+ v.Visit([&visitor](const auto& value) { visitor.Visit(value); });
+ EXPECT_TRUE(visitor.empty_value);
+ }
+
+ {
+ Variant<std::string> v("test");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_FALSE(std::get<std::string>(v).empty());
+
+ v.Visit([](auto&& value) {
+ std::remove_reference_t<decltype(value)> empty;
+ std::swap(empty, value);
+ });
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ }
+}
+
+TEST(Variant, Become) {
+ {
+ Variant<int, bool, float> v;
+
+ v.Become(0);
+ EXPECT_TRUE(v.is<int>());
+
+ v.Become(1);
+ EXPECT_TRUE(v.is<bool>());
+
+ v.Become(2);
+ EXPECT_TRUE(v.is<float>());
+
+ v.Become(3);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-1);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-2);
+ EXPECT_TRUE(v.empty());
+ }
+
+ {
+ Variant<int, bool, float> v;
+
+ v.Become(0, 10);
+ ASSERT_TRUE(v.is<int>());
+ EXPECT_EQ(10, std::get<int>(v));
+
+ v.Become(1, true);
+ ASSERT_TRUE(v.is<bool>());
+ EXPECT_EQ(true, std::get<bool>(v));
+
+ v.Become(2, 2.0f);
+ ASSERT_TRUE(v.is<float>());
+ EXPECT_FLOAT_EQ(2.0f, std::get<float>(v));
+
+ v.Become(3, 10);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-1, 10);
+ EXPECT_TRUE(v.empty());
+
+ v.Become(-2, 20);
+ EXPECT_TRUE(v.empty());
+ }
+
+ {
+ Variant<std::string> v;
+
+ v.Become(0);
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_TRUE(std::get<std::string>(v).empty());
+ }
+
+ {
+ Variant<std::string> v;
+
+ v.Become(0, "test");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("test", std::get<std::string>(v));
+ }
+
+ {
+ Variant<std::string> v("foo");
+
+ v.Become(0, "bar");
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("foo", std::get<std::string>(v));
+ }
+}
+
+TEST(Variant, Swap) {
+ {
+ Variant<std::string> a;
+ Variant<std::string> b;
+
+ std::swap(a, b);
+ EXPECT_TRUE(a.empty());
+ EXPECT_TRUE(b.empty());
+ }
+
+ {
+ Variant<std::string> a("1");
+ Variant<std::string> b;
+
+ std::swap(a, b);
+ EXPECT_TRUE(!a.empty());
+ EXPECT_TRUE(!b.empty());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+
+ {
+ Variant<std::string> a;
+ Variant<std::string> b("1");
+
+ std::swap(a, b);
+ EXPECT_TRUE(!a.empty());
+ EXPECT_TRUE(!b.empty());
+ ASSERT_TRUE(a.is<std::string>());
+ EXPECT_EQ("1", std::get<std::string>(a));
+ }
+
+ {
+ Variant<std::string> a("1");
+ Variant<std::string> b("2");
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ("2", std::get<std::string>(a));
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+
+ {
+ Variant<int, std::string> a(10);
+ Variant<int, std::string> b("1");
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<std::string>());
+ ASSERT_TRUE(b.is<int>());
+ EXPECT_EQ("1", std::get<std::string>(a));
+ EXPECT_EQ(10, std::get<int>(b));
+ }
+
+ {
+ Variant<int, std::string> a("1");
+ Variant<int, std::string> b(10);
+
+ std::swap(a, b);
+ ASSERT_TRUE(a.is<int>());
+ ASSERT_TRUE(b.is<std::string>());
+ EXPECT_EQ(10, std::get<int>(a));
+ EXPECT_EQ("1", std::get<std::string>(b));
+ }
+}
+
+TEST(Variant, Get) {
+ {
+ Variant<int, bool, float, int> v;
+
+ EXPECT_EQ(nullptr, &std::get<int>(v));
+ EXPECT_EQ(nullptr, &std::get<bool>(v));
+ EXPECT_EQ(nullptr, &std::get<float>(v));
+ EXPECT_EQ(nullptr, &std::get<0>(v));
+ EXPECT_EQ(nullptr, &std::get<1>(v));
+ EXPECT_EQ(nullptr, &std::get<2>(v));
+ EXPECT_EQ(nullptr, &std::get<3>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = 9;
+ ASSERT_TRUE(v.is<int>())
+ << "Expected type " << v.index_of<int>() << " got type " << v.index();
+ EXPECT_EQ(9, std::get<int>(v));
+ EXPECT_EQ(9, std::get<0>(v));
+
+ std::get<int>(v) = 10;
+ EXPECT_EQ(10, std::get<int>(v));
+ EXPECT_EQ(10, std::get<0>(v));
+
+ std::get<0>(v) = 11;
+ EXPECT_EQ(11, std::get<int>(v));
+ EXPECT_EQ(11, std::get<0>(v));
+
+ std::get<3>(v) = 12;
+ EXPECT_EQ(12, std::get<int>(v));
+ EXPECT_EQ(12, std::get<3>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = false;
+ ASSERT_TRUE(v.is<bool>())
+ << "Expected type " << v.index_of<bool>() << " got type " << v.index();
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+
+ std::get<bool>(v) = true;
+ EXPECT_EQ(true, std::get<bool>(v));
+ EXPECT_EQ(true, std::get<1>(v));
+
+ std::get<bool>(v) = false;
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+
+ std::get<1>(v) = true;
+ EXPECT_EQ(true, std::get<bool>(v));
+ EXPECT_EQ(true, std::get<1>(v));
+
+ std::get<1>(v) = false;
+ EXPECT_EQ(false, std::get<bool>(v));
+ EXPECT_EQ(false, std::get<1>(v));
+ }
+
+ {
+ Variant<int, bool, float, int> v;
+ v = 1.0f;
+ ASSERT_TRUE(v.is<float>())
+ << "Expected type " << v.index_of<float>() << " got type " << v.index();
+ EXPECT_EQ(2, v.index());
+ EXPECT_FLOAT_EQ(1.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.0, std::get<2>(v));
+
+ std::get<float>(v) = 1.1;
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.1, std::get<2>(v));
+
+ std::get<float>(v) = -3.0;
+ EXPECT_FLOAT_EQ(-3.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(-3.0, std::get<2>(v));
+
+ std::get<2>(v) = 1.1;
+ EXPECT_FLOAT_EQ(1.1, std::get<float>(v));
+ EXPECT_FLOAT_EQ(1.1, std::get<2>(v));
+
+ std::get<2>(v) = -3.0;
+ EXPECT_FLOAT_EQ(-3.0, std::get<float>(v));
+ EXPECT_FLOAT_EQ(-3.0, std::get<2>(v));
+ }
+
+ {
+ Variant<std::unique_ptr<int>> v(std::make_unique<int>(10));
+ std::unique_ptr<int> pointer = std::move(std::get<std::unique_ptr<int>>(v));
+ ASSERT_FALSE(v.empty());
+ EXPECT_TRUE(pointer != nullptr);
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ }
+
+ {
+ Variant<std::string> v("test");
+ std::string s = std::get<std::string>(std::move(v));
+ EXPECT_EQ("test", s);
+ }
+}
+
+TEST(Variant, IfAnyOf) {
+ {
+ Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ const Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Get(&v, &b));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ Variant<int, float> v(10);
+ ASSERT_TRUE(v.is<int>());
+
+ bool b = false;
+ EXPECT_TRUE(IfAnyOf<int>::Call(&v, [&b](const auto& value) { b = value; }));
+ EXPECT_TRUE(b);
+
+ float f = 0.0f;
+ EXPECT_TRUE((
+ IfAnyOf<int, float>::Call(&v, [&f](const auto& value) { f = value; })));
+ EXPECT_FLOAT_EQ(10.f, f);
+ }
+
+ {
+ Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ const int* original_v = std::get<std::unique_ptr<int>>(v).get();
+
+ std::unique_ptr<int> u(std::make_unique<int>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Take(&v, &u));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<std::unique_ptr<DerivedType>, int> v(
+ std::make_unique<DerivedType>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>());
+ const DerivedType* original_v =
+ std::get<std::unique_ptr<DerivedType>>(v).get();
+
+ std::unique_ptr<BaseType> u(std::make_unique<BaseType>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<DerivedType>>::Take(&v, &u));
+ ASSERT_TRUE(v.is<std::unique_ptr<DerivedType>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<DerivedType>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<std::unique_ptr<int>, int> v(std::make_unique<int>(10));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ const int* original_v = std::get<std::unique_ptr<int>>(v).get();
+
+ std::unique_ptr<int> u(std::make_unique<int>(20));
+
+ EXPECT_TRUE(IfAnyOf<std::unique_ptr<int>>::Call(
+ &v, [&u](auto&& value) { u = std::move(value); }));
+ ASSERT_TRUE(v.is<std::unique_ptr<int>>());
+ EXPECT_TRUE(std::get<std::unique_ptr<int>>(v) == nullptr);
+ EXPECT_EQ(u.get(), original_v);
+ }
+
+ {
+ Variant<int, bool, float> v(true);
+ ASSERT_TRUE(v.is<bool>());
+
+ float f = 0.f;
+ EXPECT_FALSE((IfAnyOf<int, float>::Get(&v, &f)));
+ EXPECT_FLOAT_EQ(0.f, f);
+ }
+
+ {
+ Variant<std::string, int> v("foo");
+ ASSERT_TRUE(v.is<std::string>());
+
+ std::string s = "bar";
+ EXPECT_TRUE(IfAnyOf<std::string>::Swap(&v, &s));
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("bar", std::get<std::string>(v));
+ EXPECT_EQ("foo", s);
+ }
+
+ {
+ Variant<std::string, const char*> v(static_cast<const char*>("foo"));
+ ASSERT_TRUE(v.is<const char*>());
+
+ std::string s = "bar";
+ EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ ASSERT_TRUE(v.is<const char*>());
+ EXPECT_EQ("foo", std::get<const char*>(v));
+ EXPECT_EQ("foo", s);
+
+ v = std::string("bar");
+ ASSERT_TRUE(v.is<std::string>());
+
+ EXPECT_TRUE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ ASSERT_TRUE(v.is<std::string>());
+ EXPECT_EQ("bar", s);
+ }
+
+ {
+ Variant<std::string, const char*> v;
+ ASSERT_TRUE(v.empty());
+
+ std::string s = "bar";
+ EXPECT_FALSE((IfAnyOf<std::string, const char*>::Take(&v, &s)));
+ EXPECT_EQ("bar", s);
+ }
+
+ {
+ Variant<std::string, const char*> v(static_cast<const char*>("test"));
+ ASSERT_TRUE(v.is<const char*>());
+
+ std::string s;
+ EXPECT_FALSE(IfAnyOf<>::Take(&v, &s));
+ EXPECT_TRUE(s.empty());
+ }
+}
+
+TEST(Variant, ConstVolatile) {
+ {
+ Variant<const int> v(10);
+ ASSERT_TRUE(v.is<const int>());
+ EXPECT_EQ(10, std::get<const int>(v));
+ }
+
+ {
+ Variant<const std::string> v("test");
+ ASSERT_TRUE(v.is<const std::string>());
+ EXPECT_EQ("test", std::get<const std::string>(v));
+ }
+
+ {
+ Variant<volatile int, std::string> v(10);
+ ASSERT_TRUE(v.is<volatile int>());
+ EXPECT_EQ(10, std::get<volatile int>(v));
+ }
+}
+
+TEST(Variant, HasType) {
+ EXPECT_TRUE((detail::HasType<int, int, float, bool>::value));
+ EXPECT_FALSE((detail::HasType<char, int, float, bool>::value));
+ EXPECT_FALSE(detail::HasType<>::value);
+
+ EXPECT_TRUE((detail::HasType<int&, int, float, bool>::value));
+ EXPECT_FALSE((detail::HasType<char&, int, float, bool>::value));
+}
+
+TEST(Variant, Set) {
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<int, bool,
+ float>::value));
+ EXPECT_TRUE(
+ (detail::Set<int, bool, float>::template IsSubset<bool, float>::value));
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<float>::value));
+ EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<>::value));
+
+ EXPECT_FALSE(
+ (detail::Set<int, bool, float>::template IsSubset<int, bool, float,
+ char>::value));
+ EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<bool, float,
+ char>::value));
+ EXPECT_FALSE(
+ (detail::Set<int, bool, float>::template IsSubset<float, char>::value));
+ EXPECT_FALSE((detail::Set<int, bool, float>::template IsSubset<char>::value));
+
+ EXPECT_TRUE(detail::Set<>::template IsSubset<>::value);
+ EXPECT_FALSE(detail::Set<>::template IsSubset<int>::value);
+ EXPECT_FALSE((detail::Set<>::template IsSubset<int, float>::value));
+}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
new file mode 100644
index 0000000000..8cfa86fa44
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -0,0 +1,70 @@
+cc_defaults {
+ name: "pdx_default_transport_compiler_defaults",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
+
+cc_defaults {
+ name: "pdx_default_transport_lib_defaults",
+ export_include_dirs: ["private"],
+ whole_static_libs: ["libpdx"],
+}
+
+cc_defaults {
+ name: "pdx_use_transport_servicefs",
+ export_include_dirs: ["private/servicefs"],
+ whole_static_libs: ["libpdx_servicefs", "libservicefs"],
+}
+
+cc_defaults {
+ name: "pdx_use_transport_uds",
+ export_include_dirs: ["private/uds"],
+ whole_static_libs: ["libpdx_uds"],
+}
+
+cc_library_static {
+ name: "libpdx_default_transport",
+ defaults: [
+ "pdx_default_transport_compiler_defaults",
+ "pdx_default_transport_lib_defaults",
+ "pdx_use_transport_uds",
+ ],
+}
+
+cc_binary {
+ name: "servicetool",
+ defaults: ["pdx_default_transport_compiler_defaults"],
+ srcs: [
+ "servicetool.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ static_libs: [
+ "libpdx_default_transport",
+ ],
+}
+
+// Benchmarks.
+cc_binary {
+ name: "pdx_benchmarks",
+ defaults: ["pdx_default_transport_compiler_defaults"],
+ srcs: [
+ "pdx_benchmarks.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libchrome",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libpdx_default_transport",
+ ],
+}
+
diff --git a/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp
new file mode 100644
index 0000000000..fa0adf0182
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp
@@ -0,0 +1,1090 @@
+// Use ALWAYS at the tag level. Control is performed manually during command
+// line processing.
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+#include <utils/Trace.h>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <pdx/rpc/default_initialization_allocator.h>
+#include <pdx/rpc/message_buffer.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/service.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <cstdlib>
+#include <functional>
+#include <future>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+using android::pdx::Channel;
+using android::pdx::ClientBase;
+using android::pdx::Endpoint;
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::Service;
+using android::pdx::ServiceBase;
+using android::pdx::default_transport::ClientChannelFactory;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::BufferWrapper;
+using android::pdx::rpc::DefaultInitializationAllocator;
+using android::pdx::rpc::MessageBuffer;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::RemoteMethodReturn;
+using android::pdx::rpc::ReplyBuffer;
+using android::pdx::rpc::Void;
+using android::pdx::rpc::WrapBuffer;
+
+namespace {
+
+constexpr size_t kMaxMessageSize = 4096 * 1024;
+
+std::string GetServicePath(const std::string& path, int instance_id) {
+ return path + std::to_string(instance_id);
+}
+
+void SetThreadName(const std::string& name) {
+ prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name.c_str()), 0, 0, 0);
+}
+
+constexpr uint64_t kNanosPerSecond = 1000000000llu;
+
+uint64_t GetClockNs() {
+ timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return kNanosPerSecond * t.tv_sec + t.tv_nsec;
+}
+
+template <typename T>
+ssize_t ssizeof(const T&) {
+ return static_cast<ssize_t>(sizeof(T));
+}
+
+class SchedStats {
+ public:
+ SchedStats() : SchedStats(gettid()) {}
+ SchedStats(pid_t task_id) : task_id_(task_id) {}
+ SchedStats(const SchedStats&) = default;
+ SchedStats& operator=(const SchedStats&) = default;
+
+ void Update() {
+ const std::string stats_path =
+ "/proc/" + std::to_string(task_id_) + "/schedstat";
+
+ std::string line;
+ base::ReadFileToString(base::FilePath{stats_path}, &line);
+ std::vector<std::string> stats = base::SplitString(
+ line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ CHECK_EQ(3u, stats.size());
+
+ // Calculate the deltas since the last update. Each value is absolute since
+ // the task started.
+ uint64_t current_cpu_time_ns = std::stoull(stats[0]);
+ uint64_t current_wait_ns = std::stoull(stats[1]);
+ uint64_t current_timeslices = std::stoull(stats[2]);
+ cpu_time_ns_ = current_cpu_time_ns - last_cpu_time_ns_;
+ wait_ns_ = current_wait_ns - last_wait_ns_;
+ timeslices_ = current_timeslices - last_timeslices_;
+ last_cpu_time_ns_ = current_cpu_time_ns;
+ last_wait_ns_ = current_wait_ns;
+ last_timeslices_ = current_timeslices;
+ }
+
+ pid_t task_id() const { return task_id_; }
+ uint64_t cpu_time_ns() const { return cpu_time_ns_; }
+ uint64_t wait_ns() const { return wait_ns_; }
+ uint64_t timeslices() const { return timeslices_; }
+
+ double cpu_time_s() const {
+ return static_cast<double>(cpu_time_ns_) / kNanosPerSecond;
+ }
+ double wait_s() const {
+ return static_cast<double>(wait_ns_) / kNanosPerSecond;
+ }
+
+ private:
+ int32_t task_id_;
+ uint64_t cpu_time_ns_ = 0;
+ uint64_t last_cpu_time_ns_ = 0;
+ uint64_t wait_ns_ = 0;
+ uint64_t last_wait_ns_ = 0;
+ uint64_t timeslices_ = 0;
+ uint64_t last_timeslices_ = 0;
+
+ PDX_SERIALIZABLE_MEMBERS(SchedStats, task_id_, cpu_time_ns_, wait_ns_,
+ timeslices_);
+};
+
+// Opcodes for client/service protocol.
+struct BenchmarkOps {
+ enum : int {
+ Nop,
+ Read,
+ Write,
+ Echo,
+ Stats,
+ WriteVector,
+ EchoVector,
+ Quit,
+ };
+};
+
+struct BenchmarkRPC {
+ PDX_REMOTE_METHOD(Stats, BenchmarkOps::Stats,
+ std::tuple<uint64_t, uint64_t, SchedStats>(Void));
+ PDX_REMOTE_METHOD(WriteVector, BenchmarkOps::WriteVector,
+ int(const BufferWrapper<std::vector<uint8_t>> data));
+ PDX_REMOTE_METHOD(EchoVector, BenchmarkOps::EchoVector,
+ BufferWrapper<std::vector<uint8_t>>(
+ const BufferWrapper<std::vector<uint8_t>> data));
+};
+
+struct BenchmarkResult {
+ int thread_id = 0;
+ int service_id = 0;
+ double time_delta_s = 0.0;
+ uint64_t bytes_sent = 0;
+ SchedStats sched_stats = {};
+};
+
+// Global command line option values.
+struct Options {
+ bool verbose = false;
+ int threads = 1;
+ int opcode = BenchmarkOps::Read;
+ int blocksize = 1;
+ int count = 1;
+ int instances = 1;
+ int timeout = 1;
+ int warmup = 0;
+} ProgramOptions;
+
+// Command line option names.
+const char kOptionService[] = "service";
+const char kOptionClient[] = "client";
+const char kOptionVerbose[] = "verbose";
+const char kOptionOpcode[] = "op";
+const char kOptionBlocksize[] = "bs";
+const char kOptionCount[] = "count";
+const char kOptionThreads[] = "threads";
+const char kOptionInstances[] = "instances";
+const char kOptionTimeout[] = "timeout";
+const char kOptionTrace[] = "trace";
+const char kOptionWarmup[] = "warmup";
+
+// getopt() long options.
+static option long_options[] = {
+ {kOptionService, required_argument, 0, 0},
+ {kOptionClient, required_argument, 0, 0},
+ {kOptionVerbose, no_argument, 0, 0},
+ {kOptionOpcode, required_argument, 0, 0},
+ {kOptionBlocksize, required_argument, 0, 0},
+ {kOptionCount, required_argument, 0, 0},
+ {kOptionThreads, required_argument, 0, 0},
+ {kOptionInstances, required_argument, 0, 0},
+ {kOptionTimeout, required_argument, 0, 0},
+ {kOptionTrace, no_argument, 0, 0},
+ {kOptionWarmup, required_argument, 0, 0},
+ {0, 0, 0, 0},
+};
+
+// Parses the argument for kOptionOpcode and sets the value of
+// ProgramOptions.opcode.
+void ParseOpcodeOption(const std::string& argument) {
+ if (argument == "read") {
+ ProgramOptions.opcode = BenchmarkOps::Read;
+ } else if (argument == "write") {
+ ProgramOptions.opcode = BenchmarkOps::Write;
+ } else if (argument == "echo") {
+ ProgramOptions.opcode = BenchmarkOps::Echo;
+ } else if (argument == "writevec") {
+ ProgramOptions.opcode = BenchmarkOps::WriteVector;
+ } else if (argument == "echovec") {
+ ProgramOptions.opcode = BenchmarkOps::EchoVector;
+ } else if (argument == "quit") {
+ ProgramOptions.opcode = BenchmarkOps::Quit;
+ } else if (argument == "nop") {
+ ProgramOptions.opcode = BenchmarkOps::Nop;
+ } else if (argument == "stats") {
+ ProgramOptions.opcode = BenchmarkOps::Stats;
+ } else {
+ ProgramOptions.opcode = std::stoi(argument);
+ }
+}
+
+// Implements the service side of the benchmark.
+class BenchmarkService : public ServiceBase<BenchmarkService> {
+ public:
+ std::shared_ptr<Channel> OnChannelOpen(Message& message) override {
+ VLOG(1) << "BenchmarkService::OnChannelCreate: cid="
+ << message.GetChannelId();
+ return nullptr;
+ }
+
+ void OnChannelClose(Message& message,
+ const std::shared_ptr<Channel>& /*channel*/) override {
+ VLOG(1) << "BenchmarkService::OnChannelClose: cid="
+ << message.GetChannelId();
+ }
+
+ Status<void> HandleMessage(Message& message) override {
+ ATRACE_NAME("BenchmarkService::HandleMessage");
+
+ switch (message.GetOp()) {
+ case BenchmarkOps::Nop:
+ VLOG(1) << "BenchmarkService::HandleMessage: op=nop";
+ {
+ ATRACE_NAME("Reply");
+ CHECK(message.Reply(0));
+ }
+ return {};
+
+ case BenchmarkOps::Write: {
+ VLOG(1) << "BenchmarkService::HandleMessage: op=write send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ Status<void> status;
+ if (message.GetSendLength())
+ status = message.ReadAll(send_buffer.data(), message.GetSendLength());
+
+ {
+ ATRACE_NAME("Reply");
+ if (!status)
+ CHECK(message.ReplyError(status.error()));
+ else
+ CHECK(message.Reply(message.GetSendLength()));
+ }
+ return {};
+ }
+
+ case BenchmarkOps::Read: {
+ VLOG(1) << "BenchmarkService::HandleMessage: op=read send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ Status<void> status;
+ if (message.GetReceiveLength()) {
+ status = message.WriteAll(receive_buffer.data(),
+ message.GetReceiveLength());
+ }
+
+ {
+ ATRACE_NAME("Reply");
+ if (!status)
+ CHECK(message.ReplyError(status.error()));
+ else
+ CHECK(message.Reply(message.GetReceiveLength()));
+ }
+ return {};
+ }
+
+ case BenchmarkOps::Echo: {
+ VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ Status<void> status;
+ if (message.GetSendLength())
+ status = message.ReadAll(send_buffer.data(), message.GetSendLength());
+
+ if (!status) {
+ CHECK(message.ReplyError(status.error()));
+ return {};
+ }
+
+ if (message.GetSendLength()) {
+ status =
+ message.WriteAll(send_buffer.data(), message.GetSendLength());
+ }
+
+ {
+ ATRACE_NAME("Reply");
+ if (!status)
+ CHECK(message.ReplyError(status.error()));
+ else
+ CHECK(message.Reply(message.GetSendLength()));
+ }
+ return {};
+ }
+
+ case BenchmarkOps::Stats: {
+ VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ // Snapshot the stats when the message is received.
+ const uint64_t receive_time_ns = GetClockNs();
+ sched_stats_.Update();
+
+ // Use the RPC system to return the results.
+ RemoteMethodReturn<BenchmarkRPC::Stats>(
+ message, BenchmarkRPC::Stats::Return{receive_time_ns, GetClockNs(),
+ sched_stats_});
+ return {};
+ }
+
+ case BenchmarkOps::WriteVector:
+ VLOG(1) << "BenchmarkService::HandleMessage: op=writevec send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ DispatchRemoteMethod<BenchmarkRPC::WriteVector>(
+ *this, &BenchmarkService::OnWriteVector, message, kMaxMessageSize);
+ return {};
+
+ case BenchmarkOps::EchoVector:
+ VLOG(1) << "BenchmarkService::HandleMessage: op=echovec send_length="
+ << message.GetSendLength()
+ << " receive_length=" << message.GetReceiveLength();
+
+ DispatchRemoteMethod<BenchmarkRPC::EchoVector>(
+ *this, &BenchmarkService::OnEchoVector, message, kMaxMessageSize);
+ return {};
+
+ case BenchmarkOps::Quit:
+ Cancel();
+ return ErrorStatus{ESHUTDOWN};
+
+ default:
+ VLOG(1) << "BenchmarkService::HandleMessage: default case; op="
+ << message.GetOp();
+ return Service::DefaultHandleMessage(message);
+ }
+ }
+
+ // Updates the scheduler stats from procfs for this thread.
+ void UpdateSchedStats() { sched_stats_.Update(); }
+
+ private:
+ friend BASE;
+
+ BenchmarkService(std::unique_ptr<Endpoint> endpoint)
+ : BASE("BenchmarkService", std::move(endpoint)),
+ send_buffer(kMaxMessageSize),
+ receive_buffer(kMaxMessageSize) {}
+
+ std::vector<uint8_t> send_buffer;
+ std::vector<uint8_t> receive_buffer;
+
+ // Each service thread has its own scheduler stats object.
+ static thread_local SchedStats sched_stats_;
+
+ using BufferType = BufferWrapper<
+ std::vector<uint8_t, DefaultInitializationAllocator<uint8_t>>>;
+
+ int OnWriteVector(Message&, const BufferType& data) { return data.size(); }
+ BufferType OnEchoVector(Message&, BufferType&& data) {
+ return std::move(data);
+ }
+
+ BenchmarkService(const BenchmarkService&) = delete;
+ void operator=(const BenchmarkService&) = delete;
+};
+
+thread_local SchedStats BenchmarkService::sched_stats_;
+
+// Implements the client side of the benchmark.
+class BenchmarkClient : public ClientBase<BenchmarkClient> {
+ public:
+ int Nop() {
+ ATRACE_NAME("BenchmarkClient::Nop");
+ VLOG(1) << "BenchmarkClient::Nop";
+ Transaction transaction{*this};
+ return ReturnStatusOrError(transaction.Send<int>(BenchmarkOps::Nop));
+ }
+
+ int Write(const void* buffer, size_t length) {
+ ATRACE_NAME("BenchmarkClient::Write");
+ VLOG(1) << "BenchmarkClient::Write: buffer=" << buffer
+ << " length=" << length;
+ Transaction transaction{*this};
+ return ReturnStatusOrError(
+ transaction.Send<int>(BenchmarkOps::Write, buffer, length, nullptr, 0));
+ // return write(endpoint_fd(), buffer, length);
+ }
+
+ int Read(void* buffer, size_t length) {
+ ATRACE_NAME("BenchmarkClient::Read");
+ VLOG(1) << "BenchmarkClient::Read: buffer=" << buffer
+ << " length=" << length;
+ Transaction transaction{*this};
+ return ReturnStatusOrError(
+ transaction.Send<int>(BenchmarkOps::Read, nullptr, 0, buffer, length));
+ // return read(endpoint_fd(), buffer, length);
+ }
+
+ int Echo(const void* send_buffer, size_t send_length, void* receive_buffer,
+ size_t receive_length) {
+ ATRACE_NAME("BenchmarkClient::Echo");
+ VLOG(1) << "BenchmarkClient::Echo: send_buffer=" << send_buffer
+ << " send_length=" << send_length
+ << " receive_buffer=" << receive_buffer
+ << " receive_length=" << receive_length;
+ Transaction transaction{*this};
+ return ReturnStatusOrError(
+ transaction.Send<int>(BenchmarkOps::Echo, send_buffer, send_length,
+ receive_buffer, receive_length));
+ }
+
+ int Stats(std::tuple<uint64_t, uint64_t, SchedStats>* stats_out) {
+ ATRACE_NAME("BenchmarkClient::Stats");
+ VLOG(1) << "BenchmarkClient::Stats";
+
+ auto status = InvokeRemoteMethodInPlace<BenchmarkRPC::Stats>(stats_out);
+ return status ? 0 : -status.error();
+ }
+
+ int WriteVector(const BufferWrapper<std::vector<uint8_t>>& data) {
+ ATRACE_NAME("BenchmarkClient::Stats");
+ VLOG(1) << "BenchmarkClient::Stats";
+
+ auto status = InvokeRemoteMethod<BenchmarkRPC::WriteVector>(data);
+ return ReturnStatusOrError(status);
+ }
+
+ template <typename T>
+ int WriteVector(const BufferWrapper<T>& data) {
+ ATRACE_NAME("BenchmarkClient::WriteVector");
+ VLOG(1) << "BenchmarkClient::WriteVector";
+
+ auto status = InvokeRemoteMethod<BenchmarkRPC::WriteVector>(data);
+ return ReturnStatusOrError(status);
+ }
+
+ template <typename T, typename U>
+ int EchoVector(const BufferWrapper<T>& data, BufferWrapper<U>* data_out) {
+ ATRACE_NAME("BenchmarkClient::EchoVector");
+ VLOG(1) << "BenchmarkClient::EchoVector";
+
+ MessageBuffer<ReplyBuffer>::Reserve(kMaxMessageSize - 1);
+ auto status =
+ InvokeRemoteMethodInPlace<BenchmarkRPC::EchoVector>(data_out, data);
+ return status ? 0 : -status.error();
+ }
+
+ int Quit() {
+ VLOG(1) << "BenchmarkClient::Quit";
+ Transaction transaction{*this};
+ return ReturnStatusOrError(transaction.Send<int>(BenchmarkOps::Echo));
+ }
+
+ private:
+ friend BASE;
+
+ BenchmarkClient(const std::string& service_path)
+ : BASE(ClientChannelFactory::Create(service_path),
+ ProgramOptions.timeout) {}
+
+ BenchmarkClient(const BenchmarkClient&) = delete;
+ void operator=(const BenchmarkClient&) = delete;
+};
+
+// Creates a benchmark service at |path| and dispatches messages.
+int ServiceCommand(const std::string& path) {
+ if (path.empty())
+ return -EINVAL;
+
+ // Start the requested number of dispatch threads.
+ std::vector<std::thread> dispatch_threads;
+ int service_count = ProgramOptions.instances;
+ int service_id_counter = 0;
+ int thread_id_counter = 0;
+ std::atomic<bool> done(false);
+
+ while (service_count--) {
+ std::cerr << "Starting service instance " << service_id_counter
+ << std::endl;
+ auto service = BenchmarkService::Create(
+ android::pdx::default_transport::Endpoint::CreateAndBindSocket(
+ GetServicePath(path, service_id_counter),
+ android::pdx::default_transport::Endpoint::kBlocking));
+ if (!service) {
+ std::cerr << "Failed to create service instance!!" << std::endl;
+ done = true;
+ break;
+ }
+
+ int thread_count = ProgramOptions.threads;
+ while (thread_count--) {
+ std::cerr << "Starting dispatch thread " << thread_id_counter
+ << " service " << service_id_counter << std::endl;
+
+ dispatch_threads.emplace_back(
+ [&](const int thread_id, const int service_id,
+ const std::shared_ptr<BenchmarkService>& local_service) {
+ SetThreadName("service" + std::to_string(service_id));
+
+ // Read the initial schedstats for this thread from procfs.
+ local_service->UpdateSchedStats();
+
+ ATRACE_NAME("BenchmarkService::Dispatch");
+ while (!done) {
+ auto ret = local_service->ReceiveAndDispatch();
+ if (!ret) {
+ if (ret.error() != ESHUTDOWN) {
+ std::cerr << "Error while dispatching message on thread "
+ << thread_id << " service " << service_id << ": "
+ << ret.GetErrorMessage() << std::endl;
+ } else {
+ std::cerr << "Quitting thread " << thread_id << " service "
+ << service_id << std::endl;
+ }
+ done = true;
+ return;
+ }
+ }
+ },
+ thread_id_counter++, service_id_counter, service);
+ }
+
+ service_id_counter++;
+ }
+
+ // Wait for the dispatch threads to exit.
+ for (auto& thread : dispatch_threads) {
+ thread.join();
+ }
+
+ return 0;
+}
+
+int ClientCommand(const std::string& path) {
+ // Start the requested number of client threads.
+ std::vector<std::thread> client_threads;
+ std::vector<std::future<BenchmarkResult>> client_results;
+ int service_count = ProgramOptions.instances;
+ int thread_id_counter = 0;
+ int service_id_counter = 0;
+
+ // Aggregate statistics, updated when worker threads exit.
+ std::atomic<uint64_t> total_bytes(0);
+ std::atomic<uint64_t> total_time_ns(0);
+
+ // Samples for variance calculation.
+ std::vector<uint64_t> latency_samples_ns(
+ ProgramOptions.instances * ProgramOptions.threads * ProgramOptions.count);
+ const size_t samples_per_thread = ProgramOptions.count;
+
+ std::vector<uint8_t> send_buffer(ProgramOptions.blocksize);
+ std::vector<uint8_t> receive_buffer(kMaxMessageSize);
+
+ // Barriers for synchronizing thread start.
+ std::vector<std::future<void>> ready_barrier_futures;
+ std::promise<void> go_barrier_promise;
+ std::future<void> go_barrier_future = go_barrier_promise.get_future();
+
+ // Barrier for synchronizing thread tear down.
+ std::promise<void> done_barrier_promise;
+ std::future<void> done_barrier_future = done_barrier_promise.get_future();
+
+ while (service_count--) {
+ int thread_count = ProgramOptions.threads;
+ while (thread_count--) {
+ std::cerr << "Starting client thread " << thread_id_counter << " service "
+ << service_id_counter << std::endl;
+
+ std::promise<BenchmarkResult> result_promise;
+ client_results.push_back(result_promise.get_future());
+
+ std::promise<void> ready_barrier_promise;
+ ready_barrier_futures.push_back(ready_barrier_promise.get_future());
+
+ client_threads.emplace_back(
+ [&](const int thread_id, const int service_id,
+ std::promise<BenchmarkResult> result, std::promise<void> ready) {
+ SetThreadName("client" + std::to_string(thread_id) + "/" +
+ std::to_string(service_id));
+
+ ATRACE_NAME("BenchmarkClient::Dispatch");
+
+ auto client =
+ BenchmarkClient::Create(GetServicePath(path, service_id));
+ if (!client) {
+ std::cerr << "Failed to create client for service " << service_id
+ << std::endl;
+ return -ENOMEM;
+ }
+
+ uint64_t* thread_samples =
+ &latency_samples_ns[samples_per_thread * thread_id];
+
+ // Per-thread statistics.
+ uint64_t bytes_sent = 0;
+ uint64_t time_start_ns;
+ uint64_t time_end_ns;
+ SchedStats sched_stats;
+
+ // Signal ready and wait for go.
+ ready.set_value();
+ go_barrier_future.wait();
+
+ // Warmup the scheduler.
+ int warmup = ProgramOptions.warmup;
+ while (warmup--) {
+ for (int i = 0; i < 1000000; i++)
+ ;
+ }
+
+ sched_stats.Update();
+ time_start_ns = GetClockNs();
+
+ int count = ProgramOptions.count;
+ while (count--) {
+ uint64_t iteration_start_ns = GetClockNs();
+
+ switch (ProgramOptions.opcode) {
+ case BenchmarkOps::Nop: {
+ const int ret = client->Nop();
+ if (ret < 0) {
+ std::cerr << "Failed to send nop: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ }
+ break;
+ }
+
+ case BenchmarkOps::Read: {
+ const int ret = client->Read(receive_buffer.data(),
+ ProgramOptions.blocksize);
+ if (ret < 0) {
+ std::cerr << "Failed to read: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else if (ret != ProgramOptions.blocksize) {
+ std::cerr << "Expected ret=" << ProgramOptions.blocksize
+ << "; actual ret=" << ret << std::endl;
+ return -EINVAL;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += ret;
+ }
+ break;
+ }
+
+ case BenchmarkOps::Write: {
+ const int ret =
+ client->Write(send_buffer.data(), send_buffer.size());
+ if (ret < 0) {
+ std::cerr << "Failed to write: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else if (ret != ProgramOptions.blocksize) {
+ std::cerr << "Expected ret=" << ProgramOptions.blocksize
+ << "; actual ret=" << ret << std::endl;
+ return -EINVAL;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += ret;
+ }
+ break;
+ }
+
+ case BenchmarkOps::Echo: {
+ const int ret = client->Echo(
+ send_buffer.data(), send_buffer.size(),
+ receive_buffer.data(), receive_buffer.size());
+ if (ret < 0) {
+ std::cerr << "Failed to echo: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else if (ret != ProgramOptions.blocksize) {
+ std::cerr << "Expected ret=" << ProgramOptions.blocksize
+ << "; actual ret=" << ret << std::endl;
+ return -EINVAL;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += ret * 2;
+ }
+ break;
+ }
+
+ case BenchmarkOps::Stats: {
+ std::tuple<uint64_t, uint64_t, SchedStats> stats;
+ const int ret = client->Stats(&stats);
+ if (ret < 0) {
+ std::cerr << "Failed to get stats: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ std::cerr
+ << "Round trip: receive_time_ns=" << std::get<0>(stats)
+ << " reply_time_ns=" << std::get<1>(stats)
+ << " cpu_time_s=" << std::get<2>(stats).cpu_time_s()
+ << " wait_s=" << std::get<2>(stats).wait_s()
+ << std::endl;
+ }
+ break;
+ }
+
+ case BenchmarkOps::WriteVector: {
+ const int ret = client->WriteVector(
+ WrapBuffer(send_buffer.data(), ProgramOptions.blocksize));
+ if (ret < 0) {
+ std::cerr << "Failed to write vector: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += ret;
+ }
+ break;
+ }
+
+ case BenchmarkOps::EchoVector: {
+ thread_local BufferWrapper<std::vector<
+ uint8_t, DefaultInitializationAllocator<uint8_t>>>
+ response_buffer;
+ const int ret = client->EchoVector(
+ WrapBuffer(send_buffer.data(), ProgramOptions.blocksize),
+ &response_buffer);
+ if (ret < 0) {
+ std::cerr << "Failed to echo vector: " << strerror(-ret)
+ << std::endl;
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ bytes_sent += send_buffer.size() + response_buffer.size();
+ }
+ break;
+ }
+
+ case BenchmarkOps::Quit: {
+ const int ret = client->Quit();
+ if (ret < 0 && ret != -ESHUTDOWN) {
+ std::cerr << "Failed to send quit: " << strerror(-ret);
+ return ret;
+ } else {
+ VLOG(1) << "Success";
+ }
+ break;
+ }
+
+ default:
+ std::cerr
+ << "Invalid client operation: " << ProgramOptions.opcode
+ << std::endl;
+ return -EINVAL;
+ }
+
+ uint64_t iteration_end_ns = GetClockNs();
+ uint64_t iteration_delta_ns =
+ iteration_end_ns - iteration_start_ns;
+ thread_samples[count] = iteration_delta_ns;
+
+ if (iteration_delta_ns > (kNanosPerSecond / 100)) {
+ SchedStats stats = sched_stats;
+ stats.Update();
+ std::cerr << "Thread " << thread_id << " iteration_delta_s="
+ << (static_cast<double>(iteration_delta_ns) /
+ kNanosPerSecond)
+ << " " << stats.cpu_time_s() << " " << stats.wait_s()
+ << std::endl;
+ }
+ }
+
+ time_end_ns = GetClockNs();
+ sched_stats.Update();
+
+ const double time_delta_s =
+ static_cast<double>(time_end_ns - time_start_ns) /
+ kNanosPerSecond;
+
+ total_bytes += bytes_sent;
+ total_time_ns += time_end_ns - time_start_ns;
+
+ result.set_value(
+ {thread_id, service_id, time_delta_s, bytes_sent, sched_stats});
+ done_barrier_future.wait();
+
+ return 0;
+ },
+ thread_id_counter++, service_id_counter, std::move(result_promise),
+ std::move(ready_barrier_promise));
+ }
+
+ service_id_counter++;
+ }
+
+ // Wait for workers to be ready.
+ std::cerr << "Waiting for workers to be ready..." << std::endl;
+ for (auto& ready : ready_barrier_futures)
+ ready.wait();
+
+ // Signal workers to go.
+ std::cerr << "Kicking off benchmark." << std::endl;
+ go_barrier_promise.set_value();
+
+ // Wait for all the worker threas to finish.
+ for (auto& result : client_results)
+ result.wait();
+
+ // Report worker thread results.
+ for (auto& result : client_results) {
+ BenchmarkResult benchmark_result = result.get();
+ std::cerr << std::fixed << "Thread " << benchmark_result.thread_id
+ << " service " << benchmark_result.service_id << ":" << std::endl;
+ std::cerr << "\t " << benchmark_result.bytes_sent << " bytes in "
+ << benchmark_result.time_delta_s << " seconds ("
+ << std::setprecision(0) << (benchmark_result.bytes_sent / 1024.0 /
+ benchmark_result.time_delta_s)
+ << " K/s; " << std::setprecision(3)
+ << (ProgramOptions.count / benchmark_result.time_delta_s)
+ << " txn/s; " << std::setprecision(9)
+ << (benchmark_result.time_delta_s / ProgramOptions.count)
+ << " s/txn)" << std::endl;
+ std::cerr << "\tStats: " << benchmark_result.sched_stats.cpu_time_s() << " "
+ << (benchmark_result.sched_stats.cpu_time_s() /
+ ProgramOptions.count)
+ << " " << benchmark_result.sched_stats.wait_s() << " "
+ << (benchmark_result.sched_stats.wait_s() / ProgramOptions.count)
+ << " " << benchmark_result.sched_stats.timeslices() << std::endl;
+ }
+
+ // Signal worker threads to exit.
+ done_barrier_promise.set_value();
+
+ // Wait for the worker threads to exit.
+ for (auto& thread : client_threads) {
+ thread.join();
+ }
+
+ // Report aggregate results.
+ const int total_threads = ProgramOptions.threads * ProgramOptions.instances;
+ const int iterations = ProgramOptions.count;
+ const double total_time_s =
+ static_cast<double>(total_time_ns) / kNanosPerSecond;
+ // This is about how much wall time it took to completely transfer all the
+ // paylaods.
+ const double average_time_s = total_time_s / total_threads;
+
+ const uint64_t min_sample_time_ns =
+ *std::min_element(latency_samples_ns.begin(), latency_samples_ns.end());
+ const double min_sample_time_s =
+ static_cast<double>(min_sample_time_ns) / kNanosPerSecond;
+
+ const uint64_t max_sample_time_ns =
+ *std::max_element(latency_samples_ns.begin(), latency_samples_ns.end());
+ const double max_sample_time_s =
+ static_cast<double>(max_sample_time_ns) / kNanosPerSecond;
+
+ const double total_sample_time_s =
+ std::accumulate(latency_samples_ns.begin(), latency_samples_ns.end(), 0.0,
+ [](double s, uint64_t ns) {
+ return s + static_cast<double>(ns) / kNanosPerSecond;
+ });
+ const double average_sample_time_s =
+ total_sample_time_s / latency_samples_ns.size();
+
+ const double sum_of_squared_deviations = std::accumulate(
+ latency_samples_ns.begin(), latency_samples_ns.end(), 0.0,
+ [&](double s, uint64_t ns) {
+ const double delta =
+ static_cast<double>(ns) / kNanosPerSecond - average_sample_time_s;
+ return s + delta * delta;
+ });
+ const double variance = sum_of_squared_deviations / latency_samples_ns.size();
+ const double standard_deviation = std::sqrt(variance);
+
+ const int num_buckets = 200;
+ const uint64_t sample_range_ns = max_sample_time_ns - min_sample_time_ns;
+ const uint64_t ns_per_bucket = sample_range_ns / num_buckets;
+ std::array<uint64_t, num_buckets> sample_buckets = {{0}};
+
+ // Count samples in each bucket range.
+ for (uint64_t sample_ns : latency_samples_ns) {
+ sample_buckets[(sample_ns - min_sample_time_ns) / (ns_per_bucket + 1)] += 1;
+ }
+
+ // Calculate population percentiles.
+ const uint64_t percent_50 =
+ static_cast<uint64_t>(latency_samples_ns.size() * 0.5);
+ const uint64_t percent_90 =
+ static_cast<uint64_t>(latency_samples_ns.size() * 0.9);
+ const uint64_t percent_95 =
+ static_cast<uint64_t>(latency_samples_ns.size() * 0.95);
+ const uint64_t percent_99 =
+ static_cast<uint64_t>(latency_samples_ns.size() * 0.99);
+
+ uint64_t sample_count = 0;
+ double latency_50th_percentile_s, latency_90th_percentile_s,
+ latency_95th_percentile_s, latency_99th_percentile_s;
+ for (int i = 0; i < num_buckets; i++) {
+ // Report the midpoint of the bucket range as the value of the
+ // corresponding
+ // percentile.
+ const double bucket_midpoint_time_s =
+ (ns_per_bucket * i + 0.5 * ns_per_bucket + min_sample_time_ns) /
+ kNanosPerSecond;
+ if (sample_count < percent_50 &&
+ (sample_count + sample_buckets[i]) >= percent_50) {
+ latency_50th_percentile_s = bucket_midpoint_time_s;
+ }
+ if (sample_count < percent_90 &&
+ (sample_count + sample_buckets[i]) >= percent_90) {
+ latency_90th_percentile_s = bucket_midpoint_time_s;
+ }
+ if (sample_count < percent_95 &&
+ (sample_count + sample_buckets[i]) >= percent_95) {
+ latency_95th_percentile_s = bucket_midpoint_time_s;
+ }
+ if (sample_count < percent_99 &&
+ (sample_count + sample_buckets[i]) >= percent_99) {
+ latency_99th_percentile_s = bucket_midpoint_time_s;
+ }
+ sample_count += sample_buckets[i];
+ }
+
+ std::cerr << std::fixed << "Total throughput over " << total_threads
+ << " threads:\n\t " << total_bytes << " bytes in " << average_time_s
+ << " seconds (" << std::setprecision(0)
+ << (total_bytes / 1024.0 / average_time_s) << " K/s; "
+ << std::setprecision(3)
+ << (iterations * total_threads / average_time_s)
+ << std::setprecision(9) << " txn/s; "
+ << (average_time_s / (iterations * total_threads)) << " s/txn)"
+ << std::endl;
+ std::cerr << "Sample statistics: " << std::endl;
+ std::cerr << total_sample_time_s << " s total sample time" << std::endl;
+ std::cerr << average_sample_time_s << " s avg" << std::endl;
+ std::cerr << standard_deviation << " s std dev" << std::endl;
+ std::cerr << min_sample_time_s << " s min" << std::endl;
+ std::cerr << max_sample_time_s << " s max" << std::endl;
+ std::cerr << "Latency percentiles:" << std::endl;
+ std::cerr << "50th: " << latency_50th_percentile_s << " s" << std::endl;
+ std::cerr << "90th: " << latency_90th_percentile_s << " s" << std::endl;
+ std::cerr << "95th: " << latency_95th_percentile_s << " s" << std::endl;
+ std::cerr << "99th: " << latency_99th_percentile_s << " s" << std::endl;
+
+ std::cout << total_time_ns << " " << std::fixed << std::setprecision(9)
+ << average_sample_time_s << " " << std::fixed
+ << std::setprecision(9) << standard_deviation << std::endl;
+ return 0;
+}
+
+int Usage(const std::string& command_name) {
+ // clang-format off
+ std::cout << "Usage: " << command_name << " [options]" << std::endl;
+ std::cout << "\t--verbose : Use verbose messages." << std::endl;
+ std::cout << "\t--service <endpoint path> : Start service at the given path." << std::endl;
+ std::cout << "\t--client <endpoint path> : Start client to the given path." << std::endl;
+ std::cout << "\t--op <read | write | echo> : Sepcify client operation mode." << std::endl;
+ std::cout << "\t--bs <block size bytes> : Sepcify block size to use." << std::endl;
+ std::cout << "\t--count <count> : Sepcify number of transactions to make." << std::endl;
+ std::cout << "\t--instances <count> : Specify number of service instances." << std::endl;
+ std::cout << "\t--threads <count> : Sepcify number of threads per instance." << std::endl;
+ std::cout << "\t--timeout <timeout ms | -1> : Timeout to wait for services." << std::endl;
+ std::cout << "\t--trace : Enable systrace logging." << std::endl;
+ std::cout << "\t--warmup <iterations> : Busy loops before running benchmarks." << std::endl;
+ // clang-format on
+ return -1;
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ logging::LoggingSettings logging_settings;
+ logging_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ logging::InitLogging(logging_settings);
+
+ int getopt_code;
+ int option_index;
+ std::string option = "";
+ std::string command = "";
+ std::string command_argument = "";
+ bool tracing_enabled = false;
+
+ // Process command line options.
+ while ((getopt_code =
+ getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ option = long_options[option_index].name;
+ VLOG(1) << "option=" << option;
+ switch (getopt_code) {
+ case 0:
+ if (option == kOptionVerbose) {
+ ProgramOptions.verbose = true;
+ logging::SetMinLogLevel(-1);
+ } else if (option == kOptionOpcode) {
+ ParseOpcodeOption(optarg);
+ } else if (option == kOptionBlocksize) {
+ ProgramOptions.blocksize = std::stoi(optarg);
+ if (ProgramOptions.blocksize < 0) {
+ std::cerr << "Invalid blocksize argument: "
+ << ProgramOptions.blocksize << std::endl;
+ return -EINVAL;
+ }
+ } else if (option == kOptionCount) {
+ ProgramOptions.count = std::stoi(optarg);
+ if (ProgramOptions.count < 1) {
+ std::cerr << "Invalid count argument: " << ProgramOptions.count
+ << std::endl;
+ return -EINVAL;
+ }
+ } else if (option == kOptionThreads) {
+ ProgramOptions.threads = std::stoi(optarg);
+ if (ProgramOptions.threads < 1) {
+ std::cerr << "Invalid threads argument: " << ProgramOptions.threads
+ << std::endl;
+ return -EINVAL;
+ }
+ } else if (option == kOptionInstances) {
+ ProgramOptions.instances = std::stoi(optarg);
+ if (ProgramOptions.instances < 1) {
+ std::cerr << "Invalid instances argument: "
+ << ProgramOptions.instances << std::endl;
+ return -EINVAL;
+ }
+ } else if (option == kOptionTimeout) {
+ ProgramOptions.timeout = std::stoi(optarg);
+ } else if (option == kOptionTrace) {
+ tracing_enabled = true;
+ } else if (option == kOptionWarmup) {
+ ProgramOptions.warmup = std::stoi(optarg);
+ } else {
+ command = option;
+ if (optarg)
+ command_argument = optarg;
+ }
+ break;
+ }
+ }
+
+ // Setup ATRACE/systrace based on command line.
+ atrace_setup();
+ atrace_set_tracing_enabled(tracing_enabled);
+
+ VLOG(1) << "command=" << command << " command_argument=" << command_argument;
+
+ if (command == "") {
+ return Usage(argv[0]);
+ } else if (command == kOptionService) {
+ return ServiceCommand(command_argument);
+ } else if (command == kOptionClient) {
+ return ClientCommand(command_argument);
+ } else {
+ return Usage(argv[0]);
+ }
+}
diff --git a/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h
new file mode 100644
index 0000000000..81bb17bd04
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h
@@ -0,0 +1,91 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
+
+#include <ftw.h>
+
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/service.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+class ServiceUtility : public ClientBase<ServiceUtility> {
+ public:
+ Status<int> ReloadSystemProperties() {
+ Transaction transaction{*this};
+ return ReturnStatusOrError(
+ transaction.Send<int>(opcodes::REPORT_SYSPROP_CHANGE));
+ }
+
+ static std::string GetRootEndpointPath() {
+ return ClientChannelFactory::GetRootEndpointPath();
+ }
+ static std::string GetEndpointPath(const std::string& endpoint_path) {
+ return ClientChannelFactory::GetEndpointPath(endpoint_path);
+ }
+
+ // Traverses the PDX service path space and sends a message to reload system
+ // properties to each service endpoint it finds along the way.
+ // NOTE: This method is used by atrace to poke PDX services. Please avoid
+ // unnecessary changes to this mechanism to minimize impact on atrace.
+ static bool PokeServices() {
+ const int kMaxDepth = 16;
+ const int result =
+ nftw(GetRootEndpointPath().c_str(), PokeService, kMaxDepth, FTW_PHYS);
+ return result == 0 ? true : false;
+ }
+
+ private:
+ friend BASE;
+
+ ServiceUtility(const std::string& endpoint_path, int* error = nullptr)
+ : BASE(ClientChannelFactory::Create(endpoint_path), 0) {
+ if (error)
+ *error = Client::error();
+ }
+
+ // Sends the sysprop_change message to the service at fpath, so it re-reads
+ // its system properties. Returns 0 on success or a negated errno code on
+ // failure.
+ // NOTE: This method is used by atrace to poke PDX services. Please avoid
+ // unnecessary changes to this mechanism to minimize impact on atrace.
+ static int PokeService(const char* fpath, const struct stat* /*sb*/,
+ int typeflag, struct FTW* /*ftwbuf*/) {
+ const bool kIgnoreErrors = true;
+
+ if (typeflag == FTW_F) {
+ int error;
+ auto utility = ServiceUtility::Create(fpath, &error);
+ if (!utility) {
+ if (error != -ECONNREFUSED) {
+ ALOGE("ServiceUtility::PokeService: Failed to open %s: %s.", fpath,
+ strerror(-error));
+ }
+ return kIgnoreErrors ? 0 : error;
+ }
+
+ auto status = utility->ReloadSystemProperties();
+ if (!status) {
+ ALOGE(
+ "ServiceUtility::PokeService: Failed to send sysprop change to %s: "
+ "%s",
+ fpath, status.GetErrorMessage().c_str());
+ return kIgnoreErrors ? 0 : -status.error();
+ }
+ }
+
+ return 0;
+ }
+
+ ServiceUtility(const ServiceUtility&) = delete;
+ void operator=(const ServiceUtility&) = delete;
+};
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h
new file mode 100644
index 0000000000..11163b3c48
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_manager.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
+
+#include <servicefs/channel_manager.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ChannelManager = ::android::pdx::servicefs::ChannelManager;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h
new file mode 100644
index 0000000000..d1717801bc
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
+
+#include <servicefs/client_channel.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannel = ::android::pdx::servicefs::ClientChannel;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h
new file mode 100644
index 0000000000..77b5cac2f5
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/client_channel_factory.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <servicefs/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannelFactory = ::android::pdx::servicefs::ClientChannelFactory;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h
new file mode 100644
index 0000000000..158871c892
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_dispatcher.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
+
+#include <servicefs/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ServiceDispatcher = ::android::pdx::servicefs::ServiceDispatcher;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h
new file mode 100644
index 0000000000..8f413c1fa1
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/service_endpoint.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_SERVICE_ENDPOINT_H_
+
+#include <servicefs/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using Endpoint = ::android::pdx::servicefs::Endpoint;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h
new file mode 100644
index 0000000000..f34636f10a
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_manager.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
+
+#include <uds/channel_manager.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ChannelManager = ::android::pdx::uds::ChannelManager;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h
new file mode 100644
index 0000000000..bf632d7512
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
+
+#include <uds/client_channel.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannel = ::android::pdx::uds::ClientChannel;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h
new file mode 100644
index 0000000000..e5c4e30368
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/client_channel_factory.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <uds/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ClientChannelFactory = ::android::pdx::uds::ClientChannelFactory;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h
new file mode 100644
index 0000000000..7cb7a80fe7
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_dispatcher.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
+
+#include <uds/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ServiceDispatcher = ::android::pdx::uds::ServiceDispatcher;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h
new file mode 100644
index 0000000000..1fd61030f9
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/service_endpoint.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_SERVICE_ENDPOINT_H_
+
+#include <uds/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using Endpoint = ::android::pdx::uds::Endpoint;
+
+} // namespace default_transport
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_default_transport/servicetool.cpp b/libs/vr/libpdx_default_transport/servicetool.cpp
new file mode 100644
index 0000000000..60eedb3847
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/servicetool.cpp
@@ -0,0 +1,244 @@
+#include <errno.h>
+#include <ftw.h>
+#include <getopt.h>
+#include <pdx/client.h>
+#include <pdx/service.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <pdx/default_transport/client_channel_factory.h>
+
+using android::pdx::default_transport::ClientChannelFactory;
+
+namespace {
+
+constexpr long kClientTimeoutMs = 0; // Don't wait for non-existent services.
+constexpr int kDumpBufferSize = 2 * 4096; // Two pages.
+
+class ControlClient : public android::pdx::ClientBase<ControlClient> {
+ public:
+ explicit ControlClient(const std::string& service_path, long timeout_ms);
+
+ void Reload();
+ std::string Dump();
+
+ private:
+ friend BASE;
+
+ ControlClient(const ControlClient&) = delete;
+ void operator=(const ControlClient&) = delete;
+};
+
+bool option_verbose = false;
+
+static struct option long_options[] = {
+ {"reload", required_argument, 0, 0},
+ {"dump", required_argument, 0, 0},
+ {"verbose", no_argument, 0, 0},
+ {0, 0, 0, 0},
+};
+
+#define printf_verbose(fmt, ... /*args*/) \
+ do { \
+ if (option_verbose) \
+ printf(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+void HexDump(const void* pointer, size_t length);
+
+ControlClient::ControlClient(const std::string& service_path, long timeout_ms)
+ : BASE{ClientChannelFactory::Create(service_path), timeout_ms} {}
+
+void ControlClient::Reload() {
+ android::pdx::Transaction trans{*this};
+ auto status = trans.Send<void>(android::pdx::opcodes::REPORT_SYSPROP_CHANGE,
+ nullptr, 0, nullptr, 0);
+ if (!status) {
+ fprintf(stderr, "Failed to send reload: %s\n",
+ status.GetErrorMessage().c_str());
+ }
+}
+
+std::string ControlClient::Dump() {
+ android::pdx::Transaction trans{*this};
+ std::vector<char> buffer(kDumpBufferSize);
+ auto status = trans.Send<int>(android::pdx::opcodes::DUMP_STATE, nullptr, 0,
+ buffer.data(), buffer.size());
+
+ printf_verbose("ControlClient::Dump: ret=%d\n", ReturnStatusOrError(status));
+
+ if (!status) {
+ fprintf(stderr, "Failed to send dump request: %s\n",
+ status.GetErrorMessage().c_str());
+ return "";
+ } else if (status.get() > static_cast<ssize_t>(buffer.capacity())) {
+ fprintf(stderr, "Service returned a larger size than requested: %d\n",
+ status.get());
+ return "";
+ }
+
+ if (option_verbose)
+ HexDump(buffer.data(), status.get());
+
+ return std::string(buffer.data(), status.get());
+}
+
+int Usage(const std::string& command_name) {
+ printf("Usage: %s [options]\n", command_name.c_str());
+ printf("\t--verbose : Use verbose messages.\n");
+ printf(
+ "\t--reload <all | service path> : Ask service(s) to reload system "
+ "properties.\n");
+ printf("\t--dump <all | service path> : Dump service(s) state.\n");
+ return -1;
+}
+
+typedef int (*CallbackType)(const char* path, const struct stat* sb,
+ int type_flag, FTW* ftw_buffer);
+
+int ReloadCommandCallback(const char* path, const struct stat* sb,
+ int type_flag, FTW* ftw_buffer);
+int DumpCommandCallback(const char* path, const struct stat* sb, int type_flag,
+ FTW* ftw_buffer);
+
+void CallOnAllFiles(CallbackType callback, const std::string& base_path) {
+ const int kMaxDepth = 32;
+ nftw(base_path.c_str(), callback, kMaxDepth, FTW_PHYS);
+}
+
+int ReloadCommand(const std::string& service_path) {
+ printf_verbose("ReloadCommand: service_path=%s\n", service_path.c_str());
+
+ if (service_path == "" || service_path == "all") {
+ CallOnAllFiles(ReloadCommandCallback,
+ ClientChannelFactory::GetRootEndpointPath());
+ return 0;
+ } else {
+ auto client = ControlClient::Create(service_path, kClientTimeoutMs);
+ if (!client) {
+ fprintf(stderr, "Failed to open service at \"%s\".\n",
+ service_path.c_str());
+ return -1;
+ }
+
+ client->Reload();
+ return 0;
+ }
+}
+
+int DumpCommand(const std::string& service_path) {
+ printf_verbose("DumpCommand: service_path=%s\n", service_path.c_str());
+
+ if (service_path == "" || service_path == "all") {
+ CallOnAllFiles(DumpCommandCallback,
+ ClientChannelFactory::GetRootEndpointPath());
+ return 0;
+ } else {
+ auto client = ControlClient::Create(service_path, kClientTimeoutMs);
+ if (!client) {
+ fprintf(stderr, "Failed to open service at \"%s\".\n",
+ service_path.c_str());
+ return -1;
+ }
+
+ std::string response = client->Dump();
+ if (!response.empty()) {
+ printf(
+ "--------------------------------------------------------------------"
+ "---\n");
+ printf("%s:\n", service_path.c_str());
+ printf("%s\n", response.c_str());
+ }
+ return 0;
+ }
+}
+
+int ReloadCommandCallback(const char* path, const struct stat*, int type_flag,
+ FTW*) {
+ if (type_flag == FTW_F)
+ ReloadCommand(path);
+ return 0;
+}
+
+int DumpCommandCallback(const char* path, const struct stat*, int type_flag,
+ FTW*) {
+ if (type_flag == FTW_F)
+ DumpCommand(path);
+ return 0;
+}
+
+void HexDump(const void* pointer, size_t length) {
+ uintptr_t address = reinterpret_cast<uintptr_t>(pointer);
+
+ for (size_t count = 0; count < length; count += 16, address += 16) {
+ printf("0x%08lx: ", static_cast<unsigned long>(address));
+
+ for (size_t i = 0; i < 16u; i++) {
+ if (i < std::min(length - count, static_cast<size_t>(16))) {
+ printf("%02x ", *reinterpret_cast<const uint8_t*>(address + i));
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf("|");
+
+ for (size_t i = 0; i < 16u; i++) {
+ if (i < std::min(length - count, static_cast<size_t>(16))) {
+ char c = *reinterpret_cast<const char*>(address + i);
+ if (isalnum(c) || c == ' ') {
+ printf("%c", c);
+ } else {
+ printf(".");
+ }
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf("|\n");
+ }
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ int getopt_code;
+ int option_index;
+ std::string option = "";
+ std::string command = "";
+ std::string command_argument = "";
+
+ // Process command line options.
+ while ((getopt_code =
+ getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ option = long_options[option_index].name;
+ printf_verbose("option=%s\n", option.c_str());
+ switch (getopt_code) {
+ case 0:
+ if (option == "verbose") {
+ option_verbose = true;
+ } else {
+ command = option;
+ if (optarg)
+ command_argument = optarg;
+ }
+ break;
+ }
+ }
+
+ printf_verbose("command=%s command_argument=%s\n", command.c_str(),
+ command_argument.c_str());
+
+ if (command == "") {
+ return Usage(argv[0]);
+ } else if (command == "reload") {
+ return ReloadCommand(command_argument);
+ } else if (command == "dump") {
+ return DumpCommand(command_argument);
+ } else {
+ return Usage(argv[0]);
+ }
+}
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
new file mode 100644
index 0000000000..82a5ea7526
--- /dev/null
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -0,0 +1,57 @@
+cc_library_static {
+ name: "libpdx_uds",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-DLOG_TAG=\"libpdx_uds\"",
+ "-DTRACE=0",
+ ],
+ export_include_dirs: ["private"],
+ local_include_dirs: ["private"],
+ srcs: [
+ "channel_event_set.cpp",
+ "channel_manager.cpp",
+ "client_channel_factory.cpp",
+ "client_channel.cpp",
+ "ipc_helper.cpp",
+ "service_dispatcher.cpp",
+ "service_endpoint.cpp",
+ ],
+ static_libs: [
+ "libcutils",
+ "libbase",
+ "libpdx",
+ ],
+ whole_static_libs: [
+ "libselinux",
+ ],
+}
+
+cc_test {
+ name: "libpdx_uds_tests",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "client_channel_tests.cpp",
+ "ipc_helper_tests.cpp",
+ "remote_method_tests.cpp",
+ "service_framework_tests.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libpdx_uds",
+ "libpdx",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/libs/vr/libpdx_uds/channel_event_set.cpp b/libs/vr/libpdx_uds/channel_event_set.cpp
new file mode 100644
index 0000000000..ac4dea993b
--- /dev/null
+++ b/libs/vr/libpdx_uds/channel_event_set.cpp
@@ -0,0 +1,115 @@
+#include "private/uds/channel_event_set.h"
+
+#include <log/log.h>
+
+#include <uds/ipc_helper.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+ChannelEventSet::ChannelEventSet() {
+ const int flags = EFD_CLOEXEC | EFD_NONBLOCK;
+ LocalHandle epoll_fd, event_fd;
+
+ if (!SetupHandle(epoll_create1(EPOLL_CLOEXEC), &epoll_fd, "epoll") ||
+ !SetupHandle(eventfd(0, flags), &event_fd, "event")) {
+ return;
+ }
+
+ epoll_event event;
+ event.events = 0;
+ event.data.u32 = 0;
+ if (epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_fd.Get(), &event) < 0) {
+ const int error = errno;
+ ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s",
+ strerror(error));
+ return;
+ }
+
+ epoll_fd_ = std::move(epoll_fd);
+ event_fd_ = std::move(event_fd);
+}
+
+Status<void> ChannelEventSet::AddDataFd(const LocalHandle& data_fd) {
+ epoll_event event;
+ event.events = EPOLLHUP | EPOLLRDHUP;
+ event.data.u32 = event.events;
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, data_fd.Get(), &event) < 0) {
+ const int error = errno;
+ ALOGE("ChannelEventSet::ChannelEventSet: Failed to add event_fd: %s",
+ strerror(error));
+ return ErrorStatus{error};
+ } else {
+ return {};
+ }
+}
+
+int ChannelEventSet::ModifyEvents(int clear_mask, int set_mask) {
+ ALOGD_IF(TRACE, "ChannelEventSet::ModifyEvents: clear_mask=%x set_mask=%x",
+ clear_mask, set_mask);
+ const int old_bits = event_bits_;
+ const int new_bits = (event_bits_ & ~clear_mask) | set_mask;
+ event_bits_ = new_bits;
+
+ // If anything changed clear the event and update the event mask.
+ if (old_bits != new_bits) {
+ eventfd_t value;
+ eventfd_read(event_fd_.Get(), &value);
+
+ epoll_event event;
+ event.events = POLLIN;
+ event.data.u32 = event_bits_;
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, event_fd_.Get(), &event) <
+ 0) {
+ const int error = errno;
+ ALOGE("ChannelEventSet::AddEventHandle: Failed to update event: %s",
+ strerror(error));
+ return -error;
+ }
+ }
+
+ // If there are any bits set, re-trigger the eventfd.
+ if (new_bits)
+ eventfd_write(event_fd_.Get(), 1);
+
+ return 0;
+}
+
+Status<void> ChannelEventSet::SetupHandle(int fd, LocalHandle* handle,
+ const char* error_name) {
+ const int error = errno;
+ handle->Reset(fd);
+ if (!*handle) {
+ ALOGE("ChannelEventSet::SetupHandle: Failed to setup %s handle: %s",
+ error_name, strerror(error));
+ return ErrorStatus{error};
+ }
+ return {};
+}
+
+Status<int> ChannelEventReceiver::GetPendingEvents() const {
+ constexpr long kTimeoutMs = 0;
+ epoll_event event;
+ const int count =
+ RETRY_EINTR(epoll_wait(epoll_fd_.Get(), &event, 1, kTimeoutMs));
+
+ Status<int> status;
+ if (count < 0) {
+ status.SetError(errno);
+ ALOGE("ChannelEventReceiver::GetPendingEvents: Failed to get events: %s",
+ status.GetErrorMessage().c_str());
+ return status;
+ }
+
+ const int mask_out = event.data.u32;
+ ALOGD_IF(TRACE, "ChannelEventReceiver::GetPendingEvents: mask_out=%x",
+ mask_out);
+
+ status.SetValue(mask_out);
+ return status;
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/channel_manager.cpp b/libs/vr/libpdx_uds/channel_manager.cpp
new file mode 100644
index 0000000000..afc0a4f041
--- /dev/null
+++ b/libs/vr/libpdx_uds/channel_manager.cpp
@@ -0,0 +1,44 @@
+#include <uds/channel_manager.h>
+
+#include <log/log.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+ChannelManager& ChannelManager::Get() {
+ static ChannelManager instance;
+ return instance;
+}
+
+void ChannelManager::CloseHandle(int32_t handle) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ auto channel = channels_.find(handle);
+ if (channel == channels_.end()) {
+ ALOGE("Invalid channel handle: %d", handle);
+ } else {
+ channels_.erase(channel);
+ }
+}
+
+LocalChannelHandle ChannelManager::CreateHandle(LocalHandle data_fd,
+ LocalHandle event_fd) {
+ if (data_fd && event_fd) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ int32_t handle = data_fd.Get();
+ channels_.emplace(handle,
+ ChannelData{std::move(data_fd), std::move(event_fd)});
+ return LocalChannelHandle(this, handle);
+ }
+ return LocalChannelHandle(nullptr, -1);
+}
+
+ChannelManager::ChannelData* ChannelManager::GetChannelData(int32_t handle) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ auto channel = channels_.find(handle);
+ return channel != channels_.end() ? &channel->second : nullptr;
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp
new file mode 100644
index 0000000000..9d9161784a
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel.cpp
@@ -0,0 +1,293 @@
+#include "uds/client_channel.h"
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include <pdx/client.h>
+#include <pdx/service_endpoint.h>
+#include <uds/ipc_helper.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+namespace {
+
+struct TransactionState {
+ bool GetLocalFileHandle(int index, LocalHandle* handle) {
+ if (index < 0) {
+ handle->Reset(index);
+ } else if (static_cast<size_t>(index) < response.file_descriptors.size()) {
+ *handle = std::move(response.file_descriptors[index]);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) {
+ if (index < 0) {
+ *handle = LocalChannelHandle{nullptr, index};
+ } else if (static_cast<size_t>(index) < response.channels.size()) {
+ auto& channel_info = response.channels[index];
+ *handle = ChannelManager::Get().CreateHandle(
+ std::move(channel_info.data_fd), std::move(channel_info.event_fd));
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ FileReference PushFileHandle(BorrowedHandle handle) {
+ if (!handle)
+ return handle.Get();
+ request.file_descriptors.push_back(std::move(handle));
+ return request.file_descriptors.size() - 1;
+ }
+
+ ChannelReference PushChannelHandle(BorrowedChannelHandle handle) {
+ if (!handle)
+ return handle.value();
+
+ if (auto* channel_data =
+ ChannelManager::Get().GetChannelData(handle.value())) {
+ ChannelInfo<BorrowedHandle> channel_info;
+ channel_info.data_fd.Reset(handle.value());
+ channel_info.event_fd = channel_data->event_receiver.event_fd();
+ request.channels.push_back(std::move(channel_info));
+ return request.channels.size() - 1;
+ } else {
+ return -1;
+ }
+ }
+
+ RequestHeader<BorrowedHandle> request;
+ ResponseHeader<LocalHandle> response;
+};
+
+Status<void> ReadAndDiscardData(const BorrowedHandle& socket_fd, size_t size) {
+ while (size > 0) {
+ // If there is more data to read in the message than the buffers provided
+ // by the caller, read and discard the extra data from the socket.
+ char buffer[1024];
+ size_t size_to_read = std::min(sizeof(buffer), size);
+ auto status = ReceiveData(socket_fd, buffer, size_to_read);
+ if (!status)
+ return status;
+ size -= size_to_read;
+ }
+ // We still want to return EIO error to the caller in case we had unexpected
+ // data in the socket stream.
+ return ErrorStatus(EIO);
+}
+
+Status<void> SendRequest(const BorrowedHandle& socket_fd,
+ TransactionState* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ size_t max_recv_len) {
+ size_t send_len = CountVectorSize(send_vector, send_count);
+ InitRequest(&transaction_state->request, opcode, send_len, max_recv_len,
+ false);
+ auto status = SendData(socket_fd, transaction_state->request);
+ if (status && send_len > 0)
+ status = SendDataVector(socket_fd, send_vector, send_count);
+ return status;
+}
+
+Status<void> ReceiveResponse(const BorrowedHandle& socket_fd,
+ TransactionState* transaction_state,
+ const iovec* receive_vector, size_t receive_count,
+ size_t max_recv_len) {
+ auto status = ReceiveData(socket_fd, &transaction_state->response);
+ if (!status)
+ return status;
+
+ if (transaction_state->response.recv_len > 0) {
+ std::vector<iovec> read_buffers;
+ size_t size_remaining = 0;
+ if (transaction_state->response.recv_len != max_recv_len) {
+ // If the receive buffer not exactly the size of data available, recreate
+ // the vector list to consume the data exactly since ReceiveDataVector()
+ // validates that the number of bytes received equals the number of bytes
+ // requested.
+ size_remaining = transaction_state->response.recv_len;
+ for (size_t i = 0; i < receive_count && size_remaining > 0; i++) {
+ read_buffers.push_back(receive_vector[i]);
+ iovec& last_vec = read_buffers.back();
+ if (last_vec.iov_len > size_remaining)
+ last_vec.iov_len = size_remaining;
+ size_remaining -= last_vec.iov_len;
+ }
+ receive_vector = read_buffers.data();
+ receive_count = read_buffers.size();
+ }
+ status = ReceiveDataVector(socket_fd, receive_vector, receive_count);
+ if (status && size_remaining > 0)
+ status = ReadAndDiscardData(socket_fd, size_remaining);
+ }
+ return status;
+}
+
+} // anonymous namespace
+
+ClientChannel::ClientChannel(LocalChannelHandle channel_handle)
+ : channel_handle_{std::move(channel_handle)} {
+ channel_data_ = ChannelManager::Get().GetChannelData(channel_handle_.value());
+}
+
+std::unique_ptr<pdx::ClientChannel> ClientChannel::Create(
+ LocalChannelHandle channel_handle) {
+ return std::unique_ptr<pdx::ClientChannel>{
+ new ClientChannel{std::move(channel_handle)}};
+}
+
+ClientChannel::~ClientChannel() {
+ if (channel_handle_)
+ shutdown(channel_handle_.value(), SHUT_WR);
+}
+
+void* ClientChannel::AllocateTransactionState() { return new TransactionState; }
+
+void ClientChannel::FreeTransactionState(void* state) {
+ delete static_cast<TransactionState*>(state);
+}
+
+Status<void> ClientChannel::SendImpulse(int opcode, const void* buffer,
+ size_t length) {
+ std::unique_lock<std::mutex> lock(socket_mutex_);
+ Status<void> status;
+ android::pdx::uds::RequestHeader<BorrowedHandle> request;
+ if (length > request.impulse_payload.size() ||
+ (buffer == nullptr && length != 0)) {
+ status.SetError(EINVAL);
+ return status;
+ }
+
+ InitRequest(&request, opcode, length, 0, true);
+ memcpy(request.impulse_payload.data(), buffer, length);
+ return SendData(BorrowedHandle{channel_handle_.value()}, request);
+}
+
+Status<int> ClientChannel::SendAndReceive(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ std::unique_lock<std::mutex> lock(socket_mutex_);
+ Status<int> result;
+ if ((send_vector == nullptr && send_count != 0) ||
+ (receive_vector == nullptr && receive_count != 0)) {
+ result.SetError(EINVAL);
+ return result;
+ }
+
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ size_t max_recv_len = CountVectorSize(receive_vector, receive_count);
+
+ auto status = SendRequest(BorrowedHandle{channel_handle_.value()}, state,
+ opcode, send_vector, send_count, max_recv_len);
+ if (status) {
+ status = ReceiveResponse(BorrowedHandle{channel_handle_.value()}, state,
+ receive_vector, receive_count, max_recv_len);
+ }
+ if (!result.PropagateError(status)) {
+ const int return_code = state->response.ret_code;
+ if (return_code >= 0)
+ result.SetValue(return_code);
+ else
+ result.SetError(-return_code);
+ }
+ return result;
+}
+
+Status<int> ClientChannel::SendWithInt(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) {
+ return SendAndReceive(transaction_state, opcode, send_vector, send_count,
+ receive_vector, receive_count);
+}
+
+Status<LocalHandle> ClientChannel::SendWithFileHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) {
+ Status<int> int_status =
+ SendAndReceive(transaction_state, opcode, send_vector, send_count,
+ receive_vector, receive_count);
+ Status<LocalHandle> status;
+ if (status.PropagateError(int_status))
+ return status;
+
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ LocalHandle handle;
+ if (state->GetLocalFileHandle(int_status.get(), &handle)) {
+ status.SetValue(std::move(handle));
+ } else {
+ status.SetError(EINVAL);
+ }
+ return status;
+}
+
+Status<LocalChannelHandle> ClientChannel::SendWithChannelHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector, size_t receive_count) {
+ Status<int> int_status =
+ SendAndReceive(transaction_state, opcode, send_vector, send_count,
+ receive_vector, receive_count);
+ Status<LocalChannelHandle> status;
+ if (status.PropagateError(int_status))
+ return status;
+
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ LocalChannelHandle handle;
+ if (state->GetLocalChannelHandle(int_status.get(), &handle)) {
+ status.SetValue(std::move(handle));
+ } else {
+ status.SetError(EINVAL);
+ }
+ return status;
+}
+
+FileReference ClientChannel::PushFileHandle(void* transaction_state,
+ const LocalHandle& handle) {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->PushFileHandle(handle.Borrow());
+}
+
+FileReference ClientChannel::PushFileHandle(void* transaction_state,
+ const BorrowedHandle& handle) {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->PushFileHandle(handle.Duplicate());
+}
+
+ChannelReference ClientChannel::PushChannelHandle(
+ void* transaction_state, const LocalChannelHandle& handle) {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->PushChannelHandle(handle.Borrow());
+}
+
+ChannelReference ClientChannel::PushChannelHandle(
+ void* transaction_state, const BorrowedChannelHandle& handle) {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->PushChannelHandle(handle.Duplicate());
+}
+
+bool ClientChannel::GetFileHandle(void* transaction_state, FileReference ref,
+ LocalHandle* handle) const {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->GetLocalFileHandle(ref, handle);
+}
+
+bool ClientChannel::GetChannelHandle(void* transaction_state,
+ ChannelReference ref,
+ LocalChannelHandle* handle) const {
+ auto* state = static_cast<TransactionState*>(transaction_state);
+ return state->GetLocalChannelHandle(ref, handle);
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel_factory.cpp b/libs/vr/libpdx_uds/client_channel_factory.cpp
new file mode 100644
index 0000000000..433f459769
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel_factory.cpp
@@ -0,0 +1,160 @@
+#include <uds/client_channel_factory.h>
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <thread>
+
+#include <uds/channel_manager.h>
+#include <uds/client_channel.h>
+#include <uds/ipc_helper.h>
+
+using std::chrono::duration_cast;
+using std::chrono::steady_clock;
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+std::string ClientChannelFactory::GetRootEndpointPath() {
+ return "/dev/socket/pdx";
+}
+
+std::string ClientChannelFactory::GetEndpointPath(
+ const std::string& endpoint_path) {
+ std::string path;
+ if (!endpoint_path.empty()) {
+ if (endpoint_path.front() == '/')
+ path = endpoint_path;
+ else
+ path = GetRootEndpointPath() + '/' + endpoint_path;
+ }
+ return path;
+}
+
+ClientChannelFactory::ClientChannelFactory(const std::string& endpoint_path)
+ : endpoint_path_{GetEndpointPath(endpoint_path)} {}
+
+ClientChannelFactory::ClientChannelFactory(LocalHandle socket)
+ : socket_{std::move(socket)} {}
+
+std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
+ const std::string& endpoint_path) {
+ return std::unique_ptr<pdx::ClientChannelFactory>{
+ new ClientChannelFactory{endpoint_path}};
+}
+
+std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
+ LocalHandle socket) {
+ return std::unique_ptr<pdx::ClientChannelFactory>{
+ new ClientChannelFactory{std::move(socket)}};
+}
+
+Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect(
+ int64_t timeout_ms) const {
+ Status<void> status;
+
+ bool connected = socket_.IsValid();
+ if (!connected) {
+ socket_.Reset(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ LOG_ALWAYS_FATAL_IF(
+ endpoint_path_.empty(),
+ "ClientChannelFactory::Connect: unspecified socket path");
+ }
+
+ if (!socket_) {
+ ALOGE("ClientChannelFactory::Connect: socket error: %s", strerror(errno));
+ return ErrorStatus(errno);
+ }
+
+ bool use_timeout = (timeout_ms >= 0);
+ auto now = steady_clock::now();
+ auto time_end = now + std::chrono::milliseconds{timeout_ms};
+
+ int max_eaccess = 5; // Max number of times to retry when EACCES returned.
+ while (!connected) {
+ int64_t timeout = -1;
+ if (use_timeout) {
+ auto remaining = time_end - now;
+ timeout = duration_cast<std::chrono::milliseconds>(remaining).count();
+ if (timeout < 0)
+ return ErrorStatus(ETIMEDOUT);
+ }
+ sockaddr_un remote;
+ remote.sun_family = AF_UNIX;
+ strncpy(remote.sun_path, endpoint_path_.c_str(), sizeof(remote.sun_path));
+ remote.sun_path[sizeof(remote.sun_path) - 1] = '\0';
+ ALOGD("ClientChannelFactory: Waiting for endpoint at %s", remote.sun_path);
+ status = WaitForEndpoint(endpoint_path_, timeout);
+ if (!status)
+ return ErrorStatus(status.error());
+
+ ALOGD("ClientChannelFactory: Connecting to %s", remote.sun_path);
+ int ret = RETRY_EINTR(connect(
+ socket_.Get(), reinterpret_cast<sockaddr*>(&remote), sizeof(remote)));
+ if (ret == -1) {
+ ALOGD("ClientChannelFactory: Connect error %d: %s", errno,
+ strerror(errno));
+ // if |max_eaccess| below reaches zero when errno is EACCES, the control
+ // flows into the next "else if" statement and a permanent error is
+ // returned from this function.
+ if (errno == ECONNREFUSED || (errno == EACCES && max_eaccess-- > 0)) {
+ // Connection refused/Permission denied can be the result of connecting
+ // too early (the service socket is created but its access rights are
+ // not set or not being listened to yet).
+ ALOGD("ClientChannelFactory: %s, waiting...", strerror(errno));
+ using namespace std::literals::chrono_literals;
+ std::this_thread::sleep_for(100ms);
+ } else if (errno != ENOENT && errno != ENOTDIR) {
+ // ENOENT/ENOTDIR might mean that the socket file/directory containing
+ // it has been just deleted. Try to wait for its creation and do not
+ // return an error immediately.
+ ALOGE(
+ "ClientChannelFactory::Connect: Failed to initialize connection "
+ "when connecting: %s",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+ } else {
+ connected = true;
+ ALOGD("ClientChannelFactory: Connected successfully to %s...",
+ remote.sun_path);
+ ChannelConnectionInfo<LocalHandle> connection_info;
+ status = ReceiveData(socket_.Borrow(), &connection_info);
+ if (!status)
+ return status.error_status();
+ socket_ = std::move(connection_info.channel_fd);
+ if (!socket_) {
+ ALOGE("ClientChannelFactory::Connect: Failed to obtain channel socket");
+ return ErrorStatus(EIO);
+ }
+ }
+ if (use_timeout)
+ now = steady_clock::now();
+ } // while (!connected)
+
+ RequestHeader<BorrowedHandle> request;
+ InitRequest(&request, opcodes::CHANNEL_OPEN, 0, 0, false);
+ status = SendData(socket_.Borrow(), request);
+ if (!status)
+ return status.error_status();
+ ResponseHeader<LocalHandle> response;
+ status = ReceiveData(socket_.Borrow(), &response);
+ if (!status)
+ return status.error_status();
+ int ref = response.ret_code;
+ if (ref < 0 || static_cast<size_t>(ref) > response.file_descriptors.size())
+ return ErrorStatus(EIO);
+
+ LocalHandle event_fd = std::move(response.file_descriptors[ref]);
+ return ClientChannel::Create(ChannelManager::Get().CreateHandle(
+ std::move(socket_), std::move(event_fd)));
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel_tests.cpp b/libs/vr/libpdx_uds/client_channel_tests.cpp
new file mode 100644
index 0000000000..7c3c68aa31
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel_tests.cpp
@@ -0,0 +1,162 @@
+#include <uds/client_channel.h>
+
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <limits>
+#include <random>
+#include <thread>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <pdx/client.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/service.h>
+
+#include <uds/client_channel_factory.h>
+#include <uds/service_endpoint.h>
+
+using testing::Return;
+using testing::_;
+
+using android::pdx::ClientBase;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::ServiceBase;
+using android::pdx::ServiceDispatcher;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::uds::ClientChannel;
+using android::pdx::uds::ClientChannelFactory;
+using android::pdx::uds::Endpoint;
+
+namespace {
+
+struct TestProtocol {
+ using DataType = int8_t;
+ enum {
+ kOpSum = 0,
+ };
+ PDX_REMOTE_METHOD(Sum, kOpSum, int64_t(const std::vector<DataType>&));
+};
+
+class TestService : public ServiceBase<TestService> {
+ public:
+ TestService(std::unique_ptr<Endpoint> endpoint)
+ : ServiceBase{"TestService", std::move(endpoint)} {}
+
+ Status<void> HandleMessage(Message& message) override {
+ switch (message.GetOp()) {
+ case TestProtocol::kOpSum:
+ DispatchRemoteMethod<TestProtocol::Sum>(*this, &TestService::OnSum,
+ message);
+ return {};
+
+ default:
+ return Service::HandleMessage(message);
+ }
+ }
+
+ int64_t OnSum(Message& /*message*/,
+ const std::vector<TestProtocol::DataType>& data) {
+ return std::accumulate(data.begin(), data.end(), int64_t{0});
+ }
+};
+
+class TestClient : public ClientBase<TestClient> {
+ public:
+ using ClientBase::ClientBase;
+
+ int64_t Sum(const std::vector<TestProtocol::DataType>& data) {
+ auto status = InvokeRemoteMethod<TestProtocol::Sum>(data);
+ return status ? status.get() : -1;
+ }
+};
+
+class TestServiceRunner {
+ public:
+ TestServiceRunner(LocalHandle channel_socket) {
+ auto endpoint = Endpoint::CreateFromSocketFd(LocalHandle{});
+ endpoint->RegisterNewChannelForTests(std::move(channel_socket));
+ service_ = TestService::Create(std::move(endpoint));
+ dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+ dispatcher_->AddService(service_);
+ dispatch_thread_ = std::thread(
+ std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
+ }
+
+ ~TestServiceRunner() {
+ dispatcher_->SetCanceled(true);
+ dispatch_thread_.join();
+ dispatcher_->RemoveService(service_);
+ }
+
+ private:
+ std::shared_ptr<TestService> service_;
+ std::unique_ptr<ServiceDispatcher> dispatcher_;
+ std::thread dispatch_thread_;
+};
+
+class ClientChannelTest : public testing::Test {
+ public:
+ void SetUp() override {
+ int channel_sockets[2] = {};
+ ASSERT_EQ(
+ 0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, channel_sockets));
+ LocalHandle service_channel{channel_sockets[0]};
+ LocalHandle client_channel{channel_sockets[1]};
+
+ service_runner_.reset(new TestServiceRunner{std::move(service_channel)});
+ auto factory = ClientChannelFactory::Create(std::move(client_channel));
+ auto status = factory->Connect(android::pdx::Client::kInfiniteTimeout);
+ ASSERT_TRUE(status);
+ client_ = TestClient::Create(status.take());
+ }
+
+ void TearDown() override {
+ service_runner_.reset();
+ client_.reset();
+ }
+
+ protected:
+ std::unique_ptr<TestServiceRunner> service_runner_;
+ std::shared_ptr<TestClient> client_;
+};
+
+TEST_F(ClientChannelTest, MultithreadedClient) {
+ constexpr int kNumTestThreads = 8;
+ constexpr size_t kDataSize = 1000; // Try to keep RPC buffer size below 4K.
+
+ std::random_device rd;
+ std::mt19937 gen{rd()};
+ std::uniform_int_distribution<TestProtocol::DataType> dist{
+ std::numeric_limits<TestProtocol::DataType>::min(),
+ std::numeric_limits<TestProtocol::DataType>::max()};
+
+ auto worker = [](std::shared_ptr<TestClient> client,
+ std::vector<TestProtocol::DataType> data) {
+ constexpr int kMaxIterations = 500;
+ int64_t expected = std::accumulate(data.begin(), data.end(), int64_t{0});
+ for (int i = 0; i < kMaxIterations; i++) {
+ ASSERT_EQ(expected, client->Sum(data));
+ }
+ };
+
+ // Start client threads.
+ std::vector<TestProtocol::DataType> data;
+ data.resize(kDataSize);
+ std::vector<std::thread> threads;
+ for (int i = 0; i < kNumTestThreads; i++) {
+ std::generate(data.begin(), data.end(),
+ [&dist, &gen]() { return dist(gen); });
+ threads.emplace_back(worker, client_, data);
+ }
+
+ // Wait for threads to finish.
+ for (auto& thread : threads)
+ thread.join();
+}
+
+} // namespace
diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp
new file mode 100644
index 0000000000..d75ce86e4b
--- /dev/null
+++ b/libs/vr/libpdx_uds/ipc_helper.cpp
@@ -0,0 +1,534 @@
+#include "uds/ipc_helper.h"
+
+#include <alloca.h>
+#include <errno.h>
+#include <log/log.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+
+#include <pdx/service.h>
+#include <pdx/utility.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+namespace {
+
+// Default implementations of Send/Receive interfaces to use standard socket
+// send/sendmsg/recv/recvmsg functions.
+class SocketSender : public SendInterface {
+ public:
+ ssize_t Send(int socket_fd, const void* data, size_t size,
+ int flags) override {
+ return send(socket_fd, data, size, flags);
+ }
+ ssize_t SendMessage(int socket_fd, const msghdr* msg, int flags) override {
+ return sendmsg(socket_fd, msg, flags);
+ }
+} g_socket_sender;
+
+class SocketReceiver : public RecvInterface {
+ public:
+ ssize_t Receive(int socket_fd, void* data, size_t size, int flags) override {
+ return recv(socket_fd, data, size, flags);
+ }
+ ssize_t ReceiveMessage(int socket_fd, msghdr* msg, int flags) override {
+ return recvmsg(socket_fd, msg, flags);
+ }
+} g_socket_receiver;
+
+} // anonymous namespace
+
+// Helper wrappers around send()/sendmsg() which repeat send() calls on data
+// that was not sent with the initial call to send/sendmsg. This is important to
+// handle transmissions interrupted by signals.
+Status<void> SendAll(SendInterface* sender, const BorrowedHandle& socket_fd,
+ const void* data, size_t size) {
+ Status<void> ret;
+ const uint8_t* ptr = static_cast<const uint8_t*>(data);
+ while (size > 0) {
+ ssize_t size_written =
+ RETRY_EINTR(sender->Send(socket_fd.Get(), ptr, size, MSG_NOSIGNAL));
+ if (size_written < 0) {
+ ret.SetError(errno);
+ ALOGE("SendAll: Failed to send data over socket: %s",
+ ret.GetErrorMessage().c_str());
+ break;
+ }
+ size -= size_written;
+ ptr += size_written;
+ }
+ return ret;
+}
+
+Status<void> SendMsgAll(SendInterface* sender, const BorrowedHandle& socket_fd,
+ const msghdr* msg) {
+ Status<void> ret;
+ ssize_t sent_size =
+ RETRY_EINTR(sender->SendMessage(socket_fd.Get(), msg, MSG_NOSIGNAL));
+ if (sent_size < 0) {
+ ret.SetError(errno);
+ ALOGE("SendMsgAll: Failed to send data over socket: %s",
+ ret.GetErrorMessage().c_str());
+ return ret;
+ }
+
+ ssize_t chunk_start_offset = 0;
+ for (size_t i = 0; i < msg->msg_iovlen; i++) {
+ ssize_t chunk_end_offset = chunk_start_offset + msg->msg_iov[i].iov_len;
+ if (sent_size < chunk_end_offset) {
+ size_t offset_within_chunk = sent_size - chunk_start_offset;
+ size_t data_size = msg->msg_iov[i].iov_len - offset_within_chunk;
+ const uint8_t* chunk_base =
+ static_cast<const uint8_t*>(msg->msg_iov[i].iov_base);
+ ret = SendAll(sender, socket_fd, chunk_base + offset_within_chunk,
+ data_size);
+ if (!ret)
+ break;
+ sent_size += data_size;
+ }
+ chunk_start_offset = chunk_end_offset;
+ }
+ return ret;
+}
+
+// Helper wrappers around recv()/recvmsg() which repeat recv() calls on data
+// that was not received with the initial call to recvmsg(). This is important
+// to handle transmissions interrupted by signals as well as the case when
+// initial data did not arrive in a single chunk over the socket (e.g. socket
+// buffer was full at the time of transmission, and only portion of initial
+// message was sent and the rest was blocked until the buffer was cleared by the
+// receiving side).
+Status<void> RecvMsgAll(RecvInterface* receiver,
+ const BorrowedHandle& socket_fd, msghdr* msg) {
+ Status<void> ret;
+ ssize_t size_read = RETRY_EINTR(receiver->ReceiveMessage(
+ socket_fd.Get(), msg, MSG_WAITALL | MSG_CMSG_CLOEXEC));
+ if (size_read < 0) {
+ ret.SetError(errno);
+ ALOGE("RecvMsgAll: Failed to receive data from socket: %s",
+ ret.GetErrorMessage().c_str());
+ return ret;
+ } else if (size_read == 0) {
+ ret.SetError(ESHUTDOWN);
+ ALOGW("RecvMsgAll: Socket has been shut down");
+ return ret;
+ }
+
+ ssize_t chunk_start_offset = 0;
+ for (size_t i = 0; i < msg->msg_iovlen; i++) {
+ ssize_t chunk_end_offset = chunk_start_offset + msg->msg_iov[i].iov_len;
+ if (size_read < chunk_end_offset) {
+ size_t offset_within_chunk = size_read - chunk_start_offset;
+ size_t data_size = msg->msg_iov[i].iov_len - offset_within_chunk;
+ uint8_t* chunk_base = static_cast<uint8_t*>(msg->msg_iov[i].iov_base);
+ ret = RecvAll(receiver, socket_fd, chunk_base + offset_within_chunk,
+ data_size);
+ if (!ret)
+ break;
+ size_read += data_size;
+ }
+ chunk_start_offset = chunk_end_offset;
+ }
+ return ret;
+}
+
+Status<void> RecvAll(RecvInterface* receiver, const BorrowedHandle& socket_fd,
+ void* data, size_t size) {
+ Status<void> ret;
+ uint8_t* ptr = static_cast<uint8_t*>(data);
+ while (size > 0) {
+ ssize_t size_read = RETRY_EINTR(receiver->Receive(
+ socket_fd.Get(), ptr, size, MSG_WAITALL | MSG_CMSG_CLOEXEC));
+ if (size_read < 0) {
+ ret.SetError(errno);
+ ALOGE("RecvAll: Failed to receive data from socket: %s",
+ ret.GetErrorMessage().c_str());
+ break;
+ } else if (size_read == 0) {
+ ret.SetError(ESHUTDOWN);
+ ALOGW("RecvAll: Socket has been shut down");
+ break;
+ }
+ size -= size_read;
+ ptr += size_read;
+ }
+ return ret;
+}
+
+uint32_t kMagicPreamble = 0x7564736d; // 'udsm'.
+
+struct MessagePreamble {
+ uint32_t magic{0};
+ uint32_t data_size{0};
+ uint32_t fd_count{0};
+};
+
+Status<void> SendPayload::Send(const BorrowedHandle& socket_fd) {
+ return Send(socket_fd, nullptr);
+}
+
+Status<void> SendPayload::Send(const BorrowedHandle& socket_fd,
+ const ucred* cred) {
+ SendInterface* sender = sender_ ? sender_ : &g_socket_sender;
+ MessagePreamble preamble;
+ preamble.magic = kMagicPreamble;
+ preamble.data_size = buffer_.size();
+ preamble.fd_count = file_handles_.size();
+ Status<void> ret = SendAll(sender, socket_fd, &preamble, sizeof(preamble));
+ if (!ret)
+ return ret;
+
+ msghdr msg = {};
+ iovec recv_vect = {buffer_.data(), buffer_.size()};
+ msg.msg_iov = &recv_vect;
+ msg.msg_iovlen = 1;
+
+ if (cred || !file_handles_.empty()) {
+ const size_t fd_bytes = file_handles_.size() * sizeof(int);
+ msg.msg_controllen = (cred ? CMSG_SPACE(sizeof(ucred)) : 0) +
+ (fd_bytes == 0 ? 0 : CMSG_SPACE(fd_bytes));
+ msg.msg_control = alloca(msg.msg_controllen);
+
+ cmsghdr* control = CMSG_FIRSTHDR(&msg);
+ if (cred) {
+ control->cmsg_level = SOL_SOCKET;
+ control->cmsg_type = SCM_CREDENTIALS;
+ control->cmsg_len = CMSG_LEN(sizeof(ucred));
+ memcpy(CMSG_DATA(control), cred, sizeof(ucred));
+ control = CMSG_NXTHDR(&msg, control);
+ }
+
+ if (fd_bytes) {
+ control->cmsg_level = SOL_SOCKET;
+ control->cmsg_type = SCM_RIGHTS;
+ control->cmsg_len = CMSG_LEN(fd_bytes);
+ memcpy(CMSG_DATA(control), file_handles_.data(), fd_bytes);
+ }
+ }
+
+ return SendMsgAll(sender, socket_fd, &msg);
+}
+
+// MessageWriter
+void* SendPayload::GetNextWriteBufferSection(size_t size) {
+ return buffer_.grow_by(size);
+}
+
+OutputResourceMapper* SendPayload::GetOutputResourceMapper() { return this; }
+
+// OutputResourceMapper
+Status<FileReference> SendPayload::PushFileHandle(const LocalHandle& handle) {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+}
+
+Status<FileReference> SendPayload::PushFileHandle(
+ const BorrowedHandle& handle) {
+ if (handle) {
+ const int ref = file_handles_.size();
+ file_handles_.push_back(handle.Get());
+ return ref;
+ } else {
+ return handle.Get();
+ }
+}
+
+Status<FileReference> SendPayload::PushFileHandle(const RemoteHandle& handle) {
+ return handle.Get();
+}
+
+Status<ChannelReference> SendPayload::PushChannelHandle(
+ const LocalChannelHandle& /*handle*/) {
+ return ErrorStatus{EOPNOTSUPP};
+}
+Status<ChannelReference> SendPayload::PushChannelHandle(
+ const BorrowedChannelHandle& /*handle*/) {
+ return ErrorStatus{EOPNOTSUPP};
+}
+Status<ChannelReference> SendPayload::PushChannelHandle(
+ const RemoteChannelHandle& /*handle*/) {
+ return ErrorStatus{EOPNOTSUPP};
+}
+
+Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd) {
+ return Receive(socket_fd, nullptr);
+}
+
+Status<void> ReceivePayload::Receive(const BorrowedHandle& socket_fd,
+ ucred* cred) {
+ RecvInterface* receiver = receiver_ ? receiver_ : &g_socket_receiver;
+ MessagePreamble preamble;
+ Status<void> ret = RecvAll(receiver, socket_fd, &preamble, sizeof(preamble));
+ if (!ret)
+ return ret;
+
+ if (preamble.magic != kMagicPreamble) {
+ ALOGE("ReceivePayload::Receive: Message header is invalid");
+ ret.SetError(EIO);
+ return ret;
+ }
+
+ buffer_.resize(preamble.data_size);
+ file_handles_.clear();
+ read_pos_ = 0;
+
+ msghdr msg = {};
+ iovec recv_vect = {buffer_.data(), buffer_.size()};
+ msg.msg_iov = &recv_vect;
+ msg.msg_iovlen = 1;
+
+ if (cred || preamble.fd_count) {
+ const size_t receive_fd_bytes = preamble.fd_count * sizeof(int);
+ msg.msg_controllen =
+ (cred ? CMSG_SPACE(sizeof(ucred)) : 0) +
+ (receive_fd_bytes == 0 ? 0 : CMSG_SPACE(receive_fd_bytes));
+ msg.msg_control = alloca(msg.msg_controllen);
+ }
+
+ ret = RecvMsgAll(receiver, socket_fd, &msg);
+ if (!ret)
+ return ret;
+
+ bool cred_available = false;
+ file_handles_.reserve(preamble.fd_count);
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ while (cmsg) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cred && cmsg->cmsg_len == CMSG_LEN(sizeof(ucred))) {
+ cred_available = true;
+ memcpy(cred, CMSG_DATA(cmsg), sizeof(ucred));
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ socklen_t payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ const int* fds = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+ size_t fd_count = payload_len / sizeof(int);
+ std::transform(fds, fds + fd_count, std::back_inserter(file_handles_),
+ [](int fd) { return LocalHandle{fd}; });
+ }
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+
+ if (cred && !cred_available) {
+ ALOGE("ReceivePayload::Receive: Failed to obtain message credentials");
+ ret.SetError(EIO);
+ }
+
+ return ret;
+}
+
+// MessageReader
+MessageReader::BufferSection ReceivePayload::GetNextReadBufferSection() {
+ return {buffer_.data() + read_pos_, &*buffer_.end()};
+}
+
+void ReceivePayload::ConsumeReadBufferSectionData(const void* new_start) {
+ read_pos_ = PointerDistance(new_start, buffer_.data());
+}
+
+InputResourceMapper* ReceivePayload::GetInputResourceMapper() { return this; }
+
+// InputResourceMapper
+bool ReceivePayload::GetFileHandle(FileReference ref, LocalHandle* handle) {
+ if (ref < 0) {
+ *handle = LocalHandle{ref};
+ return true;
+ }
+ if (static_cast<size_t>(ref) > file_handles_.size())
+ return false;
+ *handle = std::move(file_handles_[ref]);
+ return true;
+}
+
+bool ReceivePayload::GetChannelHandle(ChannelReference /*ref*/,
+ LocalChannelHandle* /*handle*/) {
+ return false;
+}
+
+Status<void> SendData(const BorrowedHandle& socket_fd, const void* data,
+ size_t size) {
+ return SendAll(&g_socket_sender, socket_fd, data, size);
+}
+
+Status<void> SendDataVector(const BorrowedHandle& socket_fd, const iovec* data,
+ size_t count) {
+ msghdr msg = {};
+ msg.msg_iov = const_cast<iovec*>(data);
+ msg.msg_iovlen = count;
+ return SendMsgAll(&g_socket_sender, socket_fd, &msg);
+}
+
+Status<void> ReceiveData(const BorrowedHandle& socket_fd, void* data,
+ size_t size) {
+ return RecvAll(&g_socket_receiver, socket_fd, data, size);
+}
+
+Status<void> ReceiveDataVector(const BorrowedHandle& socket_fd,
+ const iovec* data, size_t count) {
+ msghdr msg = {};
+ msg.msg_iov = const_cast<iovec*>(data);
+ msg.msg_iovlen = count;
+ return RecvMsgAll(&g_socket_receiver, socket_fd, &msg);
+}
+
+size_t CountVectorSize(const iovec* vector, size_t count) {
+ return std::accumulate(
+ vector, vector + count, size_t{0},
+ [](size_t size, const iovec& vec) { return size + vec.iov_len; });
+}
+
+void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request,
+ int opcode, uint32_t send_len, uint32_t max_recv_len,
+ bool is_impulse) {
+ request->op = opcode;
+ request->cred.pid = getpid();
+ request->cred.uid = geteuid();
+ request->cred.gid = getegid();
+ request->send_len = send_len;
+ request->max_recv_len = max_recv_len;
+ request->is_impulse = is_impulse;
+}
+
+Status<void> WaitForEndpoint(const std::string& endpoint_path,
+ int64_t timeout_ms) {
+ // Endpoint path must be absolute.
+ if (endpoint_path.empty() || endpoint_path.front() != '/')
+ return ErrorStatus(EINVAL);
+
+ // Create inotify fd.
+ LocalHandle fd{inotify_init()};
+ if (!fd)
+ return ErrorStatus(errno);
+
+ // Set the inotify fd to non-blocking.
+ int ret = fcntl(fd.Get(), F_GETFL);
+ fcntl(fd.Get(), F_SETFL, ret | O_NONBLOCK);
+
+ // Setup the pollfd.
+ pollfd pfd = {fd.Get(), POLLIN, 0};
+
+ // Find locations of each path separator.
+ std::vector<size_t> separators{0}; // The path is absolute, so '/' is at #0.
+ size_t pos = endpoint_path.find('/', 1);
+ while (pos != std::string::npos) {
+ separators.push_back(pos);
+ pos = endpoint_path.find('/', pos + 1);
+ }
+ separators.push_back(endpoint_path.size());
+
+ // Walk down the path, checking for existence and waiting if needed.
+ pos = 1;
+ size_t links = 0;
+ std::string current;
+ while (pos < separators.size() && links <= MAXSYMLINKS) {
+ std::string previous = current;
+ current = endpoint_path.substr(0, separators[pos]);
+
+ // Check for existence; proceed to setup a watch if not.
+ if (access(current.c_str(), F_OK) < 0) {
+ if (errno != ENOENT)
+ return ErrorStatus(errno);
+
+ // Extract the name of the path component to wait for.
+ std::string next = current.substr(
+ separators[pos - 1] + 1, separators[pos] - separators[pos - 1] - 1);
+
+ // Add a watch on the last existing directory we reach.
+ int wd = inotify_add_watch(
+ fd.Get(), previous.c_str(),
+ IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_TO);
+ if (wd < 0) {
+ if (errno != ENOENT)
+ return ErrorStatus(errno);
+ // Restart at the beginning if previous was deleted.
+ links = 0;
+ current.clear();
+ pos = 1;
+ continue;
+ }
+
+ // Make sure current didn't get created before the watch was added.
+ ret = access(current.c_str(), F_OK);
+ if (ret < 0) {
+ if (errno != ENOENT)
+ return ErrorStatus(errno);
+
+ bool exit_poll = false;
+ while (!exit_poll) {
+ // Wait for an event or timeout.
+ ret = poll(&pfd, 1, timeout_ms);
+ if (ret <= 0)
+ return ErrorStatus(ret == 0 ? ETIMEDOUT : errno);
+
+ // Read events.
+ char buffer[sizeof(inotify_event) + NAME_MAX + 1];
+
+ ret = read(fd.Get(), buffer, sizeof(buffer));
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ continue;
+ else
+ return ErrorStatus(errno);
+ } else if (static_cast<size_t>(ret) < sizeof(struct inotify_event)) {
+ return ErrorStatus(EIO);
+ }
+
+ auto* event = reinterpret_cast<const inotify_event*>(buffer);
+ auto* end = reinterpret_cast<const inotify_event*>(buffer + ret);
+ while (event < end) {
+ std::string event_for;
+ if (event->len > 0)
+ event_for = event->name;
+
+ if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
+ // See if this is the droid we're looking for.
+ if (next == event_for) {
+ exit_poll = true;
+ break;
+ }
+ } else if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) {
+ // Restart at the beginning if our watch dir is deleted.
+ links = 0;
+ current.clear();
+ pos = 0;
+ exit_poll = true;
+ break;
+ }
+
+ event = reinterpret_cast<const inotify_event*>(AdvancePointer(
+ event, sizeof(struct inotify_event) + event->len));
+ } // while (event < end)
+ } // while (!exit_poll)
+ } // Current dir doesn't exist.
+ ret = inotify_rm_watch(fd.Get(), wd);
+ if (ret < 0 && errno != EINVAL)
+ return ErrorStatus(errno);
+ } // if (access(current.c_str(), F_OK) < 0)
+
+ // Check for symbolic link and update link count.
+ struct stat stat_buf;
+ ret = lstat(current.c_str(), &stat_buf);
+ if (ret < 0 && errno != ENOENT)
+ return ErrorStatus(errno);
+ else if (ret == 0 && S_ISLNK(stat_buf.st_mode))
+ links++;
+ pos++;
+ } // while (pos < separators.size() && links <= MAXSYMLINKS)
+
+ return {};
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/ipc_helper_tests.cpp b/libs/vr/libpdx_uds/ipc_helper_tests.cpp
new file mode 100644
index 0000000000..bfa827e641
--- /dev/null
+++ b/libs/vr/libpdx_uds/ipc_helper_tests.cpp
@@ -0,0 +1,365 @@
+#include "uds/ipc_helper.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using testing::Return;
+using testing::SetErrnoAndReturn;
+using testing::_;
+
+using android::pdx::BorrowedHandle;
+using android::pdx::uds::SendInterface;
+using android::pdx::uds::RecvInterface;
+using android::pdx::uds::SendAll;
+using android::pdx::uds::SendMsgAll;
+using android::pdx::uds::RecvAll;
+using android::pdx::uds::RecvMsgAll;
+
+namespace {
+
+// Useful constants for tests.
+static constexpr intptr_t kPtr = 1234;
+static constexpr int kSocketFd = 5678;
+static const BorrowedHandle kSocket{kSocketFd};
+
+// Helper functions to construct test data pointer values.
+void* IntToPtr(intptr_t value) { return reinterpret_cast<void*>(value); }
+const void* IntToConstPtr(intptr_t value) {
+ return reinterpret_cast<const void*>(value);
+}
+
+// Mock classes for SendInterface/RecvInterface.
+class MockSender : public SendInterface {
+ public:
+ MOCK_METHOD4(Send, ssize_t(int socket_fd, const void* data, size_t size,
+ int flags));
+ MOCK_METHOD3(SendMessage,
+ ssize_t(int socket_fd, const msghdr* msg, int flags));
+};
+
+class MockReceiver : public RecvInterface {
+ public:
+ MOCK_METHOD4(Receive,
+ ssize_t(int socket_fd, void* data, size_t size, int flags));
+ MOCK_METHOD3(ReceiveMessage, ssize_t(int socket_fd, msghdr* msg, int flags));
+};
+
+// Test case classes.
+class SendTest : public testing::Test {
+ public:
+ SendTest() {
+ ON_CALL(sender_, Send(_, _, _, _))
+ .WillByDefault(SetErrnoAndReturn(EIO, -1));
+ ON_CALL(sender_, SendMessage(_, _, _))
+ .WillByDefault(SetErrnoAndReturn(EIO, -1));
+ }
+
+ protected:
+ MockSender sender_;
+};
+
+class RecvTest : public testing::Test {
+ public:
+ RecvTest() {
+ ON_CALL(receiver_, Receive(_, _, _, _))
+ .WillByDefault(SetErrnoAndReturn(EIO, -1));
+ ON_CALL(receiver_, ReceiveMessage(_, _, _))
+ .WillByDefault(SetErrnoAndReturn(EIO, -1));
+ }
+
+ protected:
+ MockReceiver receiver_;
+};
+
+class MessageTestBase : public testing::Test {
+ public:
+ MessageTestBase() {
+ memset(&msg_, 0, sizeof(msg_));
+ msg_.msg_iovlen = data_.size();
+ msg_.msg_iov = data_.data();
+ }
+
+ protected:
+ static constexpr intptr_t kPtr1 = kPtr;
+ static constexpr intptr_t kPtr2 = kPtr + 200;
+ static constexpr intptr_t kPtr3 = kPtr + 1000;
+
+ MockSender sender_;
+ msghdr msg_;
+ std::vector<iovec> data_{
+ {IntToPtr(kPtr1), 100}, {IntToPtr(kPtr2), 200}, {IntToPtr(kPtr3), 300}};
+};
+
+class SendMessageTest : public MessageTestBase {
+ public:
+ SendMessageTest() {
+ ON_CALL(sender_, Send(_, _, _, _))
+ .WillByDefault(SetErrnoAndReturn(EIO, -1));
+ ON_CALL(sender_, SendMessage(_, _, _))
+ .WillByDefault(SetErrnoAndReturn(EIO, -1));
+ }
+
+ protected:
+ MockSender sender_;
+};
+
+class RecvMessageTest : public MessageTestBase {
+ public:
+ RecvMessageTest() {
+ ON_CALL(receiver_, Receive(_, _, _, _))
+ .WillByDefault(SetErrnoAndReturn(EIO, -1));
+ ON_CALL(receiver_, ReceiveMessage(_, _, _))
+ .WillByDefault(SetErrnoAndReturn(EIO, -1));
+ }
+
+ protected:
+ MockReceiver receiver_;
+};
+
+// Actual tests.
+
+// SendAll
+TEST_F(SendTest, Complete) {
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+ .WillOnce(Return(100));
+
+ auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(SendTest, Signal) {
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+ .WillOnce(Return(20));
+ EXPECT_CALL(sender_,
+ Send(kSocketFd, IntToConstPtr(kPtr + 20), 80, MSG_NOSIGNAL))
+ .WillOnce(Return(40));
+ EXPECT_CALL(sender_,
+ Send(kSocketFd, IntToConstPtr(kPtr + 60), 40, MSG_NOSIGNAL))
+ .WillOnce(Return(40));
+
+ auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(SendTest, Eintr) {
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+ .WillOnce(SetErrnoAndReturn(EINTR, -1))
+ .WillOnce(Return(100));
+
+ auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(SendTest, Error) {
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+
+ auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(SendTest, Error2) {
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr), 100, MSG_NOSIGNAL))
+ .WillOnce(Return(50));
+ EXPECT_CALL(sender_,
+ Send(kSocketFd, IntToConstPtr(kPtr + 50), 50, MSG_NOSIGNAL))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+
+ auto status = SendAll(&sender_, kSocket, IntToConstPtr(kPtr), 100);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+// RecvAll
+TEST_F(RecvTest, Complete) {
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100,
+ MSG_WAITALL | MSG_CMSG_CLOEXEC))
+ .WillOnce(Return(100));
+
+ auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(RecvTest, Signal) {
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _))
+ .WillOnce(Return(20));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 20), 80, _))
+ .WillOnce(Return(40));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 60), 40, _))
+ .WillOnce(Return(40));
+
+ auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(RecvTest, Eintr) {
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _))
+ .WillOnce(SetErrnoAndReturn(EINTR, -1))
+ .WillOnce(Return(100));
+
+ auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(RecvTest, Error) {
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+
+ auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+TEST_F(RecvTest, Error2) {
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr), 100, _))
+ .WillOnce(Return(30));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr + 30), 70, _))
+ .WillOnce(SetErrnoAndReturn(EIO, -1));
+
+ auto status = RecvAll(&receiver_, kSocket, IntToPtr(kPtr), 100);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EIO, status.error());
+}
+
+// SendMsgAll
+TEST_F(SendMessageTest, Complete) {
+ EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, MSG_NOSIGNAL))
+ .WillOnce(Return(600));
+
+ auto status = SendMsgAll(&sender_, kSocket, &msg_);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(SendMessageTest, Partial) {
+ EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(70));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 70), 30, _))
+ .WillOnce(Return(30));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2), 200, _))
+ .WillOnce(Return(190));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2 + 190), 10, _))
+ .WillOnce(Return(10));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3), 300, _))
+ .WillOnce(Return(300));
+
+ auto status = SendMsgAll(&sender_, kSocket, &msg_);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(SendMessageTest, Partial2) {
+ EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(310));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3 + 10), 290, _))
+ .WillOnce(Return(290));
+
+ auto status = SendMsgAll(&sender_, kSocket, &msg_);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(SendMessageTest, Eintr) {
+ EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _))
+ .WillOnce(SetErrnoAndReturn(EINTR, -1))
+ .WillOnce(Return(70));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 70), 30, _))
+ .WillOnce(SetErrnoAndReturn(EINTR, -1))
+ .WillOnce(Return(30));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr2), 200, _))
+ .WillOnce(Return(200));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr3), 300, _))
+ .WillOnce(Return(300));
+
+ auto status = SendMsgAll(&sender_, kSocket, &msg_);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(SendMessageTest, Error) {
+ EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _))
+ .WillOnce(SetErrnoAndReturn(EBADF, -1));
+
+ auto status = SendMsgAll(&sender_, kSocket, &msg_);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EBADF, status.error());
+}
+
+TEST_F(SendMessageTest, Error2) {
+ EXPECT_CALL(sender_, SendMessage(kSocketFd, &msg_, _)).WillOnce(Return(20));
+ EXPECT_CALL(sender_, Send(kSocketFd, IntToConstPtr(kPtr1 + 20), 80, _))
+ .WillOnce(SetErrnoAndReturn(EBADF, -1));
+
+ auto status = SendMsgAll(&sender_, kSocket, &msg_);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EBADF, status.error());
+}
+
+// RecvMsgAll
+TEST_F(RecvMessageTest, Complete) {
+ EXPECT_CALL(receiver_,
+ ReceiveMessage(kSocketFd, &msg_, MSG_WAITALL | MSG_CMSG_CLOEXEC))
+ .WillOnce(Return(600));
+
+ auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(RecvMessageTest, Partial) {
+ EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+ .WillOnce(Return(70));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 70), 30, _))
+ .WillOnce(Return(30));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2), 200, _))
+ .WillOnce(Return(190));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2 + 190), 10, _))
+ .WillOnce(Return(10));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3), 300, _))
+ .WillOnce(Return(300));
+
+ auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(RecvMessageTest, Partial2) {
+ EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+ .WillOnce(Return(310));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3 + 10), 290, _))
+ .WillOnce(Return(290));
+
+ auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(RecvMessageTest, Eintr) {
+ EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+ .WillOnce(SetErrnoAndReturn(EINTR, -1))
+ .WillOnce(Return(70));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 70), 30, _))
+ .WillOnce(SetErrnoAndReturn(EINTR, -1))
+ .WillOnce(Return(30));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr2), 200, _))
+ .WillOnce(Return(200));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr3), 300, _))
+ .WillOnce(Return(300));
+
+ auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+ EXPECT_TRUE(status);
+}
+
+TEST_F(RecvMessageTest, Error) {
+ EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+ .WillOnce(SetErrnoAndReturn(EBADF, -1));
+
+ auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EBADF, status.error());
+}
+
+TEST_F(RecvMessageTest, Error2) {
+ EXPECT_CALL(receiver_, ReceiveMessage(kSocketFd, &msg_, _))
+ .WillOnce(Return(20));
+ EXPECT_CALL(receiver_, Receive(kSocketFd, IntToPtr(kPtr1 + 20), 80, _))
+ .WillOnce(SetErrnoAndReturn(EBADF, -1));
+
+ auto status = RecvMsgAll(&receiver_, kSocket, &msg_);
+ ASSERT_FALSE(status);
+ EXPECT_EQ(EBADF, status.error());
+}
+
+} // namespace
diff --git a/libs/vr/libpdx_uds/private/uds/channel_event_set.h b/libs/vr/libpdx_uds/private/uds/channel_event_set.h
new file mode 100644
index 0000000000..1f464d5f91
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/channel_event_set.h
@@ -0,0 +1,62 @@
+#ifndef ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
+#define ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
+
+#include <errno.h>
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ChannelEventSet {
+ public:
+ ChannelEventSet();
+ ChannelEventSet(ChannelEventSet&&) = default;
+ ChannelEventSet& operator=(ChannelEventSet&&) = default;
+
+ BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); }
+
+ explicit operator bool() const { return !!epoll_fd_ && !!event_fd_; }
+
+ Status<void> AddDataFd(const LocalHandle& data_fd);
+ int ModifyEvents(int clear_mask, int set_mask);
+
+ private:
+ LocalHandle epoll_fd_;
+ LocalHandle event_fd_;
+ uint32_t event_bits_ = 0;
+
+ static Status<void> SetupHandle(int fd, LocalHandle* handle,
+ const char* error_name);
+
+ ChannelEventSet(const ChannelEventSet&) = delete;
+ void operator=(const ChannelEventSet&) = delete;
+};
+
+class ChannelEventReceiver {
+ public:
+ ChannelEventReceiver() = default;
+ ChannelEventReceiver(LocalHandle epoll_fd) : epoll_fd_{std::move(epoll_fd)} {}
+ ChannelEventReceiver(ChannelEventReceiver&&) = default;
+ ChannelEventReceiver& operator=(ChannelEventReceiver&&) = default;
+
+ BorrowedHandle event_fd() const { return epoll_fd_.Borrow(); }
+ Status<int> GetPendingEvents() const;
+
+ private:
+ LocalHandle epoll_fd_;
+
+ ChannelEventReceiver(const ChannelEventReceiver&) = delete;
+ void operator=(const ChannelEventReceiver&) = delete;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_CHANNEL_EVENT_SET_H_
diff --git a/libs/vr/libpdx_uds/private/uds/channel_manager.h b/libs/vr/libpdx_uds/private/uds/channel_manager.h
new file mode 100644
index 0000000000..2aca41421a
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/channel_manager.h
@@ -0,0 +1,40 @@
+#ifndef ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
+#define ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
+
+#include <mutex>
+#include <unordered_map>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <uds/channel_event_set.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ChannelManager : public ChannelManagerInterface {
+ public:
+ static ChannelManager& Get();
+
+ LocalChannelHandle CreateHandle(LocalHandle data_fd, LocalHandle event_fd);
+ struct ChannelData {
+ LocalHandle data_fd;
+ ChannelEventReceiver event_receiver;
+ };
+
+ ChannelData* GetChannelData(int32_t handle);
+
+ private:
+ ChannelManager() = default;
+
+ void CloseHandle(int32_t handle) override;
+
+ std::mutex mutex_;
+ std::unordered_map<int32_t, ChannelData> channels_;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_CHANNEL_MANAGER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel.h b/libs/vr/libpdx_uds/private/uds/client_channel.h
new file mode 100644
index 0000000000..8f607f56a1
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/client_channel.h
@@ -0,0 +1,85 @@
+#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
+
+#include <pdx/client_channel.h>
+
+#include <mutex>
+
+#include <uds/channel_event_set.h>
+#include <uds/channel_manager.h>
+#include <uds/service_endpoint.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ClientChannel : public pdx::ClientChannel {
+ public:
+ ~ClientChannel() override;
+
+ static std::unique_ptr<pdx::ClientChannel> Create(
+ LocalChannelHandle channel_handle);
+
+ uint32_t GetIpcTag() const override { return Endpoint::kIpcTag; }
+
+ int event_fd() const override {
+ return channel_data_ ? channel_data_->event_receiver.event_fd().Get() : -1;
+ }
+ Status<int> GetEventMask(int /*events*/) override {
+ if (channel_data_)
+ return channel_data_->event_receiver.GetPendingEvents();
+ else
+ return ErrorStatus(EINVAL);
+ }
+
+ LocalChannelHandle& GetChannelHandle() override { return channel_handle_; }
+ void* AllocateTransactionState() override;
+ void FreeTransactionState(void* state) override;
+
+ Status<void> SendImpulse(int opcode, const void* buffer,
+ size_t length) override;
+
+ Status<int> SendWithInt(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) override;
+ Status<LocalHandle> SendWithFileHandle(void* transaction_state, int opcode,
+ const iovec* send_vector,
+ size_t send_count,
+ const iovec* receive_vector,
+ size_t receive_count) override;
+ Status<LocalChannelHandle> SendWithChannelHandle(
+ void* transaction_state, int opcode, const iovec* send_vector,
+ size_t send_count, const iovec* receive_vector,
+ size_t receive_count) override;
+
+ FileReference PushFileHandle(void* transaction_state,
+ const LocalHandle& handle) override;
+ FileReference PushFileHandle(void* transaction_state,
+ const BorrowedHandle& handle) override;
+ ChannelReference PushChannelHandle(void* transaction_state,
+ const LocalChannelHandle& handle) override;
+ ChannelReference PushChannelHandle(
+ void* transaction_state, const BorrowedChannelHandle& handle) override;
+ bool GetFileHandle(void* transaction_state, FileReference ref,
+ LocalHandle* handle) const override;
+ bool GetChannelHandle(void* transaction_state, ChannelReference ref,
+ LocalChannelHandle* handle) const override;
+
+ private:
+ explicit ClientChannel(LocalChannelHandle channel_handle);
+
+ Status<int> SendAndReceive(void* transaction_state, int opcode,
+ const iovec* send_vector, size_t send_count,
+ const iovec* receive_vector, size_t receive_count);
+
+ LocalChannelHandle channel_handle_;
+ ChannelManager::ChannelData* channel_data_;
+ std::mutex socket_mutex_;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel_factory.h b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h
new file mode 100644
index 0000000000..c43c5c7c49
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h
@@ -0,0 +1,36 @@
+#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
+#define ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
+
+#include <string>
+
+#include <pdx/client_channel_factory.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ClientChannelFactory : public pdx::ClientChannelFactory {
+ public:
+ static std::unique_ptr<pdx::ClientChannelFactory> Create(
+ const std::string& endpoint_path);
+ static std::unique_ptr<pdx::ClientChannelFactory> Create(LocalHandle socket);
+
+ Status<std::unique_ptr<pdx::ClientChannel>> Connect(
+ int64_t timeout_ms) const override;
+
+ static std::string GetRootEndpointPath();
+ static std::string GetEndpointPath(const std::string& endpoint_path);
+
+ private:
+ explicit ClientChannelFactory(const std::string& endpoint_path);
+ explicit ClientChannelFactory(LocalHandle socket);
+
+ mutable LocalHandle socket_;
+ std::string endpoint_path_;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_CLIENT_CHANNEL_FACTORY_H_
diff --git a/libs/vr/libpdx_uds/private/uds/ipc_helper.h b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
new file mode 100644
index 0000000000..bde16d3d31
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
@@ -0,0 +1,214 @@
+#ifndef ANDROID_PDX_UDS_IPC_HELPER_H_
+#define ANDROID_PDX_UDS_IPC_HELPER_H_
+
+#include <sys/socket.h>
+#include <utility>
+#include <vector>
+
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/serialization.h>
+#include <pdx/status.h>
+#include <pdx/utility.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+// Test interfaces used for unit-testing payload sending/receiving over sockets.
+class SendInterface {
+ public:
+ virtual ssize_t Send(int socket_fd, const void* data, size_t size,
+ int flags) = 0;
+ virtual ssize_t SendMessage(int socket_fd, const msghdr* msg, int flags) = 0;
+
+ protected:
+ virtual ~SendInterface() = default;
+};
+
+class RecvInterface {
+ public:
+ virtual ssize_t Receive(int socket_fd, void* data, size_t size,
+ int flags) = 0;
+ virtual ssize_t ReceiveMessage(int socket_fd, msghdr* msg, int flags) = 0;
+
+ protected:
+ virtual ~RecvInterface() = default;
+};
+
+// Helper methods that allow to send/receive data through abstract interfaces.
+// Useful for mocking out the underlying socket I/O.
+Status<void> SendAll(SendInterface* sender, const BorrowedHandle& socket_fd,
+ const void* data, size_t size);
+Status<void> SendMsgAll(SendInterface* sender, const BorrowedHandle& socket_fd,
+ const msghdr* msg);
+Status<void> RecvAll(RecvInterface* receiver, const BorrowedHandle& socket_fd,
+ void* data, size_t size);
+Status<void> RecvMsgAll(RecvInterface* receiver,
+ const BorrowedHandle& socket_fd, msghdr* msg);
+
+#define RETRY_EINTR(fnc_call) \
+ ([&]() -> decltype(fnc_call) { \
+ decltype(fnc_call) result; \
+ do { \
+ result = (fnc_call); \
+ } while (result == -1 && errno == EINTR); \
+ return result; \
+ })()
+
+class SendPayload : public MessageWriter, public OutputResourceMapper {
+ public:
+ SendPayload(SendInterface* sender = nullptr) : sender_{sender} {}
+ Status<void> Send(const BorrowedHandle& socket_fd);
+ Status<void> Send(const BorrowedHandle& socket_fd, const ucred* cred);
+
+ // MessageWriter
+ void* GetNextWriteBufferSection(size_t size) override;
+ OutputResourceMapper* GetOutputResourceMapper() override;
+
+ // OutputResourceMapper
+ Status<FileReference> PushFileHandle(const LocalHandle& handle) override;
+ Status<FileReference> PushFileHandle(const BorrowedHandle& handle) override;
+ Status<FileReference> PushFileHandle(const RemoteHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const LocalChannelHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const BorrowedChannelHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ const RemoteChannelHandle& handle) override;
+
+ private:
+ SendInterface* sender_;
+ ByteBuffer buffer_;
+ std::vector<int> file_handles_;
+};
+
+class ReceivePayload : public MessageReader, public InputResourceMapper {
+ public:
+ ReceivePayload(RecvInterface* receiver = nullptr) : receiver_{receiver} {}
+ Status<void> Receive(const BorrowedHandle& socket_fd);
+ Status<void> Receive(const BorrowedHandle& socket_fd, ucred* cred);
+
+ // MessageReader
+ BufferSection GetNextReadBufferSection() override;
+ void ConsumeReadBufferSectionData(const void* new_start) override;
+ InputResourceMapper* GetInputResourceMapper() override;
+
+ // InputResourceMapper
+ bool GetFileHandle(FileReference ref, LocalHandle* handle) override;
+ bool GetChannelHandle(ChannelReference ref,
+ LocalChannelHandle* handle) override;
+
+ private:
+ RecvInterface* receiver_;
+ ByteBuffer buffer_;
+ std::vector<LocalHandle> file_handles_;
+ size_t read_pos_{0};
+};
+
+template <typename FileHandleType>
+class ChannelInfo {
+ public:
+ FileHandleType data_fd;
+ FileHandleType event_fd;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(ChannelInfo, data_fd, event_fd);
+};
+
+template <typename FileHandleType>
+class ChannelConnectionInfo {
+ public:
+ FileHandleType channel_fd;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(ChannelConnectionInfo, channel_fd);
+};
+
+template <typename FileHandleType>
+class RequestHeader {
+ public:
+ int32_t op{0};
+ ucred cred;
+ uint32_t send_len{0};
+ uint32_t max_recv_len{0};
+ std::vector<FileHandleType> file_descriptors;
+ std::vector<ChannelInfo<FileHandleType>> channels;
+ std::array<uint8_t, 32> impulse_payload;
+ bool is_impulse{false};
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(RequestHeader, op, send_len, max_recv_len,
+ file_descriptors, channels, impulse_payload,
+ is_impulse);
+};
+
+template <typename FileHandleType>
+class ResponseHeader {
+ public:
+ int32_t ret_code{0};
+ uint32_t recv_len{0};
+ std::vector<FileHandleType> file_descriptors;
+ std::vector<ChannelInfo<FileHandleType>> channels;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(ResponseHeader, ret_code, recv_len, file_descriptors,
+ channels);
+};
+
+template <typename T>
+inline Status<void> SendData(const BorrowedHandle& socket_fd, const T& data) {
+ SendPayload payload;
+ rpc::Serialize(data, &payload);
+ return payload.Send(socket_fd);
+}
+
+template <typename FileHandleType>
+inline Status<void> SendData(const BorrowedHandle& socket_fd,
+ const RequestHeader<FileHandleType>& request) {
+ SendPayload payload;
+ rpc::Serialize(request, &payload);
+ return payload.Send(socket_fd, &request.cred);
+}
+
+Status<void> SendData(const BorrowedHandle& socket_fd, const void* data,
+ size_t size);
+Status<void> SendDataVector(const BorrowedHandle& socket_fd, const iovec* data,
+ size_t count);
+
+template <typename T>
+inline Status<void> ReceiveData(const BorrowedHandle& socket_fd, T* data) {
+ ReceivePayload payload;
+ Status<void> status = payload.Receive(socket_fd);
+ if (status && rpc::Deserialize(data, &payload) != rpc::ErrorCode::NO_ERROR)
+ status.SetError(EIO);
+ return status;
+}
+
+template <typename FileHandleType>
+inline Status<void> ReceiveData(const BorrowedHandle& socket_fd,
+ RequestHeader<FileHandleType>* request) {
+ ReceivePayload payload;
+ Status<void> status = payload.Receive(socket_fd, &request->cred);
+ if (status && rpc::Deserialize(request, &payload) != rpc::ErrorCode::NO_ERROR)
+ status.SetError(EIO);
+ return status;
+}
+
+Status<void> ReceiveData(const BorrowedHandle& socket_fd, void* data,
+ size_t size);
+Status<void> ReceiveDataVector(const BorrowedHandle& socket_fd,
+ const iovec* data, size_t count);
+
+size_t CountVectorSize(const iovec* data, size_t count);
+void InitRequest(android::pdx::uds::RequestHeader<BorrowedHandle>* request,
+ int opcode, uint32_t send_len, uint32_t max_recv_len,
+ bool is_impulse);
+
+Status<void> WaitForEndpoint(const std::string& endpoint_path,
+ int64_t timeout_ms);
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_IPC_HELPER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/service_dispatcher.h b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h
new file mode 100644
index 0000000000..23af4f4403
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/service_dispatcher.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
+#define ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
+
+#include <list>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include <pdx/file_handle.h>
+#include <pdx/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ServiceDispatcher : public pdx::ServiceDispatcher {
+ public:
+ // Get a new instance of ServiceDispatcher, or return nullptr if init failed.
+ static std::unique_ptr<pdx::ServiceDispatcher> Create();
+
+ ~ServiceDispatcher() override;
+ int AddService(const std::shared_ptr<Service>& service) override;
+ int RemoveService(const std::shared_ptr<Service>& service) override;
+ int ReceiveAndDispatch() override;
+ int ReceiveAndDispatch(int timeout) override;
+ int EnterDispatchLoop() override;
+ void SetCanceled(bool cancel) override;
+ bool IsCanceled() const override;
+
+ private:
+ ServiceDispatcher();
+
+ // Internal thread accounting.
+ int ThreadEnter();
+ void ThreadExit();
+
+ std::mutex mutex_;
+ std::condition_variable condition_;
+ std::atomic<bool> canceled_{false};
+
+ std::list<std::shared_ptr<Service>> services_;
+
+ int thread_count_ = 0;
+ LocalHandle event_fd_;
+ LocalHandle epoll_fd_;
+
+ ServiceDispatcher(const ServiceDispatcher&) = delete;
+ void operator=(const ServiceDispatcher&) = delete;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_SERVICE_DISPATCHER_H_
diff --git a/libs/vr/libpdx_uds/private/uds/service_endpoint.h b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
new file mode 100644
index 0000000000..368891ce05
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
@@ -0,0 +1,167 @@
+#ifndef ANDROID_PDX_UDS_SERVICE_ENDPOINT_H_
+#define ANDROID_PDX_UDS_SERVICE_ENDPOINT_H_
+
+#include <sys/stat.h>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/service.h>
+#include <pdx/service_endpoint.h>
+#include <uds/channel_event_set.h>
+#include <uds/service_dispatcher.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class Endpoint : public pdx::Endpoint {
+ public:
+ enum {
+ kIpcTag = 0x00736674, // 'uds'
+ };
+
+ // Blocking modes for service endpoint. Controls whether the epoll set is in
+ // blocking mode or not for message receive.
+ enum {
+ kBlocking = true,
+ kNonBlocking = false,
+ kDefaultBlocking = kNonBlocking,
+ };
+
+ enum : mode_t {
+ kDefaultMode = 0,
+ };
+
+ ~Endpoint() override = default;
+
+ uint32_t GetIpcTag() const override { return kIpcTag; }
+ Status<void> SetService(Service* service) override;
+ Status<void> SetChannel(int channel_id, Channel* channel) override;
+ Status<void> CloseChannel(int channel_id) override;
+ Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) override;
+ Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+ Channel* channel,
+ int* channel_id) override;
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ Channel** channel) override;
+ Status<void> MessageReceive(Message* message) override;
+ Status<void> MessageReply(Message* message, int return_code) override;
+ Status<void> MessageReplyFd(Message* message, unsigned int push_fd) override;
+ Status<void> MessageReplyChannelHandle(
+ Message* message, const LocalChannelHandle& handle) override;
+ Status<void> MessageReplyChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) override;
+ Status<void> MessageReplyChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) override;
+ Status<size_t> ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) override;
+ Status<size_t> WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) override;
+ Status<FileReference> PushFileHandle(Message* message,
+ const LocalHandle& handle) override;
+ Status<FileReference> PushFileHandle(Message* message,
+ const BorrowedHandle& handle) override;
+ Status<FileReference> PushFileHandle(Message* message,
+ const RemoteHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ Message* message, const LocalChannelHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) override;
+ Status<ChannelReference> PushChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) override;
+ LocalHandle GetFileHandle(Message* message, FileReference ref) const override;
+ LocalChannelHandle GetChannelHandle(Message* message,
+ ChannelReference ref) const override;
+
+ void* AllocateMessageState() override;
+ void FreeMessageState(void* state) override;
+
+ Status<void> Cancel() override;
+
+ // Open an endpoint at the given path.
+ // Second parameter is unused for UDS, but we have it here for compatibility
+ // in signature with servicefs::Endpoint::Create().
+ // This method uses |endpoint_path| as a relative path to endpoint socket
+ // created by init process.
+ static std::unique_ptr<Endpoint> Create(const std::string& endpoint_path,
+ mode_t /*unused_mode*/ = kDefaultMode,
+ bool blocking = kDefaultBlocking);
+
+ // Helper method to create an endpoint at the given UDS socket path. This
+ // method physically creates and binds a socket at that path.
+ static std::unique_ptr<Endpoint> CreateAndBindSocket(
+ const std::string& endpoint_path, bool blocking = kDefaultBlocking);
+
+ // Helper method to create an endpoint from an existing socket FD.
+ // Mostly helpful for tests.
+ static std::unique_ptr<Endpoint> CreateFromSocketFd(LocalHandle socket_fd);
+
+ // Test helper method to register a new channel identified by |channel_fd|
+ // socket file descriptor.
+ Status<void> RegisterNewChannelForTests(LocalHandle channel_fd);
+
+ int epoll_fd() const { return epoll_fd_.Get(); }
+
+ private:
+ struct ChannelData {
+ LocalHandle data_fd;
+ ChannelEventSet event_set;
+ Channel* channel_state{nullptr};
+ };
+
+ // This class must be instantiated using Create() static methods above.
+ Endpoint(const std::string& endpoint_path, bool blocking,
+ bool use_init_socket_fd = true);
+ Endpoint(LocalHandle socket_fd);
+
+ void Init(LocalHandle socket_fd);
+
+ Endpoint(const Endpoint&) = delete;
+ void operator=(const Endpoint&) = delete;
+
+ uint32_t GetNextAvailableMessageId() {
+ return next_message_id_.fetch_add(1, std::memory_order_relaxed);
+ }
+
+ void BuildCloseMessage(int32_t channel_id, Message* message);
+
+ Status<void> AcceptConnection(Message* message);
+ Status<void> ReceiveMessageForChannel(const BorrowedHandle& channel_fd,
+ Message* message);
+ Status<void> OnNewChannel(LocalHandle channel_fd);
+ Status<std::pair<int32_t, ChannelData*>> OnNewChannelLocked(
+ LocalHandle channel_fd, Channel* channel_state);
+ Status<void> CloseChannelLocked(int32_t channel_id);
+ Status<void> ReenableEpollEvent(const BorrowedHandle& channel_fd);
+ Channel* GetChannelState(int32_t channel_id);
+ BorrowedHandle GetChannelSocketFd(int32_t channel_id);
+ BorrowedHandle GetChannelEventFd(int32_t channel_id);
+ int32_t GetChannelId(const BorrowedHandle& channel_fd);
+ Status<void> CreateChannelSocketPair(LocalHandle* local_socket,
+ LocalHandle* remote_socket);
+
+ std::string endpoint_path_;
+ bool is_blocking_;
+ LocalHandle socket_fd_;
+ LocalHandle cancel_event_fd_;
+ LocalHandle epoll_fd_;
+
+ mutable std::mutex channel_mutex_;
+ std::map<int32_t, ChannelData> channels_;
+ std::map<int, int32_t> channel_fd_to_id_;
+ int32_t last_channel_id_{0};
+
+ Service* service_{nullptr};
+ std::atomic<uint32_t> next_message_id_;
+};
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
+
+#endif // ANDROID_PDX_UDS_PDX_SERVICE_ENDPOINT_H_
diff --git a/libs/vr/libpdx_uds/remote_method_tests.cpp b/libs/vr/libpdx_uds/remote_method_tests.cpp
new file mode 100644
index 0000000000..3109753dc2
--- /dev/null
+++ b/libs/vr/libpdx_uds/remote_method_tests.cpp
@@ -0,0 +1,951 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <array>
+#include <cstdint>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/service.h>
+#include <uds/client_channel.h>
+#include <uds/client_channel_factory.h>
+#include <uds/service_dispatcher.h>
+#include <uds/service_endpoint.h>
+
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::ClientBase;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::RemoteHandle;
+using android::pdx::ServiceBase;
+using android::pdx::ServiceDispatcher;
+using android::pdx::Status;
+using android::pdx::uds::Endpoint;
+using namespace android::pdx::rpc;
+
+namespace {
+
+std::string Rot13(const std::string& s) {
+ std::string text = s;
+ std::transform(std::begin(text), std::end(text), std::begin(text),
+ [](char c) -> char {
+ if (!std::isalpha(c)) {
+ return c;
+ } else {
+ const char pivot = std::isupper(c) ? 'A' : 'a';
+ return (c - pivot + 13) % 26 + pivot;
+ }
+ });
+ return text;
+}
+
+// Defines a serializable user type that may be transferred between client and
+// service.
+struct TestType {
+ int a;
+ float b;
+ std::string c;
+
+ TestType() {}
+ TestType(int a, float b, const std::string& c) : a(a), b(b), c(c) {}
+
+ // Make gtest expressions simpler by defining equality operator. This is not
+ // needed for serialization.
+ bool operator==(const TestType& other) const {
+ return a == other.a && b == other.b && c == other.c;
+ }
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c);
+};
+
+struct DerivedTestType : public TestType {
+ DerivedTestType() : TestType() {}
+ DerivedTestType(int a, float b) : TestType(a, b, "constant") {}
+};
+
+// Defines a serializable user type with a LocalHandle member.
+struct TestFdType {
+ int a;
+ LocalHandle fd;
+
+ TestFdType() {}
+ TestFdType(int a, LocalHandle fd) : a(a), fd(std::move(fd)) {}
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestFdType, a, fd);
+};
+
+// Defines a serializable user template type with a FileHandle member.
+template <typename FileHandleType>
+struct TestTemplateType {
+ FileHandleType fd;
+
+ TestTemplateType() {}
+ TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {}
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd);
+};
+
+struct BasicStruct {
+ int a;
+ int b;
+ std::string c;
+
+ private:
+ PDX_SERIALIZABLE_MEMBERS(BasicStruct, a, b, c);
+};
+
+using BasicStructTraits = SerializableTraits<BasicStruct>;
+
+struct NonSerializableType {
+ int a;
+ int b;
+ std::string c;
+};
+
+struct IncorrectlyDefinedSerializableType {
+ int a;
+ int b;
+
+ private:
+ using SerializableMembers = std::tuple<int, int>;
+};
+
+// Defines the contract between the client and service, including ServiceFS
+// endpoint path, method opcodes, and remote method signatures.
+struct TestInterface final {
+ // Service path.
+ static constexpr char kClientPath[] = "socket_test";
+
+ // Op codes.
+ enum {
+ kOpAdd = 0,
+ kOpFoo,
+ kOpConcatenate,
+ kOpWriteBuffer,
+ kOpStringLength,
+ kOpSendTestType,
+ kOpSendBasicStruct,
+ kOpSendVector,
+ kOpRot13,
+ kOpNoArgs,
+ kOpSendFile,
+ kOpGetFile,
+ kOpGetTestFdType,
+ kOpOpenFiles,
+ kOpReadFile,
+ kOpPushChannel,
+ kOpPositive,
+ };
+
+ // Methods.
+ PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int));
+ PDX_REMOTE_METHOD(Foo, kOpFoo, int(int, const std::string&));
+ PDX_REMOTE_METHOD(Concatenate, kOpConcatenate,
+ std::string(const std::string&, const std::string&));
+ PDX_REMOTE_METHOD(SumVector, kOpWriteBuffer, int(const std::vector<int>&));
+ PDX_REMOTE_METHOD(StringLength, kOpStringLength, int(const std::string&));
+ PDX_REMOTE_METHOD(SendTestType, kOpSendTestType, TestType(const TestType&));
+ PDX_REMOTE_METHOD(SendBasicStruct, kOpSendBasicStruct,
+ BasicStruct(const BasicStruct&));
+ PDX_REMOTE_METHOD(SendVector, kOpSendVector,
+ std::string(const std::vector<TestType>&));
+ PDX_REMOTE_METHOD(Rot13, kOpRot13, std::string(const std::string&));
+ PDX_REMOTE_METHOD(NoArgs, kOpNoArgs, int(Void));
+ PDX_REMOTE_METHOD(SendFile, kOpSendFile, int(const LocalHandle& fd));
+ PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int));
+ PDX_REMOTE_METHOD(GetTestFdType, kOpGetTestFdType,
+ TestFdType(int, const std::string&, int));
+ PDX_REMOTE_METHOD(OpenFiles, kOpOpenFiles,
+ std::vector<LocalHandle>(
+ const std::vector<std::pair<std::string, int>>&));
+ PDX_REMOTE_METHOD(ReadFile, kOpReadFile,
+ std::pair<int, BufferWrapper<std::uint8_t*>>(
+ const std::string&, int, std::size_t));
+ PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void));
+ PDX_REMOTE_METHOD(Positive, kOpPositive, void(int));
+
+ PDX_REMOTE_API(API, Add, Foo, Concatenate, SumVector, StringLength,
+ SendTestType, SendVector, Rot13, NoArgs, SendFile, GetFile,
+ GetTestFdType, OpenFiles, PushChannel, Positive);
+};
+
+constexpr char TestInterface::kClientPath[];
+
+// Test client to send messages to the test service.
+class TestClient : public ClientBase<TestClient> {
+ public:
+ int Add(int a, int b) {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Add>(a, b));
+ }
+
+ int Foo(int a, const std::string& b) {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Foo>(a, b));
+ }
+
+ std::string Concatenate(const std::string& a, const std::string& b) {
+ std::string return_value;
+
+ Status<std::string> status =
+ InvokeRemoteMethod<TestInterface::Concatenate>(a, b);
+ if (!status)
+ return std::string("[Error]");
+ else
+ return status.take();
+ }
+
+ int SumVector(const int* buffer, std::size_t size) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<TestInterface::SumVector>(WrapArray(buffer, size)));
+ }
+
+ int SumVector(const std::vector<int>& buffer) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<TestInterface::SumVector>(buffer));
+ }
+
+ int StringLength(const char* string, std::size_t size) {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::StringLength>(
+ WrapString(string, size)));
+ }
+
+ int StringLength(const std::string& string) {
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<TestInterface::StringLength>(string));
+ }
+
+ TestType SendTestType(const TestType& tt) {
+ Status<TestType> status =
+ InvokeRemoteMethod<TestInterface::SendTestType>(tt);
+ if (!status)
+ return TestType(0, 0.0, "[Error]");
+ else
+ return status.take();
+ }
+
+ BasicStruct SendBasicStruct(const BasicStruct& bs) {
+ Status<BasicStruct> status =
+ InvokeRemoteMethod<TestInterface::SendBasicStruct>(bs);
+ if (!status)
+ return BasicStruct{0, 0, "[Error]"};
+ else
+ return status.take();
+ }
+
+ std::string SendVector(const std::vector<TestType>& v) {
+ Status<std::string> status =
+ InvokeRemoteMethod<TestInterface::SendVector>(v);
+ if (!status)
+ return "[Error]";
+ else
+ return status.take();
+ }
+
+ std::string Rot13(const std::string& string) {
+ Status<std::string> status =
+ InvokeRemoteMethod<TestInterface::Rot13>(string);
+ return status ? status.get() : string;
+ }
+
+ int NoArgs() {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::NoArgs>());
+ }
+
+ int SendFile(const LocalHandle& fd) {
+ return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::SendFile>(fd));
+ }
+
+ LocalHandle GetFile(const std::string& path, int mode) {
+ Status<LocalHandle> status =
+ InvokeRemoteMethod<TestInterface::GetFile>(path, mode);
+ if (!status)
+ return LocalHandle(-status.error());
+ else
+ return status.take();
+ }
+
+ int GetFile(const std::string& path, int mode, LocalHandle* fd_out) {
+ Status<void> status =
+ InvokeRemoteMethodInPlace<TestInterface::GetFile>(fd_out, path, mode);
+ return status ? 0 : -status.error();
+ }
+
+ TestFdType GetTestFdType(int a, const std::string& path, int mode) {
+ Status<TestFdType> status =
+ InvokeRemoteMethod<TestInterface::GetTestFdType>(a, path, mode);
+ if (!status)
+ return {};
+ else
+ return status.take();
+ }
+
+ std::vector<LocalHandle> OpenFiles(
+ const std::vector<std::pair<std::string, int>>& file_specs) {
+ Status<std::vector<LocalHandle>> status =
+ InvokeRemoteMethod<TestInterface::OpenFiles>(file_specs);
+ if (!status)
+ return {};
+ else
+ return status.take();
+ }
+
+ int ReadFile(void* buffer, std::size_t size, const std::string& path,
+ int mode) {
+ auto buffer_wrapper = WrapBuffer(buffer, size);
+ auto return_value = std::make_pair(-1, buffer_wrapper);
+
+ Status<void> status = InvokeRemoteMethodInPlace<TestInterface::ReadFile>(
+ &return_value, path, mode, size);
+ return status ? return_value.first : -status.error();
+ }
+
+ int PushChannel(LocalChannelHandle* fd_out) {
+ auto status = InvokeRemoteMethodInPlace<TestInterface::PushChannel>(fd_out);
+ return status ? 0 : -status.error();
+ }
+
+ bool Positive(int test_value) {
+ auto status = InvokeRemoteMethod<TestInterface::Positive>(test_value);
+ return status.ok();
+ }
+
+ int GetFd() const { return event_fd(); }
+
+ private:
+ friend BASE;
+
+ TestClient(LocalChannelHandle channel_handle)
+ : BASE{android::pdx::uds::ClientChannel::Create(
+ std::move(channel_handle))} {}
+ TestClient()
+ : BASE{android::pdx::uds::ClientChannelFactory::Create(
+ TestInterface::kClientPath)} {}
+
+ TestClient(const TestClient&) = delete;
+ void operator=(const TestClient&) = delete;
+};
+
+// Test service that encodes/decodes messages from clients.
+class TestService : public ServiceBase<TestService> {
+ public:
+ Status<void> HandleMessage(Message& message) override {
+ switch (message.GetOp()) {
+ case TestInterface::Add::Opcode:
+ DispatchRemoteMethod<TestInterface::Add>(*this, &TestService::OnAdd,
+ message);
+ return {};
+
+ case TestInterface::Foo::Opcode:
+ DispatchRemoteMethod<TestInterface::Foo>(*this, &TestService::OnFoo,
+ message);
+ return {};
+
+ case TestInterface::Concatenate::Opcode:
+ DispatchRemoteMethod<TestInterface::Concatenate>(
+ *this, &TestService::OnConcatenate, message);
+ return {};
+
+ case TestInterface::SumVector::Opcode:
+ DispatchRemoteMethod<TestInterface::SumVector>(
+ *this, &TestService::OnSumVector, message);
+ return {};
+
+ case TestInterface::StringLength::Opcode:
+ DispatchRemoteMethod<TestInterface::StringLength>(
+ *this, &TestService::OnStringLength, message);
+ return {};
+
+ case TestInterface::SendTestType::Opcode:
+ DispatchRemoteMethod<TestInterface::SendTestType>(
+ *this, &TestService::OnSendTestType, message);
+ return {};
+
+ case TestInterface::SendVector::Opcode:
+ DispatchRemoteMethod<TestInterface::SendVector>(
+ *this, &TestService::OnSendVector, message);
+ return {};
+
+ case TestInterface::Rot13::Opcode:
+ DispatchRemoteMethod<TestInterface::Rot13>(*this, &TestService::OnRot13,
+ message);
+ return {};
+
+ case TestInterface::NoArgs::Opcode:
+ DispatchRemoteMethod<TestInterface::NoArgs>(
+ *this, &TestService::OnNoArgs, message);
+ return {};
+
+ case TestInterface::SendFile::Opcode:
+ DispatchRemoteMethod<TestInterface::SendFile>(
+ *this, &TestService::OnSendFile, message);
+ return {};
+
+ case TestInterface::GetFile::Opcode:
+ DispatchRemoteMethod<TestInterface::GetFile>(
+ *this, &TestService::OnGetFile, message);
+ return {};
+
+ case TestInterface::GetTestFdType::Opcode:
+ DispatchRemoteMethod<TestInterface::GetTestFdType>(
+ *this, &TestService::OnGetTestFdType, message);
+ return {};
+
+ case TestInterface::OpenFiles::Opcode:
+ DispatchRemoteMethod<TestInterface::OpenFiles>(
+ *this, &TestService::OnOpenFiles, message);
+ return {};
+
+ case TestInterface::ReadFile::Opcode:
+ DispatchRemoteMethod<TestInterface::ReadFile>(
+ *this, &TestService::OnReadFile, message);
+ return {};
+
+ case TestInterface::PushChannel::Opcode:
+ DispatchRemoteMethod<TestInterface::PushChannel>(
+ *this, &TestService::OnPushChannel, message);
+ return {};
+
+ case TestInterface::Positive::Opcode:
+ DispatchRemoteMethod<TestInterface::Positive>(
+ *this, &TestService::OnPositive, message);
+ return {};
+
+ default:
+ return Service::DefaultHandleMessage(message);
+ }
+ }
+
+ private:
+ friend BASE;
+
+ TestService()
+ : BASE("TestService",
+ Endpoint::CreateAndBindSocket(TestInterface::kClientPath)) {}
+
+ int OnAdd(Message&, int a, int b) { return a + b; }
+
+ int OnFoo(Message&, int a, const std::string& b) { return a + b.length(); }
+
+ std::string OnConcatenate(Message&, const std::string& a,
+ const std::string& b) {
+ return a + b;
+ }
+
+ int OnSumVector(Message&, const std::vector<int>& vector) {
+ return std::accumulate(vector.begin(), vector.end(), 0);
+ }
+
+ int OnStringLength(Message&, const std::string& string) {
+ return string.length();
+ }
+
+ TestType OnSendTestType(Message&, const TestType& tt) {
+ return TestType(tt.a + 20, tt.b - 2.0, tt.c + "foo");
+ }
+
+ std::string OnSendVector(Message&, const std::vector<TestType>& v) {
+ std::string return_value = "";
+
+ for (const auto& tt : v)
+ return_value += tt.c;
+
+ return return_value;
+ }
+
+ Status<std::string> OnRot13(Message&, const std::string& s) {
+ return {Rot13(s)};
+ }
+
+ int OnNoArgs(Message&) { return 1; }
+
+ int OnSendFile(Message&, const LocalHandle& fd) { return fd.Get(); }
+
+ LocalHandle OnGetFile(Message& message, const std::string& path, int mode) {
+ LocalHandle fd(path.c_str(), mode);
+ if (!fd)
+ message.ReplyError(errno);
+ return fd;
+ }
+
+ TestFdType OnGetTestFdType(Message& message, int a, const std::string& path,
+ int mode) {
+ TestFdType return_value(a, LocalHandle(path, mode));
+ if (!return_value.fd)
+ message.ReplyError(errno);
+ return return_value;
+ }
+
+ std::vector<LocalHandle> OnOpenFiles(
+ Message&, const std::vector<std::pair<std::string, int>>& file_specs) {
+ std::vector<LocalHandle> return_value;
+ for (auto& spec : file_specs) {
+ LocalHandle fd(spec.first, spec.second);
+ if (fd)
+ return_value.emplace_back(std::move(fd));
+ else
+ return_value.emplace_back(-errno);
+ }
+ return return_value;
+ }
+
+ std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> OnReadFile(
+ Message& message, const std::string& path, int mode, std::size_t length) {
+ std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> return_value;
+ LocalHandle fd(path, mode);
+ if (!fd) {
+ message.ReplyError(errno);
+ return return_value;
+ }
+
+ return_value.second.reserve(length);
+ const int ret = read(fd.Get(), return_value.second.data(), length);
+ if (ret < 0) {
+ message.ReplyError(errno);
+ return return_value;
+ }
+
+ return_value.second.resize(ret);
+ return_value.first = ret;
+ return return_value;
+ }
+
+ RemoteChannelHandle OnPushChannel(Message& message) {
+ auto status = message.PushChannel(0, nullptr, nullptr);
+ if (!status) {
+ message.ReplyError(status.error());
+ return {};
+ }
+ return status.take();
+ }
+
+ Status<void> OnPositive(Message& /*message*/, int test_value) {
+ if (test_value >= 0)
+ return {};
+ else
+ return ErrorStatus(EINVAL);
+ }
+
+ TestService(const TestService&) = delete;
+ void operator=(const TestService&) = delete;
+};
+
+} // anonymous namespace
+
+// Use a test fixture to ensure proper order of cleanup between clients,
+// services, and the dispatcher. As these objects are cleaned up in the same
+// thread, either the service or client must be destroyed before stopping the
+// dispatcher. The reason for this is that clients send blocking "close"
+// messages to their respective services on destruction. If this happens after
+// stopping the dispatcher the client destructor will get blocked waiting for a
+// reply that will never come. In normal use of the service framework this is
+// never an issue because clients and the dispatcher for the same service are
+// never destructed in the same thread (they live in different processes).
+class RemoteMethodTest : public ::testing::Test {
+ protected:
+ std::unique_ptr<ServiceDispatcher> dispatcher_;
+ std::thread dispatch_thread_;
+
+ void SetUp() override {
+ // Create a dispatcher to handle messages to services.
+ dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+ ASSERT_NE(nullptr, dispatcher_);
+
+ // Start the message dispatch loop in a separate thread.
+ dispatch_thread_ = std::thread(
+ std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
+ }
+
+ void TearDown() override {
+ if (dispatcher_) {
+ // Cancel the dispatcher and wait for the thread to terminate.
+ // Explicitly
+ // join the thread so that destruction doesn't deallocate the
+ // dispatcher
+ // before the thread finishes.
+ dispatcher_->SetCanceled(true);
+ dispatch_thread_.join();
+ }
+ }
+};
+
+// Test basic operation of TestService/TestClient classes.
+TEST_F(RemoteMethodTest, BasicClientService) {
+ // Create a test service and add it to the dispatcher.
+
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ const int sum = client->Add(10, 25);
+ EXPECT_GE(35, sum);
+
+ const auto cat = client->Concatenate("This is a string", ", that it is.");
+ EXPECT_EQ("This is a string, that it is.", cat);
+
+ std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
+ const auto rot13_alphabet = client->Rot13(alphabet);
+ EXPECT_EQ(Rot13(alphabet), rot13_alphabet);
+
+ const auto length = client->Foo(10, "123");
+ EXPECT_EQ(13, length);
+
+ const std::vector<int> vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ const int vector_sum = client->SumVector(vector.data(), vector.size());
+ const int vector_sum2 = client->SumVector(vector);
+ EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum);
+ EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum2);
+
+ const auto string_length1 = client->StringLength("This is a string");
+ EXPECT_EQ(16, string_length1);
+
+ const auto string_length2 = client->StringLength("1234567890");
+ EXPECT_EQ(10, string_length2);
+
+ std::string string = "1234567890";
+ const auto string_length3 =
+ client->StringLength(string.c_str(), string.length());
+ EXPECT_EQ(10, string_length3);
+
+ TestType tt{10, 0.0, "string"};
+ const auto tt_result = client->SendTestType(tt);
+ EXPECT_EQ(TestType(30, -2.0, "stringfoo"), tt_result);
+
+ std::vector<TestType> ttv = {TestType(0, 0.0, "abc"),
+ TestType(0, 0.0, "123")};
+ const std::string string_result = client->SendVector(ttv);
+ EXPECT_EQ("abc123", string_result);
+
+ const int int_result = client->NoArgs();
+ EXPECT_EQ(1, int_result);
+}
+
+TEST_F(RemoteMethodTest, LocalHandle) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ LocalHandle fd("/dev/zero", O_RDONLY);
+ ASSERT_TRUE(fd.IsValid());
+
+ int fd_result = client->SendFile(fd);
+ EXPECT_LE(0, fd_result);
+ EXPECT_NE(fd.Get(), fd_result);
+ fd = LocalHandle(-3);
+ fd_result = client->SendFile(fd);
+ EXPECT_EQ(fd.Get(), fd_result);
+
+ fd = client->GetFile("/dev/zero", O_RDONLY);
+ ASSERT_TRUE(fd.IsValid()) << "Error code: " << fd.Get();
+
+ std::array<uint8_t, 10> buffer;
+ buffer.fill(1);
+ EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size()));
+ EXPECT_EQ(buffer, decltype(buffer){{0}});
+ fd.Close();
+
+ const int error = client->GetFile("/dev/zero", O_RDONLY, &fd);
+ EXPECT_EQ(0, error);
+ EXPECT_TRUE(fd.IsValid());
+
+ buffer.fill(1);
+ EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size()));
+ EXPECT_EQ(buffer, decltype(buffer){{0}});
+
+ /*
+ Seg fault.
+ fd = client->GetFile("/dev/foobar", O_RDONLY);
+ EXPECT_FALSE(fd.IsValid());
+ */
+}
+
+TEST_F(RemoteMethodTest, PushChannel) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ // Get a new channel as an fd.
+ LocalChannelHandle channel;
+ const int ret = client->PushChannel(&channel);
+ EXPECT_EQ(0, ret);
+ EXPECT_TRUE(channel.valid());
+
+ // Create a new client from the channel.
+ auto client2 = TestClient::Create(std::move(channel));
+ ASSERT_NE(nullptr, client2);
+
+ // Test that the new channel works.
+ const int sum = client2->Add(10, 25);
+ EXPECT_GE(35, sum);
+}
+
+TEST_F(RemoteMethodTest, Positive) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ ASSERT_TRUE(client->Positive(0));
+ ASSERT_TRUE(client->Positive(1));
+ ASSERT_FALSE(client->Positive(-1));
+}
+
+TEST_F(RemoteMethodTest, AggregateLocalHandle) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ TestFdType result = client->GetTestFdType(10, "/dev/zero", O_RDONLY);
+ EXPECT_TRUE(result.fd.IsValid());
+ EXPECT_EQ(10, result.a);
+
+ std::vector<LocalHandle> files =
+ client->OpenFiles({{{"/dev/zero", O_RDONLY},
+ {"/dev/null", O_WRONLY},
+ {"/dev/zero", O_RDONLY}}});
+ ASSERT_EQ(3u, files.size());
+ EXPECT_TRUE(files[0].IsValid());
+ EXPECT_TRUE(files[1].IsValid());
+ EXPECT_TRUE(files[2].IsValid());
+}
+
+TEST_F(RemoteMethodTest, BufferWrapper) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create();
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create();
+ ASSERT_NE(nullptr, client);
+
+ const int buffer_size = 20;
+ std::vector<std::uint8_t> buffer(buffer_size, 'x');
+ std::vector<std::uint8_t> expected(buffer_size, 0);
+ int ret =
+ client->ReadFile(buffer.data(), buffer.size(), "/dev/zero", O_RDONLY);
+ EXPECT_EQ(buffer_size, ret);
+ EXPECT_EQ(expected, buffer);
+}
+
+//
+// RemoteMethodFramework: Tests the type-based framework that remote method
+// support is built upon.
+//
+
+// Test logical And template.
+TEST(RemoteMethodFramework, And) {
+ EXPECT_TRUE((And<std::true_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::true_type, std::false_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::false_type>::value));
+
+ EXPECT_TRUE((And<std::true_type, std::true_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::true_type, std::true_type, std::false_type>::value));
+ EXPECT_FALSE((And<std::true_type, std::false_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::true_type, std::false_type, std::false_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::true_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::true_type, std::false_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::false_type, std::true_type>::value));
+ EXPECT_FALSE((And<std::false_type, std::false_type, std::false_type>::value));
+}
+
+// Test convertible type constraints.
+TEST(RemoteMethodFramework, IsConvertible) {
+ // std::pair.
+ EXPECT_TRUE(
+ (IsConvertible<std::pair<int, float>, std::pair<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<int, float>, std::pair<float, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<int, float>, std::pair<float, int>>::value));
+
+ // Nested std::pair.
+ EXPECT_TRUE((IsConvertible<std::pair<std::pair<int, float>, float>,
+ std::pair<std::pair<int, float>, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::pair<std::pair<int, float>, float>,
+ std::pair<std::pair<float, int>, float>>::value));
+
+ // std::tuple and std::pair.
+ EXPECT_TRUE(
+ (IsConvertible<std::pair<int, float>, std::tuple<int, float>>::value));
+ EXPECT_TRUE(
+ (IsConvertible<std::tuple<int, float>, std::pair<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<float, float>, std::tuple<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::tuple<float, float>, std::pair<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<int, int>, std::tuple<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::tuple<int, int>, std::pair<int, float>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::pair<int, int>, std::tuple<int, int, int>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<std::tuple<int, int, int>, std::pair<int, int>>::value));
+
+ // std::vector.
+ EXPECT_TRUE((IsConvertible<std::vector<int>, std::vector<int>>::value));
+ EXPECT_FALSE((IsConvertible<std::vector<int>, std::vector<float>>::value));
+
+ // Nested std::vector.
+ EXPECT_TRUE((IsConvertible<std::vector<std::pair<int, int>>,
+ std::vector<std::pair<int, int>>>::value));
+ EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+ std::vector<std::pair<int, float>>>::value));
+ EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+ std::vector<std::pair<float, int>>>::value));
+ EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>,
+ std::vector<std::pair<float, float>>>::value));
+
+ // std::vector with nested convertible types.
+ EXPECT_TRUE((IsConvertible<std::vector<StringWrapper<char>>,
+ std::vector<std::string>>::value));
+
+ // std::map and std::unordered_map.
+ EXPECT_TRUE((IsConvertible<std::map<int, float>,
+ std::unordered_map<int, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::map<float, float>,
+ std::unordered_map<int, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::map<float, float>,
+ std::unordered_map<float, int>>::value));
+ EXPECT_FALSE((IsConvertible<std::map<float, float>,
+ std::unordered_map<int, int>>::value));
+ EXPECT_TRUE((IsConvertible<std::unordered_map<int, float>,
+ std::map<int, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+ std::map<int, float>>::value));
+ EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+ std::map<float, int>>::value));
+ EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>,
+ std::map<int, int>>::value));
+
+ // std::map with nested convertible types.
+ EXPECT_TRUE((IsConvertible<std::map<int, std::string>,
+ std::map<int, StringWrapper<char>>>::value));
+ EXPECT_TRUE(
+ (IsConvertible<std::map<std::tuple<int, int>, std::string>,
+ std::map<std::pair<int, int>, std::string>>::value));
+
+ // std::unordered_map with nested convertible types.
+ EXPECT_TRUE(
+ (IsConvertible<std::unordered_map<int, std::string>,
+ std::unordered_map<int, StringWrapper<char>>>::value));
+ EXPECT_TRUE((IsConvertible<
+ std::unordered_map<std::tuple<int, int>, std::string>,
+ std::unordered_map<std::pair<int, int>, std::string>>::value));
+
+ // std::string.
+ EXPECT_TRUE((IsConvertible<std::string, std::string>::value));
+ EXPECT_FALSE((IsConvertible<std::string, int>::value));
+ EXPECT_FALSE((IsConvertible<int, std::string>::value));
+
+ // Nested std::string.
+ EXPECT_TRUE((IsConvertible<std::pair<std::string, std::string>,
+ std::pair<std::string, std::string>>::value));
+ EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+ std::pair<std::string, int>>::value));
+ EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+ std::pair<int, std::string>>::value));
+ EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>,
+ std::pair<int, int>>::value));
+
+ // StringWrapper.
+ EXPECT_TRUE((IsConvertible<StringWrapper<char>, StringWrapper<char>>::value));
+ EXPECT_TRUE((IsConvertible<StringWrapper<char>, std::string>::value));
+ EXPECT_TRUE((IsConvertible<std::string, StringWrapper<char>>::value));
+ EXPECT_FALSE((IsConvertible<StringWrapper<char>, int>::value));
+ EXPECT_FALSE(
+ (IsConvertible<StringWrapper<char>, BufferWrapper<char*>>::value));
+
+ // BufferWrapper.
+ EXPECT_TRUE(
+ (IsConvertible<BufferWrapper<char*>, BufferWrapper<char*>>::value));
+ EXPECT_TRUE(
+ (IsConvertible<BufferWrapper<char*>, BufferWrapper<const char*>>::value));
+ EXPECT_FALSE(
+ (IsConvertible<BufferWrapper<char*>, BufferWrapper<int*>>::value));
+ EXPECT_TRUE((IsConvertible<BufferWrapper<char*>,
+ BufferWrapper<std::vector<char>>>::value));
+
+ // RemoteHandle and BorrowedHandle.
+ EXPECT_TRUE((IsConvertible<LocalHandle, RemoteHandle>::value));
+ EXPECT_TRUE((IsConvertible<LocalHandle, BorrowedHandle>::value));
+
+ // Test rewriting user defined types.
+ EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>,
+ TestTemplateType<RemoteHandle>>::value));
+ EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>,
+ TestTemplateType<BorrowedHandle>>::value));
+ EXPECT_FALSE((IsConvertible<TestTemplateType<RemoteHandle>,
+ TestTemplateType<LocalHandle>>::value));
+ EXPECT_FALSE((IsConvertible<TestTemplateType<BorrowedHandle>,
+ TestTemplateType<LocalHandle>>::value));
+
+ // TODO(eieio): More thorough testing of convertible types.
+}
+
+TEST(RemoteMethodFramework, SerializableMembers) {
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value);
+
+ EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+ TestTemplateType<LocalHandle>>>::value);
+ EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+ TestTemplateType<RemoteHandle>>>::value);
+ EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers<
+ TestTemplateType<BorrowedHandle>>>::value);
+
+ EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value);
+
+ EXPECT_TRUE(HasSerializableMembers<BasicStruct>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestType>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value);
+ EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value);
+ EXPECT_FALSE(HasSerializableMembers<NonSerializableType>::value);
+ EXPECT_FALSE(
+ HasSerializableMembers<IncorrectlyDefinedSerializableType>::value);
+}
+
+TEST(RemoteMethodFramework, RemoteAPITypes) {
+ EXPECT_EQ(0u, TestInterface::API::MethodIndex<TestInterface::Add>());
+}
diff --git a/libs/vr/libpdx_uds/service_dispatcher.cpp b/libs/vr/libpdx_uds/service_dispatcher.cpp
new file mode 100644
index 0000000000..2c52578d1c
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_dispatcher.cpp
@@ -0,0 +1,202 @@
+#include "uds/service_dispatcher.h"
+
+#include <errno.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include "pdx/service.h"
+#include "uds/service_endpoint.h"
+
+static const int kMaxEventsPerLoop = 128;
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+std::unique_ptr<pdx::ServiceDispatcher> ServiceDispatcher::Create() {
+ std::unique_ptr<ServiceDispatcher> dispatcher{new ServiceDispatcher()};
+ if (!dispatcher->epoll_fd_ || !dispatcher->event_fd_) {
+ dispatcher.reset();
+ }
+
+ return std::move(dispatcher);
+}
+
+ServiceDispatcher::ServiceDispatcher() {
+ event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ if (!event_fd_) {
+ ALOGE("Failed to create event fd because: %s\n", strerror(errno));
+ return;
+ }
+
+ epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
+ if (!epoll_fd_) {
+ ALOGE("Failed to create epoll fd because: %s\n", strerror(errno));
+ return;
+ }
+
+ // Use "this" as a unique pointer to distinguish the event fd from all
+ // the other entries that point to instances of Service.
+ epoll_event event;
+ event.events = EPOLLIN;
+ event.data.ptr = this;
+
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
+ ALOGE("Failed to add event fd to epoll fd because: %s\n", strerror(errno));
+
+ // Close the fds here and signal failure to the factory method.
+ event_fd_.Close();
+ epoll_fd_.Close();
+ }
+}
+
+ServiceDispatcher::~ServiceDispatcher() { SetCanceled(true); }
+
+int ServiceDispatcher::ThreadEnter() {
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ if (canceled_)
+ return -EBUSY;
+
+ thread_count_++;
+ return 0;
+}
+
+void ServiceDispatcher::ThreadExit() {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ thread_count_--;
+ condition_.notify_one();
+}
+
+int ServiceDispatcher::AddService(const std::shared_ptr<Service>& service) {
+ if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag)
+ return -EINVAL;
+
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ auto* endpoint = static_cast<Endpoint*>(service->endpoint());
+ epoll_event event;
+ event.events = EPOLLIN;
+ event.data.ptr = service.get();
+
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, endpoint->epoll_fd(), &event) <
+ 0) {
+ ALOGE("Failed to add service to dispatcher because: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ services_.push_back(service);
+ return 0;
+}
+
+int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) {
+ if (service->endpoint()->GetIpcTag() != Endpoint::kIpcTag)
+ return -EINVAL;
+
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ // It's dangerous to remove a service while other threads may be using it.
+ if (thread_count_ > 0)
+ return -EBUSY;
+
+ epoll_event dummy; // See BUGS in man 2 epoll_ctl.
+
+ auto* endpoint = static_cast<Endpoint*>(service->endpoint());
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, endpoint->epoll_fd(), &dummy) <
+ 0) {
+ ALOGE("Failed to remove service from dispatcher because: %s\n",
+ strerror(errno));
+ return -errno;
+ }
+
+ services_.remove(service);
+ return 0;
+}
+
+int ServiceDispatcher::ReceiveAndDispatch() { return ReceiveAndDispatch(-1); }
+
+int ServiceDispatcher::ReceiveAndDispatch(int timeout) {
+ int ret = ThreadEnter();
+ if (ret < 0)
+ return ret;
+
+ epoll_event events[kMaxEventsPerLoop];
+
+ int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, timeout);
+ if (count <= 0) {
+ ALOGE_IF(count < 0, "Failed to wait for epoll events because: %s\n",
+ strerror(errno));
+ ThreadExit();
+ return count < 0 ? -errno : -ETIMEDOUT;
+ }
+
+ for (int i = 0; i < count; i++) {
+ if (events[i].data.ptr == this) {
+ ThreadExit();
+ return -EBUSY;
+ } else {
+ Service* service = static_cast<Service*>(events[i].data.ptr);
+
+ ALOGI_IF(TRACE, "Dispatching message: fd=%d\n",
+ static_cast<Endpoint*>(service->endpoint())->epoll_fd());
+ service->ReceiveAndDispatch();
+ }
+ }
+
+ ThreadExit();
+ return 0;
+}
+
+int ServiceDispatcher::EnterDispatchLoop() {
+ int ret = ThreadEnter();
+ if (ret < 0)
+ return ret;
+
+ epoll_event events[kMaxEventsPerLoop];
+
+ while (!IsCanceled()) {
+ int count = epoll_wait(epoll_fd_.Get(), events, kMaxEventsPerLoop, -1);
+ if (count < 0 && errno != EINTR) {
+ ALOGE("Failed to wait for epoll events because: %s\n", strerror(errno));
+ ThreadExit();
+ return -errno;
+ }
+
+ for (int i = 0; i < count; i++) {
+ if (events[i].data.ptr == this) {
+ ThreadExit();
+ return -EBUSY;
+ } else {
+ Service* service = static_cast<Service*>(events[i].data.ptr);
+
+ ALOGI_IF(TRACE, "Dispatching message: fd=%d\n",
+ static_cast<Endpoint*>(service->endpoint())->epoll_fd());
+ service->ReceiveAndDispatch();
+ }
+ }
+ }
+
+ ThreadExit();
+ return 0;
+}
+
+void ServiceDispatcher::SetCanceled(bool cancel) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ canceled_ = cancel;
+
+ if (canceled_ && thread_count_ > 0) {
+ eventfd_write(event_fd_.Get(), 1); // Signal threads to quit.
+
+ condition_.wait(lock, [this] { return !(canceled_ && thread_count_ > 0); });
+
+ eventfd_t value;
+ eventfd_read(event_fd_.Get(), &value); // Unsignal.
+ }
+}
+
+bool ServiceDispatcher::IsCanceled() const { return canceled_; }
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp
new file mode 100644
index 0000000000..27a56f9fe0
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -0,0 +1,772 @@
+#include "uds/service_endpoint.h"
+
+#include <poll.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <algorithm> // std::min
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <cutils/sockets.h>
+#include <pdx/service.h>
+#include <selinux/selinux.h>
+#include <uds/channel_manager.h>
+#include <uds/client_channel_factory.h>
+#include <uds/ipc_helper.h>
+
+namespace {
+
+constexpr int kMaxBackLogForSocketListen = 1;
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::BorrowedHandle;
+using android::pdx::ChannelReference;
+using android::pdx::ErrorStatus;
+using android::pdx::FileReference;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Status;
+using android::pdx::uds::ChannelInfo;
+using android::pdx::uds::ChannelManager;
+
+struct MessageState {
+ bool GetLocalFileHandle(int index, LocalHandle* handle) {
+ if (index < 0) {
+ handle->Reset(index);
+ } else if (static_cast<size_t>(index) < request.file_descriptors.size()) {
+ *handle = std::move(request.file_descriptors[index]);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ bool GetLocalChannelHandle(int index, LocalChannelHandle* handle) {
+ if (index < 0) {
+ *handle = LocalChannelHandle{nullptr, index};
+ } else if (static_cast<size_t>(index) < request.channels.size()) {
+ auto& channel_info = request.channels[index];
+ *handle = ChannelManager::Get().CreateHandle(
+ std::move(channel_info.data_fd), std::move(channel_info.event_fd));
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ Status<FileReference> PushFileHandle(BorrowedHandle handle) {
+ if (!handle)
+ return handle.Get();
+ response.file_descriptors.push_back(std::move(handle));
+ return response.file_descriptors.size() - 1;
+ }
+
+ Status<ChannelReference> PushChannelHandle(BorrowedChannelHandle handle) {
+ if (!handle)
+ return handle.value();
+
+ if (auto* channel_data =
+ ChannelManager::Get().GetChannelData(handle.value())) {
+ ChannelInfo<BorrowedHandle> channel_info;
+ channel_info.data_fd.Reset(handle.value());
+ channel_info.event_fd = channel_data->event_receiver.event_fd();
+ response.channels.push_back(std::move(channel_info));
+ return response.channels.size() - 1;
+ } else {
+ return ErrorStatus{EINVAL};
+ }
+ }
+
+ Status<ChannelReference> PushChannelHandle(BorrowedHandle data_fd,
+ BorrowedHandle event_fd) {
+ if (!data_fd || !event_fd)
+ return ErrorStatus{EINVAL};
+ ChannelInfo<BorrowedHandle> channel_info;
+ channel_info.data_fd = std::move(data_fd);
+ channel_info.event_fd = std::move(event_fd);
+ response.channels.push_back(std::move(channel_info));
+ return response.channels.size() - 1;
+ }
+
+ Status<size_t> WriteData(const iovec* vector, size_t vector_length) {
+ size_t size = 0;
+ for (size_t i = 0; i < vector_length; i++) {
+ const auto* data = reinterpret_cast<const uint8_t*>(vector[i].iov_base);
+ response_data.insert(response_data.end(), data, data + vector[i].iov_len);
+ size += vector[i].iov_len;
+ }
+ return size;
+ }
+
+ Status<size_t> ReadData(const iovec* vector, size_t vector_length) {
+ size_t size_remaining = request_data.size() - request_data_read_pos;
+ size_t size = 0;
+ for (size_t i = 0; i < vector_length && size_remaining > 0; i++) {
+ size_t size_to_copy = std::min(size_remaining, vector[i].iov_len);
+ memcpy(vector[i].iov_base, request_data.data() + request_data_read_pos,
+ size_to_copy);
+ size += size_to_copy;
+ request_data_read_pos += size_to_copy;
+ size_remaining -= size_to_copy;
+ }
+ return size;
+ }
+
+ android::pdx::uds::RequestHeader<LocalHandle> request;
+ android::pdx::uds::ResponseHeader<BorrowedHandle> response;
+ std::vector<LocalHandle> sockets_to_close;
+ std::vector<uint8_t> request_data;
+ size_t request_data_read_pos{0};
+ std::vector<uint8_t> response_data;
+};
+
+} // anonymous namespace
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+Endpoint::Endpoint(const std::string& endpoint_path, bool blocking,
+ bool use_init_socket_fd)
+ : endpoint_path_{ClientChannelFactory::GetEndpointPath(endpoint_path)},
+ is_blocking_{blocking} {
+ LocalHandle fd;
+ if (use_init_socket_fd) {
+ // Cut off the /dev/socket/ prefix from the full socket path and use the
+ // resulting "name" to retrieve the file descriptor for the socket created
+ // by the init process.
+ constexpr char prefix[] = "/dev/socket/";
+ CHECK(android::base::StartsWith(endpoint_path_, prefix))
+ << "Endpoint::Endpoint: Socket name '" << endpoint_path_
+ << "' must begin with '" << prefix << "'";
+ std::string socket_name = endpoint_path_.substr(sizeof(prefix) - 1);
+ fd.Reset(android_get_control_socket(socket_name.c_str()));
+ CHECK(fd.IsValid())
+ << "Endpoint::Endpoint: Unable to obtain the control socket fd for '"
+ << socket_name << "'";
+ fcntl(fd.Get(), F_SETFD, FD_CLOEXEC);
+ } else {
+ fd.Reset(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ CHECK(fd.IsValid()) << "Endpoint::Endpoint: Failed to create socket: "
+ << strerror(errno);
+
+ sockaddr_un local;
+ local.sun_family = AF_UNIX;
+ strncpy(local.sun_path, endpoint_path_.c_str(), sizeof(local.sun_path));
+ local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+ unlink(local.sun_path);
+ int ret =
+ bind(fd.Get(), reinterpret_cast<sockaddr*>(&local), sizeof(local));
+ CHECK_EQ(ret, 0) << "Endpoint::Endpoint: bind error: " << strerror(errno);
+ }
+ Init(std::move(fd));
+}
+
+Endpoint::Endpoint(LocalHandle socket_fd) { Init(std::move(socket_fd)); }
+
+void Endpoint::Init(LocalHandle socket_fd) {
+ if (socket_fd) {
+ CHECK_EQ(listen(socket_fd.Get(), kMaxBackLogForSocketListen), 0)
+ << "Endpoint::Endpoint: listen error: " << strerror(errno);
+ }
+ cancel_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ CHECK(cancel_event_fd_.IsValid())
+ << "Endpoint::Endpoint: Failed to create event fd: " << strerror(errno);
+
+ epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
+ CHECK(epoll_fd_.IsValid())
+ << "Endpoint::Endpoint: Failed to create epoll fd: " << strerror(errno);
+
+ if (socket_fd) {
+ epoll_event socket_event;
+ socket_event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+ socket_event.data.fd = socket_fd.Get();
+ int ret = epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, socket_fd.Get(),
+ &socket_event);
+ CHECK_EQ(ret, 0)
+ << "Endpoint::Endpoint: Failed to add socket fd to epoll fd: "
+ << strerror(errno);
+ }
+
+ epoll_event cancel_event;
+ cancel_event.events = EPOLLIN;
+ cancel_event.data.fd = cancel_event_fd_.Get();
+
+ int ret = epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, cancel_event_fd_.Get(),
+ &cancel_event);
+ CHECK_EQ(ret, 0)
+ << "Endpoint::Endpoint: Failed to add cancel event fd to epoll fd: "
+ << strerror(errno);
+ socket_fd_ = std::move(socket_fd);
+}
+
+void* Endpoint::AllocateMessageState() { return new MessageState; }
+
+void Endpoint::FreeMessageState(void* state) {
+ delete static_cast<MessageState*>(state);
+}
+
+Status<void> Endpoint::AcceptConnection(Message* message) {
+ if (!socket_fd_)
+ return ErrorStatus(EBADF);
+
+ sockaddr_un remote;
+ socklen_t addrlen = sizeof(remote);
+ LocalHandle connection_fd{accept4(socket_fd_.Get(),
+ reinterpret_cast<sockaddr*>(&remote),
+ &addrlen, SOCK_CLOEXEC)};
+ if (!connection_fd) {
+ ALOGE("Endpoint::AcceptConnection: failed to accept connection: %s",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+
+ LocalHandle local_socket;
+ LocalHandle remote_socket;
+ auto status = CreateChannelSocketPair(&local_socket, &remote_socket);
+ if (!status)
+ return status;
+
+ // Borrow the local channel handle before we move it into OnNewChannel().
+ BorrowedHandle channel_handle = local_socket.Borrow();
+ status = OnNewChannel(std::move(local_socket));
+ if (!status)
+ return status;
+
+ // Send the channel socket fd to the client.
+ ChannelConnectionInfo<LocalHandle> connection_info;
+ connection_info.channel_fd = std::move(remote_socket);
+ status = SendData(connection_fd.Borrow(), connection_info);
+
+ if (status) {
+ // Get the CHANNEL_OPEN message from client over the channel socket.
+ status = ReceiveMessageForChannel(channel_handle, message);
+ } else {
+ CloseChannel(GetChannelId(channel_handle));
+ }
+
+ // Don't need the connection socket anymore. Further communication should
+ // happen over the channel socket.
+ shutdown(connection_fd.Get(), SHUT_WR);
+ return status;
+}
+
+Status<void> Endpoint::SetService(Service* service) {
+ service_ = service;
+ return {};
+}
+
+Status<void> Endpoint::SetChannel(int channel_id, Channel* channel) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto channel_data = channels_.find(channel_id);
+ if (channel_data == channels_.end())
+ return ErrorStatus{EINVAL};
+ channel_data->second.channel_state = channel;
+ return {};
+}
+
+Status<void> Endpoint::OnNewChannel(LocalHandle channel_fd) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ Status<void> status;
+ status.PropagateError(OnNewChannelLocked(std::move(channel_fd), nullptr));
+ return status;
+}
+
+Status<std::pair<int32_t, Endpoint::ChannelData*>> Endpoint::OnNewChannelLocked(
+ LocalHandle channel_fd, Channel* channel_state) {
+ epoll_event event;
+ event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+ event.data.fd = channel_fd.Get();
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, channel_fd.Get(), &event) < 0) {
+ ALOGE(
+ "Endpoint::OnNewChannelLocked: Failed to add channel to endpoint: %s\n",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+ ChannelData channel_data;
+ channel_data.event_set.AddDataFd(channel_fd);
+ channel_data.data_fd = std::move(channel_fd);
+ channel_data.channel_state = channel_state;
+ for (;;) {
+ // Try new channel IDs until we find one which is not already in the map.
+ if (last_channel_id_++ == std::numeric_limits<int32_t>::max())
+ last_channel_id_ = 1;
+ auto iter = channels_.lower_bound(last_channel_id_);
+ if (iter == channels_.end() || iter->first != last_channel_id_) {
+ channel_fd_to_id_.emplace(channel_data.data_fd.Get(), last_channel_id_);
+ iter = channels_.emplace_hint(iter, last_channel_id_,
+ std::move(channel_data));
+ return std::make_pair(last_channel_id_, &iter->second);
+ }
+ }
+}
+
+Status<void> Endpoint::ReenableEpollEvent(const BorrowedHandle& fd) {
+ epoll_event event;
+ event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+ event.data.fd = fd.Get();
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, fd.Get(), &event) < 0) {
+ ALOGE(
+ "Endpoint::ReenableEpollEvent: Failed to re-enable channel to "
+ "endpoint: %s\n",
+ strerror(errno));
+ return ErrorStatus(errno);
+ }
+ return {};
+}
+
+Status<void> Endpoint::CloseChannel(int channel_id) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ return CloseChannelLocked(channel_id);
+}
+
+Status<void> Endpoint::CloseChannelLocked(int32_t channel_id) {
+ ALOGD_IF(TRACE, "Endpoint::CloseChannelLocked: channel_id=%d", channel_id);
+
+ auto iter = channels_.find(channel_id);
+ if (iter == channels_.end())
+ return ErrorStatus{EINVAL};
+
+ int channel_fd = iter->second.data_fd.Get();
+ Status<void> status;
+ epoll_event dummy; // See BUGS in man 2 epoll_ctl.
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &dummy) < 0) {
+ status.SetError(errno);
+ ALOGE(
+ "Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: "
+ "%s\n",
+ strerror(errno));
+ } else {
+ status.SetValue();
+ }
+
+ channel_fd_to_id_.erase(channel_fd);
+ channels_.erase(iter);
+ return status;
+}
+
+Status<void> Endpoint::ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+
+ auto search = channels_.find(channel_id);
+ if (search != channels_.end()) {
+ auto& channel_data = search->second;
+ channel_data.event_set.ModifyEvents(clear_mask, set_mask);
+ return {};
+ }
+
+ return ErrorStatus{EINVAL};
+}
+
+Status<void> Endpoint::CreateChannelSocketPair(LocalHandle* local_socket,
+ LocalHandle* remote_socket) {
+ Status<void> status;
+ char* endpoint_context = nullptr;
+ // Make sure the channel socket has the correct SELinux label applied.
+ // Here we get the label from the endpoint file descriptor, which should be
+ // something like "u:object_r:pdx_service_endpoint_socket:s0" and replace
+ // "endpoint" with "channel" to produce the channel label such as this:
+ // "u:object_r:pdx_service_channel_socket:s0".
+ if (fgetfilecon_raw(socket_fd_.Get(), &endpoint_context) > 0) {
+ std::string channel_context = endpoint_context;
+ freecon(endpoint_context);
+ const std::string suffix = "_endpoint_socket";
+ auto pos = channel_context.find(suffix);
+ if (pos != std::string::npos) {
+ channel_context.replace(pos, suffix.size(), "_channel_socket");
+ } else {
+ ALOGW(
+ "Endpoint::CreateChannelSocketPair: Endpoint security context '%s' "
+ "does not contain expected substring '%s'",
+ channel_context.c_str(), suffix.c_str());
+ }
+ ALOGE_IF(setsockcreatecon_raw(channel_context.c_str()) == -1,
+ "Endpoint::CreateChannelSocketPair: Failed to set channel socket "
+ "security context: %s",
+ strerror(errno));
+ } else {
+ ALOGE(
+ "Endpoint::CreateChannelSocketPair: Failed to obtain the endpoint "
+ "socket's security context: %s",
+ strerror(errno));
+ }
+
+ int channel_pair[2] = {};
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, channel_pair) == -1) {
+ ALOGE("Endpoint::CreateChannelSocketPair: Failed to create socket pair: %s",
+ strerror(errno));
+ status.SetError(errno);
+ return status;
+ }
+
+ setsockcreatecon_raw(nullptr);
+
+ local_socket->Reset(channel_pair[0]);
+ remote_socket->Reset(channel_pair[1]);
+
+ int optval = 1;
+ if (setsockopt(local_socket->Get(), SOL_SOCKET, SO_PASSCRED, &optval,
+ sizeof(optval)) == -1) {
+ ALOGE(
+ "Endpoint::CreateChannelSocketPair: Failed to enable the receiving of "
+ "the credentials for channel %d: %s",
+ local_socket->Get(), strerror(errno));
+ status.SetError(errno);
+ }
+ return status;
+}
+
+Status<RemoteChannelHandle> Endpoint::PushChannel(Message* message,
+ int /*flags*/,
+ Channel* channel,
+ int* channel_id) {
+ LocalHandle local_socket;
+ LocalHandle remote_socket;
+ auto status = CreateChannelSocketPair(&local_socket, &remote_socket);
+ if (!status)
+ return status.error_status();
+
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto channel_data = OnNewChannelLocked(std::move(local_socket), channel);
+ if (!channel_data)
+ return channel_data.error_status();
+ *channel_id = channel_data.get().first;
+
+ // Flags are ignored for now.
+ // TODO(xiaohuit): Implement those.
+
+ auto* state = static_cast<MessageState*>(message->GetState());
+ Status<ChannelReference> ref = state->PushChannelHandle(
+ remote_socket.Borrow(),
+ channel_data.get().second->event_set.event_fd().Borrow());
+ if (!ref)
+ return ref.error_status();
+ state->sockets_to_close.push_back(std::move(remote_socket));
+ return RemoteChannelHandle{ref.get()};
+}
+
+Status<int> Endpoint::CheckChannel(const Message* /*message*/,
+ ChannelReference /*ref*/,
+ Channel** /*channel*/) {
+ // TODO(xiaohuit): Implement this.
+ return ErrorStatus(EFAULT);
+}
+
+Channel* Endpoint::GetChannelState(int32_t channel_id) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto channel_data = channels_.find(channel_id);
+ return (channel_data != channels_.end()) ? channel_data->second.channel_state
+ : nullptr;
+}
+
+BorrowedHandle Endpoint::GetChannelSocketFd(int32_t channel_id) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ BorrowedHandle handle;
+ auto channel_data = channels_.find(channel_id);
+ if (channel_data != channels_.end())
+ handle = channel_data->second.data_fd.Borrow();
+ return handle;
+}
+
+BorrowedHandle Endpoint::GetChannelEventFd(int32_t channel_id) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ BorrowedHandle handle;
+ auto channel_data = channels_.find(channel_id);
+ if (channel_data != channels_.end())
+ handle = channel_data->second.event_set.event_fd().Borrow();
+ return handle;
+}
+
+int32_t Endpoint::GetChannelId(const BorrowedHandle& channel_fd) {
+ std::lock_guard<std::mutex> autolock(channel_mutex_);
+ auto iter = channel_fd_to_id_.find(channel_fd.Get());
+ return (iter != channel_fd_to_id_.end()) ? iter->second : -1;
+}
+
+Status<void> Endpoint::ReceiveMessageForChannel(
+ const BorrowedHandle& channel_fd, Message* message) {
+ RequestHeader<LocalHandle> request;
+ int32_t channel_id = GetChannelId(channel_fd);
+ auto status = ReceiveData(channel_fd.Borrow(), &request);
+ if (!status) {
+ if (status.error() == ESHUTDOWN) {
+ BuildCloseMessage(channel_id, message);
+ return {};
+ } else {
+ CloseChannel(channel_id);
+ return status;
+ }
+ }
+
+ MessageInfo info;
+ info.pid = request.cred.pid;
+ info.tid = -1;
+ info.cid = channel_id;
+ info.mid = request.is_impulse ? Message::IMPULSE_MESSAGE_ID
+ : GetNextAvailableMessageId();
+ info.euid = request.cred.uid;
+ info.egid = request.cred.gid;
+ info.op = request.op;
+ info.flags = 0;
+ info.service = service_;
+ info.channel = GetChannelState(channel_id);
+ info.send_len = request.send_len;
+ info.recv_len = request.max_recv_len;
+ info.fd_count = request.file_descriptors.size();
+ static_assert(sizeof(info.impulse) == request.impulse_payload.size(),
+ "Impulse payload sizes must be the same in RequestHeader and "
+ "MessageInfo");
+ memcpy(info.impulse, request.impulse_payload.data(),
+ request.impulse_payload.size());
+ *message = Message{info};
+ auto* state = static_cast<MessageState*>(message->GetState());
+ state->request = std::move(request);
+ if (request.send_len > 0 && !request.is_impulse) {
+ state->request_data.resize(request.send_len);
+ status = ReceiveData(channel_fd, state->request_data.data(),
+ state->request_data.size());
+ }
+
+ if (status && request.is_impulse)
+ status = ReenableEpollEvent(channel_fd);
+
+ if (!status) {
+ if (status.error() == ESHUTDOWN) {
+ BuildCloseMessage(channel_id, message);
+ return {};
+ } else {
+ CloseChannel(channel_id);
+ return status;
+ }
+ }
+
+ return status;
+}
+
+void Endpoint::BuildCloseMessage(int32_t channel_id, Message* message) {
+ ALOGD_IF(TRACE, "Endpoint::BuildCloseMessage: channel_id=%d", channel_id);
+ MessageInfo info;
+ info.pid = -1;
+ info.tid = -1;
+ info.cid = channel_id;
+ info.mid = GetNextAvailableMessageId();
+ info.euid = -1;
+ info.egid = -1;
+ info.op = opcodes::CHANNEL_CLOSE;
+ info.flags = 0;
+ info.service = service_;
+ info.channel = GetChannelState(channel_id);
+ info.send_len = 0;
+ info.recv_len = 0;
+ info.fd_count = 0;
+ *message = Message{info};
+}
+
+Status<void> Endpoint::MessageReceive(Message* message) {
+ // Receive at most one event from the epoll set. This should prevent multiple
+ // dispatch threads from attempting to handle messages on the same socket at
+ // the same time.
+ epoll_event event;
+ int count = RETRY_EINTR(
+ epoll_wait(epoll_fd_.Get(), &event, 1, is_blocking_ ? -1 : 0));
+ if (count < 0) {
+ ALOGE("Endpoint::MessageReceive: Failed to wait for epoll events: %s\n",
+ strerror(errno));
+ return ErrorStatus{errno};
+ } else if (count == 0) {
+ return ErrorStatus{ETIMEDOUT};
+ }
+
+ if (event.data.fd == cancel_event_fd_.Get()) {
+ return ErrorStatus{ESHUTDOWN};
+ }
+
+ if (socket_fd_ && event.data.fd == socket_fd_.Get()) {
+ auto status = AcceptConnection(message);
+ if (!status)
+ return status;
+ return ReenableEpollEvent(socket_fd_.Borrow());
+ }
+
+ BorrowedHandle channel_fd{event.data.fd};
+ if (event.events & (EPOLLRDHUP | EPOLLHUP)) {
+ BuildCloseMessage(GetChannelId(channel_fd), message);
+ return {};
+ }
+
+ return ReceiveMessageForChannel(channel_fd, message);
+}
+
+Status<void> Endpoint::MessageReply(Message* message, int return_code) {
+ const int32_t channel_id = message->GetChannelId();
+ auto channel_socket = GetChannelSocketFd(channel_id);
+ if (!channel_socket)
+ return ErrorStatus{EBADF};
+
+ auto* state = static_cast<MessageState*>(message->GetState());
+ switch (message->GetOp()) {
+ case opcodes::CHANNEL_CLOSE:
+ return CloseChannel(channel_id);
+
+ case opcodes::CHANNEL_OPEN:
+ if (return_code < 0) {
+ return CloseChannel(channel_id);
+ } else {
+ // Reply with the event fd.
+ auto push_status = state->PushFileHandle(GetChannelEventFd(channel_id));
+ state->response_data.clear(); // Just in case...
+ if (!push_status)
+ return push_status.error_status();
+ return_code = push_status.get();
+ }
+ break;
+ }
+
+ state->response.ret_code = return_code;
+ state->response.recv_len = state->response_data.size();
+ auto status = SendData(channel_socket, state->response);
+ if (status && !state->response_data.empty()) {
+ status = SendData(channel_socket, state->response_data.data(),
+ state->response_data.size());
+ }
+
+ if (status)
+ status = ReenableEpollEvent(channel_socket);
+
+ return status;
+}
+
+Status<void> Endpoint::MessageReplyFd(Message* message, unsigned int push_fd) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ auto ref = state->PushFileHandle(BorrowedHandle{static_cast<int>(push_fd)});
+ if (!ref)
+ return ref.error_status();
+ return MessageReply(message, ref.get());
+}
+
+Status<void> Endpoint::MessageReplyChannelHandle(
+ Message* message, const LocalChannelHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ auto ref = state->PushChannelHandle(handle.Borrow());
+ if (!ref)
+ return ref.error_status();
+ return MessageReply(message, ref.get());
+}
+
+Status<void> Endpoint::MessageReplyChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ auto ref = state->PushChannelHandle(handle.Duplicate());
+ if (!ref)
+ return ref.error_status();
+ return MessageReply(message, ref.get());
+}
+
+Status<void> Endpoint::MessageReplyChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) {
+ return MessageReply(message, handle.value());
+}
+
+Status<size_t> Endpoint::ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->ReadData(vector, vector_length);
+}
+
+Status<size_t> Endpoint::WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->WriteData(vector, vector_length);
+}
+
+Status<FileReference> Endpoint::PushFileHandle(Message* message,
+ const LocalHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->PushFileHandle(handle.Borrow());
+}
+
+Status<FileReference> Endpoint::PushFileHandle(Message* message,
+ const BorrowedHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->PushFileHandle(handle.Duplicate());
+}
+
+Status<FileReference> Endpoint::PushFileHandle(Message* /*message*/,
+ const RemoteHandle& handle) {
+ return handle.Get();
+}
+
+Status<ChannelReference> Endpoint::PushChannelHandle(
+ Message* message, const LocalChannelHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->PushChannelHandle(handle.Borrow());
+}
+
+Status<ChannelReference> Endpoint::PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) {
+ auto* state = static_cast<MessageState*>(message->GetState());
+ return state->PushChannelHandle(handle.Duplicate());
+}
+
+Status<ChannelReference> Endpoint::PushChannelHandle(
+ Message* /*message*/, const RemoteChannelHandle& handle) {
+ return handle.value();
+}
+
+LocalHandle Endpoint::GetFileHandle(Message* message, FileReference ref) const {
+ LocalHandle handle;
+ auto* state = static_cast<MessageState*>(message->GetState());
+ state->GetLocalFileHandle(ref, &handle);
+ return handle;
+}
+
+LocalChannelHandle Endpoint::GetChannelHandle(Message* message,
+ ChannelReference ref) const {
+ LocalChannelHandle handle;
+ auto* state = static_cast<MessageState*>(message->GetState());
+ state->GetLocalChannelHandle(ref, &handle);
+ return handle;
+}
+
+Status<void> Endpoint::Cancel() {
+ if (eventfd_write(cancel_event_fd_.Get(), 1) < 0)
+ return ErrorStatus{errno};
+ return {};
+}
+
+std::unique_ptr<Endpoint> Endpoint::Create(const std::string& endpoint_path,
+ mode_t /*unused_mode*/,
+ bool blocking) {
+ return std::unique_ptr<Endpoint>(new Endpoint(endpoint_path, blocking));
+}
+
+std::unique_ptr<Endpoint> Endpoint::CreateAndBindSocket(
+ const std::string& endpoint_path, bool blocking) {
+ return std::unique_ptr<Endpoint>(
+ new Endpoint(endpoint_path, blocking, false));
+}
+
+std::unique_ptr<Endpoint> Endpoint::CreateFromSocketFd(LocalHandle socket_fd) {
+ return std::unique_ptr<Endpoint>(new Endpoint(std::move(socket_fd)));
+}
+
+Status<void> Endpoint::RegisterNewChannelForTests(LocalHandle channel_fd) {
+ int optval = 1;
+ if (setsockopt(channel_fd.Get(), SOL_SOCKET, SO_PASSCRED, &optval,
+ sizeof(optval)) == -1) {
+ ALOGE(
+ "Endpoint::RegisterNewChannelForTests: Failed to enable the receiving"
+ "of the credentials for channel %d: %s",
+ channel_fd.Get(), strerror(errno));
+ return ErrorStatus(errno);
+ }
+ return OnNewChannel(std::move(channel_fd));
+}
+
+} // namespace uds
+} // namespace pdx
+} // namespace android
diff --git a/libs/vr/libpdx_uds/service_framework_tests.cpp b/libs/vr/libpdx_uds/service_framework_tests.cpp
new file mode 100644
index 0000000000..2943239495
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_framework_tests.cpp
@@ -0,0 +1,688 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+#include <array>
+#include <atomic>
+#include <memory>
+#include <numeric>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/service.h>
+#include <private/android_filesystem_config.h>
+#include <uds/client_channel.h>
+#include <uds/client_channel_factory.h>
+#include <uds/service_dispatcher.h>
+#include <uds/service_endpoint.h>
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::Channel;
+using android::pdx::ChannelReference;
+using android::pdx::ClientBase;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::ServiceBase;
+using android::pdx::ServiceDispatcher;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::uds::Endpoint;
+
+namespace {
+
+const size_t kLargeDataSize = 100000;
+
+const char kTestServicePath[] = "socket_test";
+const char kTestService1[] = "1";
+const char kTestService2[] = "2";
+
+enum test_op_codes {
+ TEST_OP_GET_SERVICE_ID,
+ TEST_OP_SET_TEST_CHANNEL,
+ TEST_OP_GET_THIS_CHANNEL_ID,
+ TEST_OP_GET_TEST_CHANNEL_ID,
+ TEST_OP_CHECK_CHANNEL_ID,
+ TEST_OP_CHECK_CHANNEL_OBJECT,
+ TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE,
+ TEST_OP_GET_NEW_CHANNEL,
+ TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE,
+ TEST_OP_GET_THIS_PROCESS_ID,
+ TEST_OP_GET_THIS_THREAD_ID,
+ TEST_OP_GET_THIS_EUID,
+ TEST_OP_GET_THIS_EGID,
+ TEST_OP_IMPULSE,
+ TEST_OP_POLLHUP_FROM_SERVICE,
+ TEST_OP_POLLIN_FROM_SERVICE,
+ TEST_OP_SEND_LARGE_DATA_RETURN_SUM,
+};
+
+using ImpulsePayload = std::array<std::uint8_t, sizeof(MessageInfo::impulse)>;
+
+// The test service creates a TestChannel for every client (channel) that
+// connects. This represents the service-side context for each client.
+class TestChannel : public Channel {
+ public:
+ explicit TestChannel(int channel_id) : channel_id_(channel_id) {}
+
+ int channel_id() const { return channel_id_; }
+
+ private:
+ friend class TestService;
+
+ int channel_id_;
+
+ TestChannel(const TestChannel&) = delete;
+ void operator=(const TestChannel&) = delete;
+};
+
+// Test service that creates a TestChannel for each channel and responds to test
+// messages.
+class TestService : public ServiceBase<TestService> {
+ public:
+ std::shared_ptr<Channel> OnChannelOpen(Message& message) override {
+ return std::make_shared<TestChannel>(message.GetChannelId());
+ }
+
+ void OnChannelClose(Message& /*message*/,
+ const std::shared_ptr<Channel>& channel) override {
+ if (test_channel_ == channel)
+ test_channel_ = nullptr;
+ }
+
+ void HandleImpulse(Message& message) override {
+ switch (message.GetOp()) {
+ case TEST_OP_SET_TEST_CHANNEL:
+ test_channel_ = message.GetChannel<TestChannel>();
+ break;
+
+ case TEST_OP_IMPULSE: {
+ impulse_payload_.fill(0);
+ std::copy(message.ImpulseBegin(), message.ImpulseEnd(),
+ impulse_payload_.begin());
+ break;
+ }
+
+ case TEST_OP_POLLHUP_FROM_SERVICE: {
+ message.ModifyChannelEvents(0, EPOLLHUP);
+ break;
+ }
+ }
+ }
+
+ Status<void> HandleMessage(Message& message) override {
+ switch (message.GetOp()) {
+ case TEST_OP_GET_SERVICE_ID:
+ REPLY_MESSAGE_RETURN(message, service_id_, {});
+
+ // Set the test channel to the TestChannel for the current channel. Other
+ // messages can use this to perform tests.
+ case TEST_OP_SET_TEST_CHANNEL:
+ test_channel_ = message.GetChannel<TestChannel>();
+ REPLY_MESSAGE_RETURN(message, 0, {});
+
+ // Return the channel id for the current channel.
+ case TEST_OP_GET_THIS_CHANNEL_ID:
+ REPLY_MESSAGE_RETURN(message, message.GetChannelId(), {});
+
+ // Return the channel id for the test channel.
+ case TEST_OP_GET_TEST_CHANNEL_ID:
+ if (test_channel_)
+ REPLY_MESSAGE_RETURN(message, test_channel_->channel_id(), {});
+ else
+ REPLY_ERROR_RETURN(message, ENOENT, {});
+
+ // Test check channel feature.
+ case TEST_OP_CHECK_CHANNEL_ID: {
+ ChannelReference ref = 0;
+ if (!message.ReadAll(&ref, sizeof(ref)))
+ REPLY_ERROR_RETURN(message, EIO, {});
+
+ const Status<int> ret = message.CheckChannel<TestChannel>(ref, nullptr);
+ REPLY_MESSAGE_RETURN(message, ret, {});
+ }
+
+ case TEST_OP_CHECK_CHANNEL_OBJECT: {
+ std::shared_ptr<TestChannel> channel;
+ ChannelReference ref = 0;
+ if (!message.ReadAll(&ref, sizeof(ref)))
+ REPLY_ERROR_RETURN(message, EIO, {});
+
+ const Status<int> ret =
+ message.CheckChannel<TestChannel>(ref, &channel);
+ if (!ret)
+ REPLY_MESSAGE_RETURN(message, ret, {});
+
+ if (channel != nullptr)
+ REPLY_MESSAGE_RETURN(message, channel->channel_id(), {});
+ else
+ REPLY_ERROR_RETURN(message, ENODATA, {});
+ }
+
+ case TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE: {
+ ChannelReference ref = 0;
+ if (!message.ReadAll(&ref, sizeof(ref)))
+ REPLY_ERROR_RETURN(message, EIO, {});
+
+ const Status<int> ret = message.CheckChannel<TestChannel>(
+ other_service_.get(), ref, nullptr);
+ REPLY_MESSAGE_RETURN(message, ret, {});
+ }
+
+ case TEST_OP_GET_NEW_CHANNEL: {
+ auto channel = std::make_shared<TestChannel>(-1);
+ Status<RemoteChannelHandle> channel_handle =
+ message.PushChannel(0, channel, &channel->channel_id_);
+ REPLY_MESSAGE_RETURN(message, channel_handle, {});
+ }
+
+ case TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE: {
+ if (!other_service_)
+ REPLY_ERROR_RETURN(message, EINVAL, {});
+
+ auto channel = std::make_shared<TestChannel>(-1);
+ Status<RemoteChannelHandle> channel_handle = message.PushChannel(
+ other_service_.get(), 0, channel, &channel->channel_id_);
+ REPLY_MESSAGE_RETURN(message, channel_handle, {});
+ }
+
+ case TEST_OP_GET_THIS_PROCESS_ID:
+ REPLY_MESSAGE_RETURN(message, message.GetProcessId(), {});
+
+ case TEST_OP_GET_THIS_THREAD_ID:
+ REPLY_MESSAGE_RETURN(message, message.GetThreadId(), {});
+
+ case TEST_OP_GET_THIS_EUID:
+ REPLY_MESSAGE_RETURN(message, message.GetEffectiveUserId(), {});
+
+ case TEST_OP_GET_THIS_EGID:
+ REPLY_MESSAGE_RETURN(message, message.GetEffectiveGroupId(), {});
+
+ case TEST_OP_POLLIN_FROM_SERVICE:
+ REPLY_MESSAGE_RETURN(message, message.ModifyChannelEvents(0, EPOLLIN),
+ {});
+
+ case TEST_OP_SEND_LARGE_DATA_RETURN_SUM: {
+ std::array<int, kLargeDataSize> data_array;
+ size_t size_to_read = data_array.size() * sizeof(int);
+ if (!message.ReadAll(data_array.data(), size_to_read)) {
+ REPLY_ERROR_RETURN(message, EIO, {});
+ }
+ int sum = std::accumulate(data_array.begin(), data_array.end(), 0);
+ REPLY_MESSAGE_RETURN(message, sum, {});
+ }
+
+ default:
+ return Service::DefaultHandleMessage(message);
+ }
+ }
+
+ const ImpulsePayload& GetImpulsePayload() const { return impulse_payload_; }
+
+ private:
+ friend BASE;
+
+ std::shared_ptr<TestChannel> test_channel_;
+ std::shared_ptr<TestService> other_service_;
+ int service_id_;
+ ImpulsePayload impulse_payload_;
+
+ static std::atomic<int> service_counter_;
+
+ TestService(const std::string& name,
+ const std::shared_ptr<TestService>& other_service)
+ : TestService(name, other_service, false) {}
+
+ TestService(const std::string& name,
+ const std::shared_ptr<TestService>& other_service, bool blocking)
+ : BASE(std::string("TestService") + name,
+ Endpoint::CreateAndBindSocket(kTestServicePath + name, blocking)),
+ other_service_(other_service),
+ service_id_(service_counter_++) {}
+
+ explicit TestService(const std::string& name) : TestService(name, nullptr) {}
+
+ TestService(const TestService&) = delete;
+ void operator=(const TestService&) = delete;
+};
+
+std::atomic<int> TestService::service_counter_;
+
+// Test client to send messages to the test service.
+class TestClient : public ClientBase<TestClient> {
+ public:
+ // Requests the service id of the service this channel is connected to.
+ int GetServiceId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_SERVICE_ID));
+ }
+
+ // Requests the test channel to be set to this client's channel.
+ int SetTestChannel() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_SET_TEST_CHANNEL));
+ }
+
+ // Request the test channel to be set to this client's channel using an async
+ // message.
+ int SetTestChannelAsync() {
+ return ReturnStatusOrError(SendImpulse(TEST_OP_SET_TEST_CHANNEL));
+ }
+
+ // Sends a test async message with payload.
+ int SendAsync(const void* buffer, size_t length) {
+ Transaction trans{*this};
+ return ReturnStatusOrError(SendImpulse(TEST_OP_IMPULSE, buffer, length));
+ }
+
+ // Requests the channel id for this client.
+ int GetThisChannelId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_CHANNEL_ID));
+ }
+
+ // Requests the channel id of the test channel.
+ int GetTestChannelId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_TEST_CHANNEL_ID));
+ }
+
+ // Checks whether the fd |channel_id| is a channel to the test service.
+ // Returns the channel id of the channel.
+ int CheckChannelIdArgument(BorrowedChannelHandle channel) {
+ Transaction trans{*this};
+ ChannelReference ref = trans.PushChannelHandle(channel).get();
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_ID, &ref,
+ sizeof(ref), nullptr, 0));
+ }
+
+ // Checks whether the fd |channel_id| is a channel to the test service.
+ // Returns the channel id of the channel exercising the context pointer.
+ int CheckChannelObjectArgument(BorrowedChannelHandle channel) {
+ Transaction trans{*this};
+ ChannelReference ref = trans.PushChannelHandle(channel).get();
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_OBJECT,
+ &ref, sizeof(ref), nullptr, 0));
+ }
+
+ // Checks whether the fd |channel_fd| is a channel to the other test service.
+ // Returns 0 on success.
+ int CheckChannelFromOtherService(BorrowedChannelHandle channel) {
+ Transaction trans{*this};
+ ChannelReference ref = trans.PushChannelHandle(channel).get();
+ return ReturnStatusOrError(
+ trans.Send<int>(TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE, &ref,
+ sizeof(ref), nullptr, 0));
+ }
+
+ // Requests a new channel to the service.
+ std::unique_ptr<TestClient> GetNewChannel() {
+ Transaction trans{*this};
+ auto status = trans.Send<LocalChannelHandle>(TEST_OP_GET_NEW_CHANNEL);
+ if (status)
+ return TestClient::Create(status.take());
+ else
+ return nullptr;
+ }
+
+ // Requests a new channel to the other service.
+ std::unique_ptr<TestClient> GetNewChannelFromOtherService() {
+ Transaction trans{*this};
+ auto status = trans.Send<LocalChannelHandle>(
+ TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE);
+ if (status)
+ return TestClient::Create(status.take());
+ else
+ return nullptr;
+ }
+
+ // Requests an id from the message description.
+ pid_t GetThisProcessId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_PROCESS_ID));
+ }
+ pid_t GetThisThreadId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_THREAD_ID));
+ }
+ uid_t GetThisEffectiveUserId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EUID));
+ }
+ gid_t GetThisEffectiveGroupId() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EGID));
+ }
+
+ int SendPollHupEvent() {
+ return ReturnStatusOrError(SendImpulse(TEST_OP_POLLHUP_FROM_SERVICE));
+ }
+
+ int SendPollInEvent() {
+ Transaction trans{*this};
+ return ReturnStatusOrError(trans.Send<int>(TEST_OP_POLLIN_FROM_SERVICE));
+ }
+
+ int SendLargeDataReturnSum(
+ const std::array<int, kLargeDataSize>& data_array) {
+ Transaction trans{*this};
+ return ReturnStatusOrError(
+ trans.Send<int>(TEST_OP_SEND_LARGE_DATA_RETURN_SUM, data_array.data(),
+ data_array.size() * sizeof(int), nullptr, 0));
+ }
+
+ Status<int> GetEventMask(int events) {
+ if (auto* client_channel = GetChannel()) {
+ return client_channel->GetEventMask(events);
+ } else {
+ return ErrorStatus(EINVAL);
+ }
+ }
+
+ using ClientBase<TestClient>::event_fd;
+
+ enum : size_t { kMaxPayload = MAX_IMPULSE_LENGTH };
+
+ private:
+ friend BASE;
+
+ explicit TestClient(const std::string& name)
+ : BASE{android::pdx::uds::ClientChannelFactory::Create(kTestServicePath +
+ name)} {}
+
+ explicit TestClient(LocalChannelHandle channel)
+ : BASE{android::pdx::uds::ClientChannel::Create(std::move(channel))} {}
+
+ TestClient(const TestClient&) = delete;
+ void operator=(const TestClient&) = delete;
+};
+
+} // anonymous namespace
+
+// Use a test fixture to ensure proper order of cleanup between clients,
+// services, and the dispatcher. These objects are cleaned up in the same
+// thread, order is important; either the service or the client must be
+// destroyed before the dispatcher is stopped. The reason for this is that
+// clients send blocking "close" messages to their respective services on
+// destruction. If this happens after the dispatcher is stopped the client
+// destructor will get blocked waiting for a reply that will never come. In
+// normal use of the service framework this is never an issue because clients
+// and the dispatcher for the same service are never destructed in the same
+// thread (they live in different processes).
+class ServiceFrameworkTest : public ::testing::Test {
+ protected:
+ std::unique_ptr<ServiceDispatcher> dispatcher_;
+ std::thread dispatch_thread_;
+
+ void SetUp() override {
+ // Create a dispatcher to handle messages to services.
+ dispatcher_ = android::pdx::uds::ServiceDispatcher::Create();
+ ASSERT_NE(nullptr, dispatcher_);
+
+ // Start the message dispatch loop in a separate thread.
+ dispatch_thread_ = std::thread(
+ std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get()));
+ }
+
+ void TearDown() override {
+ if (dispatcher_) {
+ // Cancel the dispatcher and wait for the thread to terminate. Explicitly
+ // join the thread so that destruction doesn't deallocate the dispatcher
+ // before the thread finishes.
+ dispatcher_->SetCanceled(true);
+ dispatch_thread_.join();
+ }
+ }
+};
+
+// Test basic operation of TestService/TestClient classes.
+TEST_F(ServiceFrameworkTest, BasicClientService) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ // Get the channel id that will be returned by the next tests.
+ const int channel_id = client->GetThisChannelId();
+ EXPECT_LE(0, channel_id);
+
+ // Check return value before test channel is set.
+ EXPECT_EQ(-ENOENT, client->GetTestChannelId());
+
+ // Set test channel and perform the test again.
+ EXPECT_EQ(0, client->SetTestChannel());
+ EXPECT_EQ(channel_id, client->GetTestChannelId());
+}
+
+// Test impulses.
+TEST_F(ServiceFrameworkTest, Impulse) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ // Get the channel id that will be returned by the next tests.
+ const int channel_id = client->GetThisChannelId();
+ EXPECT_LE(0, channel_id);
+
+ // Check return value before test channel is set.
+ EXPECT_EQ(-ENOENT, client->GetTestChannelId());
+
+ // Set test channel with an impulse and perform the test again.
+ EXPECT_EQ(0, client->SetTestChannelAsync());
+ EXPECT_EQ(channel_id, client->GetTestChannelId());
+
+ ImpulsePayload expected_payload = {{'a', 'b', 'c'}};
+ EXPECT_EQ(0, client->SendAsync(expected_payload.data(), 3));
+ // Send a synchronous message to make sure the async message is handled before
+ // we check the payload.
+ client->GetThisChannelId();
+ EXPECT_EQ(expected_payload, service->GetImpulsePayload());
+
+ // Impulse payloads are limited to 4 machine words.
+ EXPECT_EQ(
+ 0, client->SendAsync(expected_payload.data(), TestClient::kMaxPayload));
+ EXPECT_EQ(-EINVAL, client->SendAsync(expected_payload.data(),
+ TestClient::kMaxPayload + 1));
+
+ // Test invalid pointer.
+ const std::uint8_t* invalid_pointer = nullptr;
+ EXPECT_EQ(-EINVAL, client->SendAsync(invalid_pointer, sizeof(int)));
+}
+
+// Test Message::PushChannel/Service::PushChannel API.
+TEST_F(ServiceFrameworkTest, PushChannel) {
+ // Create a test service and add it to the dispatcher.
+ auto other_service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, other_service);
+ ASSERT_EQ(0, dispatcher_->AddService(other_service));
+
+ // Create a second test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService2, other_service);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to the second test service.
+ auto client1 = TestClient::Create(kTestService2);
+ ASSERT_NE(nullptr, client1);
+
+ // Test the creation of new channels using the push APIs.
+ const int channel_id1 = client1->GetThisChannelId();
+ EXPECT_LE(0, channel_id1);
+
+ auto client2 = client1->GetNewChannel();
+ EXPECT_NE(nullptr, client2);
+ EXPECT_NE(client1->event_fd(), client2->event_fd());
+
+ const int channel_id2 = client2->GetThisChannelId();
+ EXPECT_LE(0, channel_id2);
+ EXPECT_NE(channel_id1, channel_id2);
+
+ auto client3 = client1->GetNewChannelFromOtherService();
+ EXPECT_NE(nullptr, client3);
+ EXPECT_NE(client1->event_fd(), client3->event_fd());
+
+ const int channel_id3 = client3->GetThisChannelId();
+ EXPECT_LE(0, channel_id3);
+
+ // Test which services the channels are connected to.
+ const int service_id1 = client1->GetServiceId();
+ EXPECT_LE(0, service_id1);
+
+ const int service_id2 = client2->GetServiceId();
+ EXPECT_LE(0, service_id2);
+
+ const int service_id3 = client3->GetServiceId();
+ EXPECT_LE(0, service_id3);
+
+ EXPECT_EQ(service_id1, service_id2);
+ EXPECT_NE(service_id1, service_id3);
+}
+
+// Tests process id, thread id, effective user id, and effective group id
+// returned in the message description.
+TEST_F(ServiceFrameworkTest, Ids) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ // Pids 0-2 are defined, no user task should have them.
+
+ const pid_t process_id1 = client->GetThisProcessId();
+ EXPECT_LT(2, process_id1);
+
+ pid_t process_id2;
+
+ std::thread thread([&]() {
+ process_id2 = client->GetThisProcessId();
+ });
+ thread.join();
+
+ EXPECT_LT(2, process_id2);
+ EXPECT_EQ(process_id1, process_id2);
+
+ // This test must run as root for the rest of these tests to work.
+ const int euid1 = client->GetThisEffectiveUserId();
+ ASSERT_EQ(0, euid1);
+
+ const int egid1 = client->GetThisEffectiveGroupId();
+ EXPECT_EQ(0, egid1);
+
+ // Set effective uid/gid to system.
+ ASSERT_EQ(0, setegid(AID_SYSTEM));
+ ASSERT_EQ(0, seteuid(AID_SYSTEM));
+
+ const int euid2 = client->GetThisEffectiveUserId();
+ EXPECT_EQ(AID_SYSTEM, euid2);
+
+ const int egid2 = client->GetThisEffectiveGroupId();
+ EXPECT_EQ(AID_SYSTEM, egid2);
+
+ // Set the euid/egid back to root.
+ ASSERT_EQ(0, setegid(0));
+ ASSERT_EQ(0, seteuid(0));
+}
+
+TEST_F(ServiceFrameworkTest, PollIn) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ epoll_event event;
+ int count = epoll_wait(client->event_fd(), &event, 1, 0);
+ ASSERT_EQ(0, count);
+
+ client->SendPollInEvent();
+
+ count = epoll_wait(client->event_fd(), &event, 1, -1);
+ ASSERT_EQ(1, count);
+ ASSERT_TRUE((EPOLLIN & event.events) != 0);
+}
+
+TEST_F(ServiceFrameworkTest, PollHup) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ epoll_event event;
+ int count = epoll_wait(client->event_fd(), &event, 1, 0);
+ ASSERT_EQ(0, count);
+
+ client->SendPollHupEvent();
+
+ count = epoll_wait(client->event_fd(), &event, 1, -1);
+ ASSERT_EQ(1, count);
+ auto event_status = client->GetEventMask(event.events);
+ ASSERT_TRUE(event_status.ok());
+ ASSERT_TRUE((EPOLLHUP & event_status.get()) != 0);
+}
+
+TEST_F(ServiceFrameworkTest, LargeDataSum) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ std::array<int, kLargeDataSize> data_array;
+ std::iota(data_array.begin(), data_array.end(), 0);
+ int expected_sum = std::accumulate(data_array.begin(), data_array.end(), 0);
+ int sum = client->SendLargeDataReturnSum(data_array);
+ ASSERT_EQ(expected_sum, sum);
+}
+
+TEST_F(ServiceFrameworkTest, Cancel) {
+ // Create a test service and add it to the dispatcher.
+ auto service = TestService::Create(kTestService1, nullptr, true);
+ ASSERT_NE(nullptr, service);
+ ASSERT_EQ(0, dispatcher_->AddService(service));
+
+ // Create a client to service.
+ auto client = TestClient::Create(kTestService1);
+ ASSERT_NE(nullptr, client);
+
+ auto previous_time = std::chrono::system_clock::now();
+ dispatcher_->ReceiveAndDispatch(100); // 0.1 seconds should block.
+ auto time = std::chrono::system_clock::now();
+ ASSERT_LE(100, std::chrono::duration_cast<std::chrono::milliseconds>(
+ time - previous_time)
+ .count());
+ service->Cancel();
+ // Non-blocking. Return immediately.
+ dispatcher_->ReceiveAndDispatch(-1);
+ dispatcher_->ReceiveAndDispatch(-1);
+}
diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp
new file mode 100644
index 0000000000..364873dae2
--- /dev/null
+++ b/libs/vr/libperformance/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 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.
+
+sourceFiles = [
+ "performance_client.cpp",
+ "performance_rpc.cpp",
+]
+
+includeFiles = [ "include" ]
+
+staticLibraries = ["libpdx_default_transport"]
+
+sharedLibraries = [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libutils",
+]
+
+cc_library {
+ srcs: sourceFiles,
+ cflags: [
+ "-DLOG_TAG=\"libperformance\"",
+ "-DTRACE=0"
+ ],
+ export_include_dirs: includeFiles,
+ static_libs: staticLibraries,
+ shared_libs: sharedLibraries,
+ name: "libperformance",
+}
diff --git a/libs/vr/libperformance/include/CPPLINT.cfg b/libs/vr/libperformance/include/CPPLINT.cfg
new file mode 100644
index 0000000000..2f8a3c018c
--- /dev/null
+++ b/libs/vr/libperformance/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libperformance/include/dvr/performance_client_api.h b/libs/vr/libperformance/include/dvr/performance_client_api.h
new file mode 100644
index 0000000000..2216e3869a
--- /dev/null
+++ b/libs/vr/libperformance/include/dvr/performance_client_api.h
@@ -0,0 +1,59 @@
+#ifndef ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
+#define ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
+
+#include <stddef.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Sets the CPU partition for a task.
+///
+/// Sets the CPU partition for a task to the partition described by a CPU
+/// partition path.
+///
+/// TODO(eieio): Describe supported partitions and rules governing assignment.
+///
+/// @param task_id The task id of task to attach to a partition. When task_id is
+/// 0 the current task id is substituted.
+/// @param partition NULL-terminated ASCII string describing the CPU partition
+/// to attach the task to.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrSetCpuPartition(pid_t task_id, const char* partition);
+
+/// Sets the scheduler class for a task.
+///
+/// Sets the scheduler class for a task to the class described by a semantic
+/// string.
+///
+/// Supported classes for applications are: audio, graphics, normal, and
+/// background. Additional options following a ':' to be supported in the
+/// future.
+///
+/// @param task_id The task id of task to attach to a partition. When task_id is
+/// 0 the current task id is substituted.
+/// @param scheduler_class NULL-terminated ASCII string containing the desired
+/// scheduler class.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrSetSchedulerClass(pid_t task_id, const char* scheduler_class);
+
+/// Gets the CPU partition for a task.
+///
+/// Gets the CPU partition path for a task as a NULL-terminated ASCII string. If
+/// the path is too large to fit in the supplied buffer, -ENOBUFS is returned.
+///
+/// @param task_id The task id of the task to retrieve the partition for. When
+/// task_id is 0 the current task id is substituted.
+/// @param partition Pointer to an ASCII string buffer to store the partition
+/// path.
+/// @param size Size of the string buffer in bytes, including space for the NULL
+/// terminator.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrGetCpuPartition(pid_t task_id, char* partition, size_t size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_PERFORMANCE_CLIENT_API_H_
diff --git a/libs/vr/libperformance/include/private/dvr/performance_client.h b/libs/vr/libperformance/include/private/dvr/performance_client.h
new file mode 100644
index 0000000000..a61c6b2773
--- /dev/null
+++ b/libs/vr/libperformance/include/private/dvr/performance_client.h
@@ -0,0 +1,36 @@
+#ifndef ANDROID_DVR_PERFORMANCE_CLIENT_H_
+#define ANDROID_DVR_PERFORMANCE_CLIENT_H_
+
+#include <sys/types.h>
+
+#include <cstddef>
+#include <string>
+#include <tuple>
+
+#include <pdx/client.h>
+
+namespace android {
+namespace dvr {
+
+class PerformanceClient : public pdx::ClientBase<PerformanceClient> {
+ public:
+ int SetCpuPartition(pid_t task_id, const std::string& partition);
+ int SetCpuPartition(pid_t task_id, const char* partition);
+ int SetSchedulerClass(pid_t task_id, const std::string& scheduler_class);
+ int SetSchedulerClass(pid_t task_id, const char* scheduler_class);
+ int GetCpuPartition(pid_t task_id, std::string* partition_out);
+ int GetCpuPartition(pid_t task_id, char* partition_out, std::size_t size);
+
+ private:
+ friend BASE;
+
+ explicit PerformanceClient(int* error);
+
+ PerformanceClient(const PerformanceClient&) = delete;
+ void operator=(const PerformanceClient&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCE_CLIENT_H_
diff --git a/libs/vr/libperformance/include/private/dvr/performance_rpc.h b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
new file mode 100644
index 0000000000..73bdaa7d5c
--- /dev/null
+++ b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_PERFORMANCE_RPC_H_
+#define ANDROID_DVR_PERFORMANCE_RPC_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+#include <pdx/rpc/remote_method_type.h>
+
+namespace android {
+namespace dvr {
+
+// Performance Service RPC interface. Defines the endpoint paths, op codes, and
+// method type signatures supported by performanced.
+struct PerformanceRPC {
+ // Service path.
+ static constexpr char kClientPath[] = "system/performance/client";
+
+ // Op codes.
+ enum {
+ kOpSetCpuPartition = 0,
+ kOpSetSchedulerClass,
+ kOpGetCpuPartition,
+ };
+
+ // Methods.
+ PDX_REMOTE_METHOD(SetCpuPartition, kOpSetCpuPartition,
+ int(pid_t, const std::string&));
+ PDX_REMOTE_METHOD(SetSchedulerClass, kOpSetSchedulerClass,
+ int(pid_t, const std::string&));
+ PDX_REMOTE_METHOD(GetCpuPartition, kOpGetCpuPartition, std::string(pid_t));
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_PERFORMANCE_RPC_H_
diff --git a/libs/vr/libperformance/performance_client.cpp b/libs/vr/libperformance/performance_client.cpp
new file mode 100644
index 0000000000..2124162bf7
--- /dev/null
+++ b/libs/vr/libperformance/performance_client.cpp
@@ -0,0 +1,119 @@
+#include "include/private/dvr/performance_client.h"
+
+#include <sys/types.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/string_wrapper.h>
+#include <private/dvr/performance_rpc.h>
+
+using android::pdx::rpc::WrapString;
+
+namespace android {
+namespace dvr {
+
+PerformanceClient::PerformanceClient(int* error)
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ PerformanceRPC::kClientPath)) {
+ if (error)
+ *error = Client::error();
+}
+
+int PerformanceClient::SetCpuPartition(pid_t task_id,
+ const std::string& partition) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetCpuPartition>(task_id, partition));
+}
+
+int PerformanceClient::SetCpuPartition(pid_t task_id, const char* partition) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetCpuPartition>(
+ task_id, WrapString(partition)));
+}
+
+int PerformanceClient::SetSchedulerClass(pid_t task_id,
+ const std::string& scheduler_class) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetSchedulerClass>(task_id,
+ scheduler_class));
+}
+
+int PerformanceClient::SetSchedulerClass(pid_t task_id,
+ const char* scheduler_class) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+ task_id, WrapString(scheduler_class)));
+}
+
+int PerformanceClient::GetCpuPartition(pid_t task_id,
+ std::string* partition_out) {
+ if (partition_out == nullptr)
+ return -EINVAL;
+
+ if (task_id == 0)
+ task_id = gettid();
+
+ auto status = InvokeRemoteMethodInPlace<PerformanceRPC::GetCpuPartition>(
+ partition_out, task_id);
+ return status ? 0 : -status.error();
+}
+
+int PerformanceClient::GetCpuPartition(pid_t task_id, char* partition_out,
+ std::size_t size) {
+ if (partition_out == nullptr)
+ return -EINVAL;
+
+ if (task_id == 0)
+ task_id = gettid();
+
+ auto wrapper = WrapString(partition_out, size);
+ auto status = InvokeRemoteMethodInPlace<PerformanceRPC::GetCpuPartition>(
+ &wrapper, task_id);
+ if (!status)
+ return -status.error();
+
+ if (wrapper.size() < size)
+ partition_out[wrapper.size()] = '\0';
+
+ return 0;
+}
+
+} // namespace dvr
+} // namespace android
+
+extern "C" int dvrSetCpuPartition(pid_t task_id, const char* partition) {
+ int error;
+ if (auto client = android::dvr::PerformanceClient::Create(&error))
+ return client->SetCpuPartition(task_id, partition);
+ else
+ return error;
+}
+
+extern "C" int dvrSetSchedulerClass(pid_t task_id,
+ const char* scheduler_class) {
+ int error;
+ if (auto client = android::dvr::PerformanceClient::Create(&error))
+ return client->SetSchedulerClass(task_id, scheduler_class);
+ else
+ return error;
+}
+
+extern "C" int dvrGetCpuPartition(pid_t task_id, char* partition, size_t size) {
+ int error;
+ if (auto client = android::dvr::PerformanceClient::Create(&error))
+ return client->GetCpuPartition(task_id, partition, size);
+ else
+ return error;
+}
diff --git a/libs/vr/libperformance/performance_rpc.cpp b/libs/vr/libperformance/performance_rpc.cpp
new file mode 100644
index 0000000000..9135349ca2
--- /dev/null
+++ b/libs/vr/libperformance/performance_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/performance_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char PerformanceRPC::kClientPath[];
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvr_manager/Android.bp b/libs/vr/libvr_manager/Android.bp
new file mode 100644
index 0000000000..87848779f2
--- /dev/null
+++ b/libs/vr/libvr_manager/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 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.
+
+exported_include_dirs = [ "include" ]
+
+include_dirs = ["frameworks/native/include/vr/vr_manager"]
+
+src_files = [
+ "vr_manager.cpp",
+ "trusted_uids.cpp",
+]
+
+static_libs = [
+ "libutils",
+ "libbinder",
+]
+
+cc_library_static {
+ srcs: src_files,
+ include_dirs: include_dirs,
+ export_include_dirs: exported_include_dirs,
+ cflags: ["-Wall", "-Werror", "-Wunused", "-Wunreachable-code"],
+ static_libs: static_libs,
+ name: "libvr_manager",
+}
diff --git a/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h b/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h
new file mode 100644
index 0000000000..4496fbff7a
--- /dev/null
+++ b/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h
@@ -0,0 +1,33 @@
+#ifndef ANDROID_DVR_TRUSTED_UIDS_H_
+#define ANDROID_DVR_TRUSTED_UIDS_H_
+
+#include <sys/types.h>
+
+namespace android {
+namespace dvr {
+
+/**
+ * Tells if a provided UID can be trusted to access restricted VR APIs.
+ *
+ * UID trust is based on the android.permission.RESTRICTED_VR_ACCESS permission.
+ * AID_SYSTEM and AID_ROOT are automatically trusted by Android.
+ *
+ * UIDs are guaranteed not to be reused until the next reboot even in case
+ * of package reinstall. For performance reasons this method caches results by
+ * default, as otherwise every check would trigger a Java call.
+ *
+ * This function is thread-safe.
+ *
+ * @param uid The uid to check.
+ * @param use_cache If true any cached result for the provided uid will be
+ * reused. If false this call will reach the Application Manager Service
+ * in Java to get updated values. Any updates will be stored in the cache.
+ * @return true if the uid is trusted, false if not or if the VR Manager Service
+ * could not be reached to verify the uid.
+ */
+bool IsTrustedUid(uid_t uid, bool use_cache = true);
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_TRUSTED_UIDS_H_
diff --git a/libs/vr/libvr_manager/trusted_uids.cpp b/libs/vr/libvr_manager/trusted_uids.cpp
new file mode 100644
index 0000000000..4228a050db
--- /dev/null
+++ b/libs/vr/libvr_manager/trusted_uids.cpp
@@ -0,0 +1,51 @@
+#include "private/dvr/trusted_uids.h"
+
+#include <mutex>
+#include <unordered_map>
+
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String16.h>
+#include <vr/vr_manager/vr_manager.h>
+
+namespace android {
+namespace dvr {
+
+bool IsTrustedUid(uid_t uid, bool use_cache) {
+ static std::unordered_map<uid_t, bool> uid_cache;
+ static std::mutex uid_cache_mutex;
+
+ // Whitelist requests from the system UID.
+ // These are already whitelisted by the permission service, but it might not
+ // be available if the ActivityManagerService is up during boot.
+ // This ensures the correct result for system services while booting up.
+ if (uid == AID_SYSTEM)
+ return true;
+
+ std::lock_guard<std::mutex> lock(uid_cache_mutex);
+
+ if (use_cache) {
+ auto it = uid_cache.find(uid);
+ if (it != uid_cache.end())
+ return it->second;
+ }
+
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+ if (binder == 0) {
+ ALOGW("Could not access permission service");
+ return false;
+ }
+
+ // Note: we ignore the pid because it's only used to automatically reply
+ // true if the caller is the Activity Manager Service.
+ bool trusted = interface_cast<IPermissionController>(binder)->checkPermission(
+ String16("android.permission.RESTRICTED_VR_ACCESS"), -1, uid);
+
+ // Cache the information for this uid to avoid future Java calls.
+ uid_cache[uid] = trusted;
+ return trusted;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvr_manager/vr_manager.cpp b/libs/vr/libvr_manager/vr_manager.cpp
new file mode 100644
index 0000000000..5cfc22eec3
--- /dev/null
+++ b/libs/vr/libvr_manager/vr_manager.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VrManager"
+#include <utils/Log.h>
+
+#include <vr/vr_manager/vr_manager.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+// Must be kept in sync with interface defined in IVrStateCallbacks.aidl.
+
+class BpVrStateCallbacks : public BpInterface<IVrStateCallbacks> {
+ public:
+ explicit BpVrStateCallbacks(const sp<IBinder>& impl)
+ : BpInterface<IVrStateCallbacks>(impl) {}
+
+ void onVrStateChanged(bool enabled) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IVrStateCallbacks::getInterfaceDescriptor());
+ data.writeBool(enabled);
+ remote()->transact(ON_VR_STATE_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(VrStateCallbacks, "android.service.vr.IVrStateCallbacks");
+
+status_t BnVrStateCallbacks::onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ switch(code) {
+ case ON_VR_STATE_CHANGED: {
+ CHECK_INTERFACE(IVrStateCallbacks, data, reply);
+ onVrStateChanged(data.readBool());
+ return OK;
+ }
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+// Must be kept in sync with interface defined in
+// IPersistentVrStateCallbacks.aidl.
+
+class BpPersistentVrStateCallbacks
+ : public BpInterface<IPersistentVrStateCallbacks> {
+ public:
+ explicit BpPersistentVrStateCallbacks(const sp<IBinder>& impl)
+ : BpInterface<IPersistentVrStateCallbacks>(impl) {}
+
+ void onPersistentVrStateChanged(bool enabled) {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IPersistentVrStateCallbacks::getInterfaceDescriptor());
+ data.writeBool(enabled);
+ remote()->transact(ON_PERSISTENT_VR_STATE_CHANGED,
+ data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(PersistentVrStateCallbacks,
+ "android.service.vr.IPersistentVrStateCallbacks");
+
+status_t BnPersistentVrStateCallbacks::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch(code) {
+ case ON_PERSISTENT_VR_STATE_CHANGED: {
+ CHECK_INTERFACE(IPersistentVrStateCallbacks, data, reply);
+ onPersistentVrStateChanged(data.readBool());
+ return OK;
+ }
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+// Must be kept in sync with interface defined in IVrManager.aidl.
+
+class BpVrManager : public BpInterface<IVrManager> {
+ public:
+ explicit BpVrManager(const sp<IBinder>& impl)
+ : BpInterface<IVrManager>(impl) {}
+
+ void registerListener(const sp<IVrStateCallbacks>& cb) override {
+ Parcel data;
+ data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(cb));
+ remote()->transact(REGISTER_LISTENER, data, NULL);
+ }
+
+ void unregisterListener(const sp<IVrStateCallbacks>& cb) override {
+ Parcel data;
+ data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(cb));
+ remote()->transact(UNREGISTER_LISTENER, data, NULL);
+ }
+
+ void registerPersistentVrStateListener(
+ const sp<IPersistentVrStateCallbacks>& cb) override {
+ Parcel data;
+ data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(cb));
+ remote()->transact(REGISTER_PERSISTENT_VR_STATE_LISTENER, data, NULL);
+ }
+
+ void unregisterPersistentVrStateListener(
+ const sp<IPersistentVrStateCallbacks>& cb) override {
+ Parcel data;
+ data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(cb));
+ remote()->transact(UNREGISTER_PERSISTENT_VR_STATE_LISTENER, data, NULL);
+ }
+
+ bool getVrModeState() override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IVrManager::getInterfaceDescriptor());
+ remote()->transact(GET_VR_MODE_STATE, data, &reply);
+ int32_t ret = reply.readExceptionCode();
+ if (ret != 0) {
+ return false;
+ }
+ return reply.readBool();
+ }
+};
+
+IMPLEMENT_META_INTERFACE(VrManager, "android.service.vr.IVrManager");
+
+} // namespace android
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
new file mode 100644
index 0000000000..de26a74f48
--- /dev/null
+++ b/libs/vr/libvrflinger/Android.bp
@@ -0,0 +1,78 @@
+// Copyright (C) 2008 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.
+
+sourceFiles = [
+ "acquired_buffer.cpp",
+ "epoll_event_dispatcher.cpp",
+ "display_manager_service.cpp",
+ "display_service.cpp",
+ "display_surface.cpp",
+ "hardware_composer.cpp",
+ "vr_flinger.cpp",
+ "vsync_service.cpp",
+]
+
+includeFiles = [ "include" ]
+
+staticLibraries = [
+ "libsurfaceflingerincludes",
+ "libhwcomposer-command-buffer",
+ "libbufferhub",
+ "libbufferhubqueue",
+ "libdisplay",
+ "libdvrcommon",
+ "libperformance",
+ "libvrsensor",
+ "libpdx_default_transport",
+ "libvr_manager",
+]
+
+sharedLibraries = [
+ "android.frameworks.vr.composer@1.0",
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.composer@2.1",
+ "libbinder",
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libhardware",
+ "libnativewindow",
+ "libutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libvulkan",
+ "libui",
+ "libgui",
+ "libsync",
+ "libhidlbase",
+ "libhidltransport",
+ "libfmq",
+]
+
+cc_library_static {
+ srcs: sourceFiles,
+ export_include_dirs: includeFiles,
+
+ cflags: [
+ "-DLOG_TAG=\"vr_flinger\"",
+ "-DTRACE=0",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+ ],
+ shared_libs: sharedLibraries,
+ whole_static_libs: staticLibraries,
+ name: "libvrflinger",
+}
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
new file mode 100644
index 0000000000..7932a9c9d3
--- /dev/null
+++ b/libs/vr/libvrflinger/acquired_buffer.cpp
@@ -0,0 +1,100 @@
+#include "acquired_buffer.h"
+
+#include <log/log.h>
+#include <sync/sync.h>
+
+using android::pdx::LocalHandle;
+
+namespace android {
+namespace dvr {
+
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+ LocalHandle acquire_fence)
+ : buffer_(buffer), acquire_fence_(std::move(acquire_fence)) {}
+
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+ int* error) {
+ LocalHandle fence;
+ const int ret = buffer->Acquire(&fence);
+
+ if (error)
+ *error = ret;
+
+ if (ret < 0) {
+ ALOGW("AcquiredBuffer::AcquiredBuffer: Failed to acquire buffer: %s",
+ strerror(-ret));
+ buffer_ = nullptr;
+ // Default construct sets acquire_fence_ to empty.
+ } else {
+ buffer_ = buffer;
+ acquire_fence_ = std::move(fence);
+ }
+}
+
+AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other)
+ : buffer_(std::move(other.buffer_)),
+ acquire_fence_(std::move(other.acquire_fence_)) {}
+
+AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); }
+
+AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) {
+ if (this != &other) {
+ Release(LocalHandle(kEmptyFence));
+
+ buffer_ = std::move(other.buffer_);
+ acquire_fence_ = std::move(other.acquire_fence_);
+ }
+ return *this;
+}
+
+bool AcquiredBuffer::IsAvailable() const {
+ if (IsEmpty())
+ return false;
+
+ // Only check the fence if the acquire fence is not empty.
+ if (acquire_fence_) {
+ const int ret = sync_wait(acquire_fence_.Get(), 0);
+ ALOGD_IF(TRACE || (ret < 0 && errno != ETIME),
+ "AcquiredBuffer::IsAvailable: acquire_fence_=%d sync_wait()=%d "
+ "errno=%d.",
+ acquire_fence_.Get(), ret, ret < 0 ? errno : 0);
+ if (ret == 0) {
+ // The fence is completed, so to avoid further calls to sync_wait we close
+ // it here.
+ acquire_fence_.Close();
+ }
+ return ret == 0;
+ } else {
+ return true;
+ }
+}
+
+LocalHandle AcquiredBuffer::ClaimAcquireFence() {
+ return std::move(acquire_fence_);
+}
+
+std::shared_ptr<BufferConsumer> AcquiredBuffer::ClaimBuffer() {
+ return std::move(buffer_);
+}
+
+int AcquiredBuffer::Release(LocalHandle release_fence) {
+ if (buffer_) {
+ // Close the release fence since we can't transfer it with an async release.
+ release_fence.Close();
+ const int ret = buffer_->ReleaseAsync();
+ if (ret < 0) {
+ ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s",
+ buffer_->id(), strerror(-ret));
+ if (ret != -ESHUTDOWN)
+ return ret;
+ }
+
+ buffer_ = nullptr;
+ acquire_fence_.Close();
+ }
+
+ return 0;
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
new file mode 100644
index 0000000000..dd4fcc5e73
--- /dev/null
+++ b/libs/vr/libvrflinger/acquired_buffer.h
@@ -0,0 +1,82 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
+
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <memory>
+
+namespace android {
+namespace dvr {
+
+// Manages the ACQUIRE/RELEASE ownership cycle of a BufferConsumer.
+class AcquiredBuffer {
+ public:
+ static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle;
+
+ AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {}
+
+ // Constructs an AcquiredBuffer from a BufferConsumer pointer and an acquire
+ // fence. The BufferConsumer MUST be in the ACQUIRED state prior to calling
+ // this constructor; the constructor does not attempt to ACQUIRE the buffer
+ // itself.
+ AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+ pdx::LocalHandle acquire_fence);
+
+ // Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST
+ // be in the POSTED state prior to calling this constructor, as this
+ // constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails
+ // this instance is left in the empty state. An optional error code is
+ // returned in |error|, which may be nullptr if not needed.
+ AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, int* error);
+
+ // Move constructor. Behaves similarly to the move assignment operator below.
+ AcquiredBuffer(AcquiredBuffer&& other);
+
+ ~AcquiredBuffer();
+
+ // Move assignment operator. Moves the BufferConsumer and acquire fence from
+ // |other| into this instance after RELEASING the current BufferConsumer and
+ // closing the acquire fence. After the move |other| is left in the empty
+ // state.
+ AcquiredBuffer& operator=(AcquiredBuffer&& other);
+
+ // Accessors for the underlying BufferConsumer, the acquire fence, and the
+ // use-case specific sequence value from the acquisition (see
+ // private/dvr/buffer_hub_client.h).
+ std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+ int acquire_fence() const { return acquire_fence_.Get(); }
+
+ // When non-empty, returns true if the acquired fence was signaled (or if the
+ // fence is empty). Returns false when empty or if the fence is not signaled.
+ bool IsAvailable() const;
+
+ bool IsEmpty() const { return buffer_ == nullptr; }
+
+ // Returns the acquire fence, passing ownership to the caller.
+ pdx::LocalHandle ClaimAcquireFence();
+
+ // Returns the buffer, passing ownership to the caller. Caller is responsible
+ // for calling Release on the returned buffer.
+ std::shared_ptr<BufferConsumer> ClaimBuffer();
+
+ // Releases the BufferConsumer, passing the release fence in |release_fence|
+ // to the producer. On success, the BufferConsumer and acquire fence are set
+ // to empty state; if release fails, the BufferConsumer and acquire fence are
+ // left in place and a negative error code is returned.
+ int Release(pdx::LocalHandle release_fence);
+
+ private:
+ AcquiredBuffer(const AcquiredBuffer&) = delete;
+ void operator=(const AcquiredBuffer&) = delete;
+
+ std::shared_ptr<BufferConsumer> buffer_;
+ // Mutable so that the fence can be closed when it is determined to be
+ // signaled during IsAvailable().
+ mutable pdx::LocalHandle acquire_fence_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
new file mode 100644
index 0000000000..a0b3efea7e
--- /dev/null
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -0,0 +1,155 @@
+#include "display_manager_service.h"
+
+#include <pdx/channel_handle.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/android_filesystem_config.h>
+#include <private/dvr/display_protocol.h>
+#include <private/dvr/trusted_uids.h>
+#include <sys/poll.h>
+
+#include <array>
+
+using android::dvr::display::DisplayManagerProtocol;
+using android::pdx::Channel;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Message;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::ErrorStatus;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::IfAnyOf;
+using android::pdx::rpc::RemoteMethodError;
+
+namespace android {
+namespace dvr {
+
+void DisplayManager::SetNotificationsPending(bool pending) {
+ auto status = service_->ModifyChannelEvents(channel_id_, pending ? 0 : POLLIN,
+ pending ? POLLIN : 0);
+ ALOGE_IF(!status,
+ "DisplayManager::SetNotificationPending: Failed to modify channel "
+ "events: %s",
+ status.GetErrorMessage().c_str());
+}
+
+DisplayManagerService::DisplayManagerService(
+ const std::shared_ptr<DisplayService>& display_service)
+ : BASE("DisplayManagerService",
+ Endpoint::Create(DisplayManagerProtocol::kClientPath)),
+ display_service_(display_service) {
+ display_service_->SetDisplayConfigurationUpdateNotifier(
+ std::bind(&DisplayManagerService::OnDisplaySurfaceChange, this));
+}
+
+std::shared_ptr<pdx::Channel> DisplayManagerService::OnChannelOpen(
+ pdx::Message& message) {
+ const int user_id = message.GetEffectiveUserId();
+ const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
+
+ // Prevent more than one display manager from registering at a time or
+ // untrusted UIDs from connecting.
+ if (display_manager_ || !trusted) {
+ RemoteMethodError(message, EPERM);
+ return nullptr;
+ }
+
+ display_manager_ =
+ std::make_shared<DisplayManager>(this, message.GetChannelId());
+ return display_manager_;
+}
+
+void DisplayManagerService::OnChannelClose(
+ pdx::Message& /*message*/, const std::shared_ptr<pdx::Channel>& channel) {
+ // Unregister the display manager when the channel closes.
+ if (display_manager_ == channel)
+ display_manager_ = nullptr;
+}
+
+pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) {
+ auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel());
+
+ switch (message.GetOp()) {
+ case DisplayManagerProtocol::GetSurfaceState::Opcode:
+ DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceState>(
+ *this, &DisplayManagerService::OnGetSurfaceState, message);
+ return {};
+
+ case DisplayManagerProtocol::GetSurfaceQueue::Opcode:
+ DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
+ *this, &DisplayManagerService::OnGetSurfaceQueue, message);
+ return {};
+
+ case DisplayManagerProtocol::SetupNamedBuffer::Opcode:
+ DispatchRemoteMethod<DisplayManagerProtocol::SetupNamedBuffer>(
+ *this, &DisplayManagerService::OnSetupNamedBuffer, message);
+ return {};
+
+ default:
+ return Service::DefaultHandleMessage(message);
+ }
+}
+
+pdx::Status<std::vector<display::SurfaceState>>
+DisplayManagerService::OnGetSurfaceState(pdx::Message& /*message*/) {
+ std::vector<display::SurfaceState> items;
+
+ display_service_->ForEachDisplaySurface(
+ SurfaceType::Application,
+ [&items](const std::shared_ptr<DisplaySurface>& surface) mutable {
+ items.push_back({surface->surface_id(), surface->process_id(),
+ surface->user_id(), surface->attributes(),
+ surface->update_flags(), surface->GetQueueIds()});
+ surface->ClearUpdate();
+ });
+
+ // The fact that we're in the message handler implies that display_manager_ is
+ // not nullptr. No check required, unless this service becomes multi-threaded.
+ display_manager_->SetNotificationsPending(false);
+ return items;
+}
+
+pdx::Status<pdx::LocalChannelHandle> DisplayManagerService::OnGetSurfaceQueue(
+ pdx::Message& /*message*/, int surface_id, int queue_id) {
+ auto surface = display_service_->GetDisplaySurface(surface_id);
+ if (!surface || surface->surface_type() != SurfaceType::Application)
+ return ErrorStatus(EINVAL);
+
+ auto queue =
+ std::static_pointer_cast<ApplicationDisplaySurface>(surface)->GetQueue(
+ queue_id);
+ if (!queue)
+ return ErrorStatus(EINVAL);
+
+ auto status = queue->CreateConsumerQueueHandle();
+ ALOGE_IF(
+ !status,
+ "DisplayManagerService::OnGetSurfaceQueue: Failed to create consumer "
+ "queue for queue_id=%d: %s",
+ queue->id(), status.GetErrorMessage().c_str());
+
+ return status;
+}
+
+pdx::Status<BorrowedNativeBufferHandle>
+DisplayManagerService::OnSetupNamedBuffer(pdx::Message& message,
+ const std::string& name, size_t size,
+ uint64_t usage) {
+ const int user_id = message.GetEffectiveUserId();
+ const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
+
+ if (!trusted) {
+ ALOGE(
+ "DisplayService::SetupNamedBuffer: Named buffers may only be created "
+ "by trusted UIDs: user_id=%d",
+ user_id);
+ return ErrorStatus(EPERM);
+ }
+ return display_service_->SetupNamedBuffer(name, size, usage);
+}
+
+void DisplayManagerService::OnDisplaySurfaceChange() {
+ if (display_manager_)
+ display_manager_->SetNotificationsPending(true);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
new file mode 100644
index 0000000000..0857eb5298
--- /dev/null
+++ b/libs/vr/libvrflinger/display_manager_service.h
@@ -0,0 +1,77 @@
+#ifndef ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
+#define ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
+
+#include <pdx/service.h>
+#include <pdx/status.h>
+#include <private/dvr/display_protocol.h>
+
+#include "display_service.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayManagerService;
+
+// The display manager is a client of the display manager service. This class
+// represents the connected client that the display manager service sends
+// notifications to.
+class DisplayManager : public pdx::Channel {
+ public:
+ DisplayManager(DisplayManagerService* service, int channel_id)
+ : service_(service), channel_id_(channel_id) {}
+
+ int channel_id() const { return channel_id_; }
+
+ // Sets or clears the channel event mask to indicate pending events that the
+ // display manager on the other end of the channel should read and handle.
+ // When |pending| is true the POLLIN bit is set in the event mask; when
+ // |pending| is false the POLLIN bit is cleared in the event mask.
+ void SetNotificationsPending(bool pending);
+
+ private:
+ DisplayManager(const DisplayManager&) = delete;
+ void operator=(const DisplayManager&) = delete;
+
+ DisplayManagerService* service_;
+ int channel_id_;
+};
+
+// The display manager service marshalls state and events from the display
+// service to the display manager.
+class DisplayManagerService : public pdx::ServiceBase<DisplayManagerService> {
+ public:
+ std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
+ void OnChannelClose(pdx::Message& message,
+ const std::shared_ptr<pdx::Channel>& channel) override;
+ pdx::Status<void> HandleMessage(pdx::Message& message) override;
+
+ private:
+ friend BASE;
+
+ explicit DisplayManagerService(
+ const std::shared_ptr<DisplayService>& display_service);
+
+ pdx::Status<std::vector<display::SurfaceState>> OnGetSurfaceState(
+ pdx::Message& message);
+ pdx::Status<pdx::LocalChannelHandle> OnGetSurfaceQueue(pdx::Message& message,
+ int surface_id,
+ int queue_id);
+ pdx::Status<BorrowedNativeBufferHandle> OnSetupNamedBuffer(
+ pdx::Message& message, const std::string& name, size_t size,
+ uint64_t usage);
+
+ // Called by the display service to indicate changes to display surfaces that
+ // the display manager should evaluate.
+ void OnDisplaySurfaceChange();
+
+ DisplayManagerService(const DisplayManagerService&) = delete;
+ void operator=(const DisplayManagerService&) = delete;
+
+ std::shared_ptr<DisplayService> display_service_;
+ std::shared_ptr<DisplayManager> display_manager_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
new file mode 100644
index 0000000000..47efa7653d
--- /dev/null
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -0,0 +1,265 @@
+#include "display_service.h"
+
+#include <unistd.h>
+#include <vector>
+
+#include <dvr/dvr_display_types.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/remote_method.h>
+#include <private/dvr/display_protocol.h>
+#include <private/dvr/numeric.h>
+#include <private/dvr/types.h>
+
+using android::dvr::display::DisplayProtocol;
+using android::pdx::Channel;
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::Status;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+DisplayService::DisplayService(Hwc2::Composer* hidl,
+ RequestDisplayCallback request_display_callback)
+ : BASE("DisplayService",
+ Endpoint::Create(display::DisplayProtocol::kClientPath)),
+ hardware_composer_(hidl, request_display_callback),
+ request_display_callback_(request_display_callback) {
+ hardware_composer_.Initialize();
+}
+
+bool DisplayService::IsInitialized() const {
+ return BASE::IsInitialized() && hardware_composer_.IsInitialized();
+}
+
+std::string DisplayService::DumpState(size_t /*max_length*/) {
+ return hardware_composer_.Dump();
+}
+
+void DisplayService::OnChannelClose(pdx::Message& message,
+ const std::shared_ptr<Channel>& channel) {
+ if (auto surface = std::static_pointer_cast<DisplaySurface>(channel)) {
+ surface->OnSetAttributes(message,
+ {{display::SurfaceAttribute::Visible,
+ display::SurfaceAttributeValue{false}}});
+ SurfaceUpdated(surface->surface_type(),
+ display::SurfaceUpdateFlags::VisibilityChanged);
+ }
+}
+
+// First-level dispatch for display service messages. Directly handles messages
+// that are independent of the display surface (metrics, creation) and routes
+// surface-specific messages to the per-instance handlers.
+Status<void> DisplayService::HandleMessage(pdx::Message& message) {
+ ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp());
+ switch (message.GetOp()) {
+ case DisplayProtocol::GetMetrics::Opcode:
+ DispatchRemoteMethod<DisplayProtocol::GetMetrics>(
+ *this, &DisplayService::OnGetMetrics, message);
+ return {};
+
+ case DisplayProtocol::CreateSurface::Opcode:
+ DispatchRemoteMethod<DisplayProtocol::CreateSurface>(
+ *this, &DisplayService::OnCreateSurface, message);
+ return {};
+
+ case DisplayProtocol::GetNamedBuffer::Opcode:
+ DispatchRemoteMethod<DisplayProtocol::GetNamedBuffer>(
+ *this, &DisplayService::OnGetNamedBuffer, message);
+ return {};
+
+ case DisplayProtocol::IsVrAppRunning::Opcode:
+ DispatchRemoteMethod<DisplayProtocol::IsVrAppRunning>(
+ *this, &DisplayService::IsVrAppRunning, message);
+ return {};
+
+ // Direct the surface specific messages to the surface instance.
+ case DisplayProtocol::SetAttributes::Opcode:
+ case DisplayProtocol::CreateQueue::Opcode:
+ case DisplayProtocol::GetSurfaceInfo::Opcode:
+ return HandleSurfaceMessage(message);
+
+ default:
+ return Service::HandleMessage(message);
+ }
+}
+
+Status<display::Metrics> DisplayService::OnGetMetrics(
+ pdx::Message& /*message*/) {
+ return {{static_cast<uint32_t>(GetDisplayMetrics().width),
+ static_cast<uint32_t>(GetDisplayMetrics().height),
+ static_cast<uint32_t>(GetDisplayMetrics().dpi.x),
+ static_cast<uint32_t>(GetDisplayMetrics().dpi.y),
+ static_cast<uint32_t>(
+ hardware_composer_.native_display_metrics().vsync_period_ns),
+ 0,
+ 0,
+ 0,
+ 0.0,
+ {},
+ {}}};
+}
+
+// Creates a new DisplaySurface and associates it with this channel. This may
+// only be done once per channel.
+Status<display::SurfaceInfo> DisplayService::OnCreateSurface(
+ pdx::Message& message, const display::SurfaceAttributes& attributes) {
+ // A surface may only be created once per channel.
+ if (message.GetChannel())
+ return ErrorStatus(EINVAL);
+
+ ALOGI_IF(TRACE, "DisplayService::OnCreateSurface: cid=%d",
+ message.GetChannelId());
+
+ // Use the channel id as the unique surface id.
+ const int surface_id = message.GetChannelId();
+ const int process_id = message.GetProcessId();
+ const int user_id = message.GetEffectiveUserId();
+
+ ALOGI_IF(TRACE,
+ "DisplayService::OnCreateSurface: surface_id=%d process_id=%d",
+ surface_id, process_id);
+
+ auto surface_status =
+ DisplaySurface::Create(this, surface_id, process_id, user_id, attributes);
+ if (!surface_status) {
+ ALOGE("DisplayService::OnCreateSurface: Failed to create surface: %s",
+ surface_status.GetErrorMessage().c_str());
+ return ErrorStatus(surface_status.error());
+ }
+
+ SurfaceType surface_type = surface_status.get()->surface_type();
+ display::SurfaceUpdateFlags update_flags =
+ surface_status.get()->update_flags();
+ display::SurfaceInfo surface_info{surface_status.get()->surface_id(),
+ surface_status.get()->visible(),
+ surface_status.get()->z_order()};
+
+ message.SetChannel(surface_status.take());
+
+ SurfaceUpdated(surface_type, update_flags);
+ return {surface_info};
+}
+
+void DisplayService::SurfaceUpdated(SurfaceType surface_type,
+ display::SurfaceUpdateFlags update_flags) {
+ ALOGD_IF(TRACE, "DisplayService::SurfaceUpdated: update_flags=%x",
+ update_flags.value());
+ if (update_flags.value() != 0) {
+ if (surface_type == SurfaceType::Application)
+ NotifyDisplayConfigurationUpdate();
+ else
+ UpdateActiveDisplaySurfaces();
+ }
+}
+
+pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnGetNamedBuffer(
+ pdx::Message& /* message */, const std::string& name) {
+ ALOGD_IF(TRACE, "displayService::OnGetNamedBuffer: name=%s", name.c_str());
+ auto named_buffer = named_buffers_.find(name);
+ if (named_buffer != named_buffers_.end())
+ return {BorrowedNativeBufferHandle(*named_buffer->second, 0)};
+ else
+ return pdx::ErrorStatus(EINVAL);
+}
+
+// Calls the message handler for the DisplaySurface associated with this
+// channel.
+Status<void> DisplayService::HandleSurfaceMessage(pdx::Message& message) {
+ auto surface = std::static_pointer_cast<DisplaySurface>(message.GetChannel());
+ ALOGW_IF(!surface,
+ "DisplayService::HandleSurfaceMessage: surface is nullptr!");
+
+ if (surface)
+ return surface->HandleMessage(message);
+ else
+ return ErrorStatus(EINVAL);
+}
+
+std::shared_ptr<DisplaySurface> DisplayService::GetDisplaySurface(
+ int surface_id) const {
+ return std::static_pointer_cast<DisplaySurface>(GetChannel(surface_id));
+}
+
+std::vector<std::shared_ptr<DisplaySurface>>
+DisplayService::GetDisplaySurfaces() const {
+ return GetChannels<DisplaySurface>();
+}
+
+std::vector<std::shared_ptr<DirectDisplaySurface>>
+DisplayService::GetVisibleDisplaySurfaces() const {
+ std::vector<std::shared_ptr<DirectDisplaySurface>> visible_surfaces;
+
+ ForEachDisplaySurface(
+ SurfaceType::Direct,
+ [&](const std::shared_ptr<DisplaySurface>& surface) mutable {
+ if (surface->visible()) {
+ visible_surfaces.push_back(
+ std::static_pointer_cast<DirectDisplaySurface>(surface));
+ surface->ClearUpdate();
+ }
+ });
+
+ return visible_surfaces;
+}
+
+void DisplayService::UpdateActiveDisplaySurfaces() {
+ auto visible_surfaces = GetVisibleDisplaySurfaces();
+
+ std::sort(visible_surfaces.begin(), visible_surfaces.end(),
+ [](const std::shared_ptr<DisplaySurface>& a,
+ const std::shared_ptr<DisplaySurface>& b) {
+ return a->z_order() < b->z_order();
+ });
+
+ ALOGD_IF(TRACE,
+ "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
+ visible_surfaces.size());
+
+ hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
+}
+
+pdx::Status<BorrowedNativeBufferHandle> DisplayService::SetupNamedBuffer(
+ const std::string& name, size_t size, uint64_t usage) {
+ auto named_buffer = named_buffers_.find(name);
+ if (named_buffer == named_buffers_.end()) {
+ auto ion_buffer = std::make_unique<IonBuffer>(static_cast<int>(size), 1,
+ HAL_PIXEL_FORMAT_BLOB, usage);
+ named_buffer =
+ named_buffers_.insert(std::make_pair(name, std::move(ion_buffer)))
+ .first;
+ }
+
+ return {BorrowedNativeBufferHandle(*named_buffer->second, 0)};
+}
+
+void DisplayService::OnHardwareComposerRefresh() {
+ hardware_composer_.OnHardwareComposerRefresh();
+}
+
+void DisplayService::SetDisplayConfigurationUpdateNotifier(
+ DisplayConfigurationUpdateNotifier update_notifier) {
+ update_notifier_ = update_notifier;
+}
+
+void DisplayService::NotifyDisplayConfigurationUpdate() {
+ if (update_notifier_)
+ update_notifier_();
+}
+
+Status<bool> DisplayService::IsVrAppRunning(pdx::Message& /*message*/) {
+ bool visible = false;
+ ForEachDisplaySurface(
+ SurfaceType::Application,
+ [&visible](const std::shared_ptr<DisplaySurface>& surface) {
+ if (surface->visible())
+ visible = true;
+ });
+
+ return {visible};
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
new file mode 100644
index 0000000000..bb4eeef1c6
--- /dev/null
+++ b/libs/vr/libvrflinger/display_service.h
@@ -0,0 +1,125 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
+
+#include <pdx/service.h>
+#include <pdx/status.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/display_protocol.h>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "acquired_buffer.h"
+#include "display_surface.h"
+#include "epoll_event_dispatcher.h"
+#include "hardware_composer.h"
+
+namespace android {
+namespace dvr {
+
+// DisplayService implements the display service component of VrFlinger.
+class DisplayService : public pdx::ServiceBase<DisplayService> {
+ public:
+ bool IsInitialized() const override;
+ std::string DumpState(size_t max_length) override;
+
+ void OnChannelClose(pdx::Message& message,
+ const std::shared_ptr<pdx::Channel>& channel) override;
+ pdx::Status<void> HandleMessage(pdx::Message& message) override;
+
+ std::shared_ptr<DisplaySurface> GetDisplaySurface(int surface_id) const;
+ std::vector<std::shared_ptr<DisplaySurface>> GetDisplaySurfaces() const;
+ std::vector<std::shared_ptr<DirectDisplaySurface>> GetVisibleDisplaySurfaces()
+ const;
+
+ // Updates the list of actively displayed surfaces. This must be called after
+ // any change to client/manager attributes that affect visibility or z order.
+ void UpdateActiveDisplaySurfaces();
+
+ pdx::Status<BorrowedNativeBufferHandle> SetupNamedBuffer(
+ const std::string& name, size_t size, uint64_t usage);
+
+ template <class A>
+ void ForEachDisplaySurface(SurfaceType surface_type, A action) const {
+ ForEachChannel([surface_type,
+ action](const ChannelIterator::value_type& pair) mutable {
+ auto surface = std::static_pointer_cast<DisplaySurface>(pair.second);
+ if (surface->surface_type() == surface_type)
+ action(surface);
+ });
+ }
+
+ using DisplayConfigurationUpdateNotifier = std::function<void(void)>;
+ void SetDisplayConfigurationUpdateNotifier(
+ DisplayConfigurationUpdateNotifier notifier);
+
+ using VSyncCallback = HardwareComposer::VSyncCallback;
+ void SetVSyncCallback(VSyncCallback callback) {
+ hardware_composer_.SetVSyncCallback(callback);
+ }
+
+ HWCDisplayMetrics GetDisplayMetrics() {
+ return hardware_composer_.display_metrics();
+ }
+
+ void GrantDisplayOwnership() { hardware_composer_.Enable(); }
+ void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
+
+ void OnHardwareComposerRefresh();
+
+ private:
+ friend BASE;
+ friend DisplaySurface;
+
+ friend class VrDisplayStateService;
+
+ using RequestDisplayCallback = std::function<void(bool)>;
+
+ DisplayService(android::Hwc2::Composer* hidl,
+ RequestDisplayCallback request_display_callback);
+
+ pdx::Status<BorrowedNativeBufferHandle> OnGetNamedBuffer(
+ pdx::Message& message, const std::string& name);
+ pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message);
+ pdx::Status<display::SurfaceInfo> OnCreateSurface(
+ pdx::Message& message, const display::SurfaceAttributes& attributes);
+
+ // Temporary query for current VR status. Will be removed later.
+ pdx::Status<bool> IsVrAppRunning(pdx::Message& message);
+
+ pdx::Status<void> AddEventHandler(int fd, int events,
+ EpollEventDispatcher::Handler handler) {
+ return dispatcher_.AddEventHandler(fd, events, handler);
+ }
+ pdx::Status<void> RemoveEventHandler(int fd) {
+ return dispatcher_.RemoveEventHandler(fd);
+ }
+
+ void SurfaceUpdated(SurfaceType surface_type,
+ display::SurfaceUpdateFlags update_flags);
+
+ // Called by DisplaySurface to signal that a surface property has changed and
+ // the display manager should be notified.
+ void NotifyDisplayConfigurationUpdate();
+
+ pdx::Status<void> HandleSurfaceMessage(pdx::Message& message);
+
+ HardwareComposer hardware_composer_;
+ RequestDisplayCallback request_display_callback_;
+ EpollEventDispatcher dispatcher_;
+ DisplayConfigurationUpdateNotifier update_notifier_;
+
+ std::unordered_map<std::string, std::unique_ptr<IonBuffer>> named_buffers_;
+
+ DisplayService(const DisplayService&) = delete;
+ void operator=(const DisplayService&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
new file mode 100644
index 0000000000..fb2751b77d
--- /dev/null
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -0,0 +1,457 @@
+#include "display_surface.h"
+
+#include <private/android_filesystem_config.h>
+#include <utils/Trace.h>
+
+#include <private/dvr/trusted_uids.h>
+
+#include "display_service.h"
+#include "hardware_composer.h"
+
+#define LOCAL_TRACE 1
+
+using android::dvr::display::DisplayProtocol;
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::ErrorStatus;
+using android::pdx::LocalChannelHandle;
+using android::pdx::LocalHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::IfAnyOf;
+
+namespace android {
+namespace dvr {
+
+DisplaySurface::DisplaySurface(DisplayService* service,
+ SurfaceType surface_type, int surface_id,
+ int process_id, int user_id,
+ const display::SurfaceAttributes& attributes)
+ : service_(service),
+ surface_type_(surface_type),
+ surface_id_(surface_id),
+ process_id_(process_id),
+ user_id_(user_id),
+ attributes_(attributes),
+ update_flags_(display::SurfaceUpdateFlags::NewSurface) {}
+
+DisplaySurface::~DisplaySurface() {
+ ALOGD_IF(LOCAL_TRACE,
+ "DisplaySurface::~DisplaySurface: surface_id=%d process_id=%d",
+ surface_id(), process_id());
+}
+
+Status<void> DisplaySurface::HandleMessage(pdx::Message& message) {
+ switch (message.GetOp()) {
+ case DisplayProtocol::SetAttributes::Opcode:
+ DispatchRemoteMethod<DisplayProtocol::SetAttributes>(
+ *this, &DisplaySurface::OnSetAttributes, message);
+ break;
+
+ case DisplayProtocol::GetSurfaceInfo::Opcode:
+ DispatchRemoteMethod<DisplayProtocol::GetSurfaceInfo>(
+ *this, &DisplaySurface::OnGetSurfaceInfo, message);
+ break;
+
+ case DisplayProtocol::CreateQueue::Opcode:
+ DispatchRemoteMethod<DisplayProtocol::CreateQueue>(
+ *this, &DisplaySurface::OnCreateQueue, message);
+ break;
+ }
+
+ return {};
+}
+
+Status<void> DisplaySurface::OnSetAttributes(
+ pdx::Message& /*message*/, const display::SurfaceAttributes& attributes) {
+ display::SurfaceUpdateFlags update_flags;
+
+ for (const auto& attribute : attributes) {
+ const auto& key = attribute.first;
+ const auto* variant = &attribute.second;
+ bool invalid_value = false;
+ bool visibility_changed = false;
+
+ // Catch attributes that have significance to the display service.
+ switch (key) {
+ case display::SurfaceAttribute::ZOrder:
+ invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
+ variant, [&](const auto& value) {
+ if (z_order_ != value) {
+ visibility_changed = true;
+ z_order_ = value;
+ }
+ });
+ break;
+ case display::SurfaceAttribute::Visible:
+ invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+ variant, [&](const auto& value) {
+ if (visible_ != value) {
+ visibility_changed = true;
+ visible_ = value;
+ }
+ });
+ break;
+ }
+
+ if (invalid_value) {
+ ALOGW(
+ "DisplaySurface::OnClientSetAttributes: Failed to set display "
+ "surface attribute '%d' because of incompatible type: %d",
+ key, variant->index());
+ } else {
+ // Only update the attribute map with valid values.
+ attributes_[attribute.first] = attribute.second;
+
+ // All attribute changes generate a notification, even if the value
+ // doesn't change. Visibility attributes set a flag only if the value
+ // changes.
+ update_flags.Set(display::SurfaceUpdateFlags::AttributesChanged);
+ if (visibility_changed)
+ update_flags.Set(display::SurfaceUpdateFlags::VisibilityChanged);
+ }
+ }
+
+ SurfaceUpdated(update_flags);
+ return {};
+}
+
+void DisplaySurface::SurfaceUpdated(display::SurfaceUpdateFlags update_flags) {
+ ALOGD_IF(TRACE,
+ "DisplaySurface::SurfaceUpdated: surface_id=%d update_flags=0x%x",
+ surface_id(), update_flags.value());
+
+ update_flags_.Set(update_flags);
+ service()->SurfaceUpdated(surface_type(), update_flags_);
+}
+
+void DisplaySurface::ClearUpdate() {
+ ALOGD_IF(TRACE, "DisplaySurface::ClearUpdate: surface_id=%d", surface_id());
+ update_flags_ = display::SurfaceUpdateFlags::None;
+}
+
+Status<display::SurfaceInfo> DisplaySurface::OnGetSurfaceInfo(
+ Message& /*message*/) {
+ ALOGD_IF(
+ TRACE,
+ "DisplaySurface::OnGetSurfaceInfo: surface_id=%d visible=%d z_order=%d",
+ surface_id(), visible(), z_order());
+ return {{surface_id(), visible(), z_order()}};
+}
+
+Status<void> DisplaySurface::RegisterQueue(
+ const std::shared_ptr<ConsumerQueue>& consumer_queue) {
+ ALOGD_IF(TRACE, "DisplaySurface::RegisterQueue: surface_id=%d queue_id=%d",
+ surface_id(), consumer_queue->id());
+ // Capture references for the lambda to work around apparent clang bug.
+ // TODO(eieio): Figure out if there is a clang bug or C++11 ambiguity when
+ // capturing self and consumer_queue by copy in the following case:
+ // auto self = Self();
+ // [self, consumer_queue](int events) {
+ // self->OnQueueEvent(consuemr_queue, events); }
+ //
+ struct State {
+ std::shared_ptr<DisplaySurface> surface;
+ std::shared_ptr<ConsumerQueue> queue;
+ };
+ State state{Self(), consumer_queue};
+
+ return service()->AddEventHandler(
+ consumer_queue->queue_fd(), EPOLLIN | EPOLLHUP | EPOLLET,
+ [state](int events) {
+ state.surface->OnQueueEvent(state.queue, events);
+ });
+}
+
+Status<void> DisplaySurface::UnregisterQueue(
+ const std::shared_ptr<ConsumerQueue>& consumer_queue) {
+ ALOGD_IF(TRACE, "DisplaySurface::UnregisterQueue: surface_id=%d queue_id=%d",
+ surface_id(), consumer_queue->id());
+ return service()->RemoveEventHandler(consumer_queue->queue_fd());
+}
+
+void DisplaySurface::OnQueueEvent(
+ const std::shared_ptr<ConsumerQueue>& /*consumer_queue*/, int /*events*/) {
+ ALOGE(
+ "DisplaySurface::OnQueueEvent: ERROR base virtual method should not be "
+ "called!!!");
+}
+
+std::shared_ptr<ConsumerQueue> ApplicationDisplaySurface::GetQueue(
+ int32_t queue_id) {
+ ALOGD_IF(TRACE,
+ "ApplicationDisplaySurface::GetQueue: surface_id=%d queue_id=%d",
+ surface_id(), queue_id);
+
+ auto search = consumer_queues_.find(queue_id);
+ if (search != consumer_queues_.end())
+ return search->second;
+ else
+ return nullptr;
+}
+
+std::vector<int32_t> ApplicationDisplaySurface::GetQueueIds() const {
+ std::vector<int32_t> queue_ids;
+ for (const auto& entry : consumer_queues_)
+ queue_ids.push_back(entry.first);
+ return queue_ids;
+}
+
+Status<LocalChannelHandle> ApplicationDisplaySurface::OnCreateQueue(
+ Message& /*message*/, size_t meta_size_bytes) {
+ ATRACE_NAME("ApplicationDisplaySurface::OnCreateQueue");
+ ALOGD_IF(TRACE,
+ "ApplicationDisplaySurface::OnCreateQueue: surface_id=%d, "
+ "meta_size_bytes=%zu",
+ surface_id(), meta_size_bytes);
+
+ std::lock_guard<std::mutex> autolock(lock_);
+ auto producer = ProducerQueue::Create(meta_size_bytes);
+ if (!producer) {
+ ALOGE(
+ "ApplicationDisplaySurface::OnCreateQueue: Failed to create producer "
+ "queue!");
+ return ErrorStatus(ENOMEM);
+ }
+
+ std::shared_ptr<ConsumerQueue> consumer =
+ producer->CreateSilentConsumerQueue();
+ auto status = RegisterQueue(consumer);
+ if (!status) {
+ ALOGE(
+ "ApplicationDisplaySurface::OnCreateQueue: Failed to register consumer "
+ "queue: %s",
+ status.GetErrorMessage().c_str());
+ return status.error_status();
+ }
+
+ consumer_queues_[consumer->id()] = std::move(consumer);
+
+ SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
+ return std::move(producer->GetChannelHandle());
+}
+
+void ApplicationDisplaySurface::OnQueueEvent(
+ const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
+ ALOGD_IF(TRACE,
+ "ApplicationDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
+ consumer_queue->id(), events);
+
+ // Always give the queue a chance to handle its internal bookkeeping.
+ consumer_queue->HandleQueueEvents();
+
+ // Check for hangup and remove a queue that is no longer needed.
+ std::lock_guard<std::mutex> autolock(lock_);
+ if (consumer_queue->hung_up()) {
+ ALOGD_IF(TRACE, "ApplicationDisplaySurface::OnQueueEvent: Removing queue.");
+ UnregisterQueue(consumer_queue);
+ auto search = consumer_queues_.find(consumer_queue->id());
+ if (search != consumer_queues_.end()) {
+ consumer_queues_.erase(search);
+ } else {
+ ALOGE(
+ "ApplicationDisplaySurface::OnQueueEvent: Failed to find queue_id=%d",
+ consumer_queue->id());
+ }
+ SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
+ }
+}
+
+Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue(
+ Message& /*message*/, size_t meta_size_bytes) {
+ ATRACE_NAME("DirectDisplaySurface::OnCreateQueue");
+ ALOGD_IF(
+ TRACE,
+ "DirectDisplaySurface::OnCreateQueue: surface_id=%d meta_size_bytes=%zu",
+ surface_id(), meta_size_bytes);
+
+ std::lock_guard<std::mutex> autolock(lock_);
+ if (!direct_queue_) {
+ auto producer = ProducerQueue::Create(meta_size_bytes);
+ if (!producer) {
+ ALOGE(
+ "DirectDisplaySurface::OnCreateQueue: Failed to create producer "
+ "queue!");
+ return ErrorStatus(ENOMEM);
+ }
+
+ direct_queue_ = producer->CreateConsumerQueue();
+ auto status = RegisterQueue(direct_queue_);
+ if (!status) {
+ ALOGE(
+ "DirectDisplaySurface::OnCreateQueue: Failed to register consumer "
+ "queue: %s",
+ status.GetErrorMessage().c_str());
+ return status.error_status();
+ }
+
+ return std::move(producer->GetChannelHandle());
+ } else {
+ return ErrorStatus(EALREADY);
+ }
+}
+
+void DirectDisplaySurface::OnQueueEvent(
+ const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
+ ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
+ consumer_queue->id(), events);
+
+ // Always give the queue a chance to handle its internal bookkeeping.
+ consumer_queue->HandleQueueEvents();
+
+ // Check for hangup and remove a queue that is no longer needed.
+ std::lock_guard<std::mutex> autolock(lock_);
+ if (consumer_queue->hung_up()) {
+ ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: Removing queue.");
+ UnregisterQueue(consumer_queue);
+ direct_queue_ = nullptr;
+ }
+}
+
+void DirectDisplaySurface::DequeueBuffersLocked() {
+ if (direct_queue_ == nullptr) {
+ ALOGE(
+ "DirectDisplaySurface::DequeueBuffersLocked: Consumer queue is not "
+ "initialized.");
+ return;
+ }
+
+ while (true) {
+ LocalHandle acquire_fence;
+ size_t slot;
+ auto buffer_status = direct_queue_->Dequeue(0, &slot, &acquire_fence);
+ if (!buffer_status) {
+ ALOGD_IF(
+ TRACE && buffer_status.error() == ETIMEDOUT,
+ "DirectDisplaySurface::DequeueBuffersLocked: All buffers dequeued.");
+ ALOGE_IF(buffer_status.error() != ETIMEDOUT,
+ "DirectDisplaySurface::DequeueBuffersLocked: Failed to dequeue "
+ "buffer: %s",
+ buffer_status.GetErrorMessage().c_str());
+ return;
+ }
+ auto buffer_consumer = buffer_status.take();
+
+ if (!visible()) {
+ ATRACE_NAME("DropFrameOnInvisibleSurface");
+ ALOGD_IF(TRACE,
+ "DirectDisplaySurface::DequeueBuffersLocked: Discarding "
+ "buffer_id=%d on invisible surface.",
+ buffer_consumer->id());
+ buffer_consumer->Discard();
+ continue;
+ }
+
+ if (acquired_buffers_.IsFull()) {
+ ALOGE(
+ "DirectDisplaySurface::DequeueBuffersLocked: Posted buffers full, "
+ "overwriting.");
+ acquired_buffers_.PopBack();
+ }
+
+ acquired_buffers_.Append(
+ AcquiredBuffer(buffer_consumer, std::move(acquire_fence)));
+ }
+}
+
+AcquiredBuffer DirectDisplaySurface::AcquireCurrentBuffer() {
+ std::lock_guard<std::mutex> autolock(lock_);
+ DequeueBuffersLocked();
+
+ if (acquired_buffers_.IsEmpty()) {
+ ALOGE(
+ "DirectDisplaySurface::AcquireCurrentBuffer: attempt to acquire buffer "
+ "when none are posted.");
+ return AcquiredBuffer();
+ }
+ AcquiredBuffer buffer = std::move(acquired_buffers_.Front());
+ acquired_buffers_.PopFront();
+ ALOGD_IF(TRACE, "DirectDisplaySurface::AcquireCurrentBuffer: buffer: %p",
+ buffer.buffer().get());
+ return buffer;
+}
+
+AcquiredBuffer DirectDisplaySurface::AcquireNewestAvailableBuffer(
+ AcquiredBuffer* skipped_buffer) {
+ std::lock_guard<std::mutex> autolock(lock_);
+ DequeueBuffersLocked();
+
+ AcquiredBuffer buffer;
+ int frames = 0;
+ // Basic latency stopgap for when the application misses a frame:
+ // If the application recovers on the 2nd or 3rd (etc) frame after
+ // missing, this code will skip frames to catch up by checking if
+ // the next frame is also available.
+ while (!acquired_buffers_.IsEmpty() &&
+ acquired_buffers_.Front().IsAvailable()) {
+ // Capture the skipped buffer into the result parameter.
+ // Note that this API only supports skipping one buffer per vsync.
+ if (frames > 0 && skipped_buffer)
+ *skipped_buffer = std::move(buffer);
+ ++frames;
+ buffer = std::move(acquired_buffers_.Front());
+ acquired_buffers_.PopFront();
+ if (frames == 2)
+ break;
+ }
+ ALOGD_IF(TRACE,
+ "DirectDisplaySurface::AcquireNewestAvailableBuffer: buffer: %p",
+ buffer.buffer().get());
+ return buffer;
+}
+
+bool DirectDisplaySurface::IsBufferAvailable() {
+ std::lock_guard<std::mutex> autolock(lock_);
+ DequeueBuffersLocked();
+
+ return !acquired_buffers_.IsEmpty() &&
+ acquired_buffers_.Front().IsAvailable();
+}
+
+bool DirectDisplaySurface::IsBufferPosted() {
+ std::lock_guard<std::mutex> autolock(lock_);
+ DequeueBuffersLocked();
+
+ return !acquired_buffers_.IsEmpty();
+}
+
+Status<std::shared_ptr<DisplaySurface>> DisplaySurface::Create(
+ DisplayService* service, int surface_id, int process_id, int user_id,
+ const display::SurfaceAttributes& attributes) {
+ bool direct = false;
+ auto search = attributes.find(display::SurfaceAttribute::Direct);
+ if (search != attributes.end()) {
+ if (!IfAnyOf<int32_t, int64_t, bool, float>::Get(&search->second,
+ &direct)) {
+ ALOGE(
+ "DisplaySurface::Create: Invalid type for SurfaceAttribute::Direct!");
+ return ErrorStatus(EINVAL);
+ }
+ }
+
+ ALOGD_IF(TRACE,
+ "DisplaySurface::Create: surface_id=%d process_id=%d user_id=%d "
+ "direct=%d",
+ surface_id, process_id, user_id, direct);
+
+ if (direct) {
+ const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
+ if (trusted) {
+ return {std::shared_ptr<DisplaySurface>{new DirectDisplaySurface(
+ service, surface_id, process_id, user_id, attributes)}};
+ } else {
+ ALOGE(
+ "DisplaySurface::Create: Direct surfaces may only be created by "
+ "trusted UIDs: user_id=%d",
+ user_id);
+ return ErrorStatus(EPERM);
+ }
+ } else {
+ return {std::shared_ptr<DisplaySurface>{new ApplicationDisplaySurface(
+ service, surface_id, process_id, user_id, attributes)}};
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
new file mode 100644
index 0000000000..c456b1050f
--- /dev/null
+++ b/libs/vr/libvrflinger/display_surface.h
@@ -0,0 +1,185 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
+
+#include <pdx/file_handle.h>
+#include <pdx/service.h>
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_protocol.h>
+#include <private/dvr/ring_buffer.h>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "acquired_buffer.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayService;
+
+enum class SurfaceType {
+ Direct,
+ Application,
+};
+
+class DisplaySurface : public pdx::Channel {
+ public:
+ static pdx::Status<std::shared_ptr<DisplaySurface>> Create(
+ DisplayService* service, int surface_id, int process_id, int user_id,
+ const display::SurfaceAttributes& attributes);
+
+ ~DisplaySurface() override;
+
+ DisplayService* service() const { return service_; }
+ SurfaceType surface_type() const { return surface_type_; }
+ int surface_id() const { return surface_id_; }
+ int process_id() const { return process_id_; }
+ int user_id() const { return user_id_; }
+
+ bool visible() const { return visible_; }
+ int z_order() const { return z_order_; }
+
+ const display::SurfaceAttributes& attributes() const { return attributes_; }
+ display::SurfaceUpdateFlags update_flags() const { return update_flags_; }
+
+ virtual std::vector<int32_t> GetQueueIds() const { return {}; }
+
+ bool IsUpdatePending() const {
+ return update_flags_.value() != display::SurfaceUpdateFlags::None;
+ }
+
+ protected:
+ DisplaySurface(DisplayService* service, SurfaceType surface_type,
+ int surface_id, int process_id, int user_id,
+ const display::SurfaceAttributes& attributes);
+
+ // Utility to retrieve a shared pointer to this channel as the desired derived
+ // type.
+ template <
+ typename T = DisplaySurface,
+ typename = std::enable_if_t<std::is_base_of<DisplaySurface, T>::value>>
+ std::shared_ptr<T> Self() {
+ return std::static_pointer_cast<T>(shared_from_this());
+ }
+
+ virtual pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
+ pdx::Message& message, size_t meta_size_bytes) = 0;
+
+ // Registers a consumer queue with the event dispatcher in DisplayService. The
+ // OnQueueEvent callback below is called to handle queue events.
+ pdx::Status<void> RegisterQueue(
+ const std::shared_ptr<ConsumerQueue>& consumer_queue);
+ pdx::Status<void> UnregisterQueue(
+ const std::shared_ptr<ConsumerQueue>& consumer_queue);
+
+ // Called by the event dispatcher in DisplayService when a registered queue
+ // event triggers. Executes on the event dispatcher thread.
+ virtual void OnQueueEvent(
+ const std::shared_ptr<ConsumerQueue>& consumer_queue, int events);
+
+ void SurfaceUpdated(display::SurfaceUpdateFlags update_flags);
+ void ClearUpdate();
+
+ // Synchronizes access to mutable state below between message dispatch thread
+ // and frame post thread.
+ mutable std::mutex lock_;
+
+ private:
+ friend class DisplayService;
+ friend class DisplayManagerService;
+
+ // Dispatches display surface messages to the appropriate handlers. This
+ // handler runs on the VrFlinger message dispatch thread.
+ pdx::Status<void> HandleMessage(pdx::Message& message);
+
+ pdx::Status<void> OnSetAttributes(
+ pdx::Message& message, const display::SurfaceAttributes& attributes);
+ pdx::Status<display::SurfaceInfo> OnGetSurfaceInfo(pdx::Message& message);
+
+ DisplayService* service_;
+ SurfaceType surface_type_;
+ int surface_id_;
+ int process_id_;
+ int user_id_;
+
+ display::SurfaceAttributes attributes_;
+ display::SurfaceUpdateFlags update_flags_ = display::SurfaceUpdateFlags::None;
+
+ // Subset of attributes that may be interpreted by the display service.
+ bool visible_ = false;
+ int z_order_ = 0;
+
+ DisplaySurface(const DisplaySurface&) = delete;
+ void operator=(const DisplaySurface&) = delete;
+};
+
+class ApplicationDisplaySurface : public DisplaySurface {
+ public:
+ ApplicationDisplaySurface(DisplayService* service, int surface_id,
+ int process_id, int user_id,
+ const display::SurfaceAttributes& attributes)
+ : DisplaySurface(service, SurfaceType::Application, surface_id,
+ process_id, user_id, attributes) {}
+
+ std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id);
+ std::vector<int32_t> GetQueueIds() const override;
+
+ private:
+ pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
+ pdx::Message& message, size_t meta_size_bytes) override;
+ void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
+ int events) override;
+
+ std::unordered_map<int32_t, std::shared_ptr<ConsumerQueue>> consumer_queues_;
+};
+
+class DirectDisplaySurface : public DisplaySurface {
+ public:
+ DirectDisplaySurface(DisplayService* service, int surface_id, int process_id,
+ int user_id,
+ const display::SurfaceAttributes& attributes)
+ : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id,
+ user_id, attributes),
+ acquired_buffers_(kMaxPostedBuffers) {}
+ bool IsBufferAvailable();
+ bool IsBufferPosted();
+ AcquiredBuffer AcquireCurrentBuffer();
+
+ // Get the newest buffer. Up to one buffer will be skipped. If a buffer is
+ // skipped, it will be stored in skipped_buffer if non null.
+ AcquiredBuffer AcquireNewestAvailableBuffer(AcquiredBuffer* skipped_buffer);
+
+ private:
+ pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
+ pdx::Message& message, size_t meta_size_bytes) override;
+ void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
+ int events) override;
+
+ // The capacity of the pending buffer queue. Should be enough to hold all the
+ // buffers of this DisplaySurface, although in practice only 1 or 2 frames
+ // will be pending at a time.
+ static constexpr int kSurfaceBufferMaxCount = 4;
+ static constexpr int kSurfaceViewMaxCount = 4;
+ static constexpr int kMaxPostedBuffers =
+ kSurfaceBufferMaxCount * kSurfaceViewMaxCount;
+
+ // Returns whether a frame is available without locking the mutex.
+ bool IsFrameAvailableNoLock() const;
+
+ // Dequeue all available buffers from the consumer queue.
+ void DequeueBuffersLocked();
+
+ // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be
+ // posted and pending.
+ RingBuffer<AcquiredBuffer> acquired_buffers_;
+
+ std::shared_ptr<ConsumerQueue> direct_queue_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
new file mode 100644
index 0000000000..06b69bbc26
--- /dev/null
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
@@ -0,0 +1,142 @@
+#include "epoll_event_dispatcher.h"
+
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/prctl.h>
+
+#include <dvr/performance_client_api.h>
+
+namespace android {
+namespace dvr {
+
+EpollEventDispatcher::EpollEventDispatcher() {
+ epoll_fd_.Reset(epoll_create(64));
+ if (!epoll_fd_) {
+ ALOGE("Failed to create epoll fd: %s", strerror(errno));
+ return;
+ }
+
+ event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ if (!event_fd_) {
+ ALOGE("Failed to create event for epolling: %s", strerror(errno));
+ return;
+ }
+
+ // Add watch for eventfd. This should only watch for EPOLLIN, which gets set
+ // when eventfd_write occurs. Use "this" as a unique sentinal value to
+ // identify events from the event fd.
+ epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}};
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
+ ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno));
+ return;
+ }
+
+ thread_ = std::thread(&EpollEventDispatcher::EventThread, this);
+}
+
+EpollEventDispatcher::~EpollEventDispatcher() { Stop(); }
+
+void EpollEventDispatcher::Stop() {
+ exit_thread_.store(true);
+ eventfd_write(event_fd_.Get(), 1);
+}
+
+pdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask,
+ Handler handler) {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ epoll_event event;
+ event.events = event_mask;
+ event.data.ptr = &(handlers_[fd] = handler);
+
+ ALOGD_IF(
+ TRACE,
+ "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p",
+ fd, event_mask, event.data.ptr);
+
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) {
+ const int error = errno;
+ ALOGE("Failed to add fd to epoll set because: %s", strerror(error));
+ return pdx::ErrorStatus(error);
+ } else {
+ return {};
+ }
+}
+
+pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) {
+ ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
+ std::lock_guard<std::mutex> lock(lock_);
+
+ epoll_event dummy; // See BUGS in man 2 epoll_ctl.
+ if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &dummy) < 0) {
+ const int error = errno;
+ ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
+ return pdx::ErrorStatus(error);
+ }
+
+ // If the fd was valid above, add it to the list of ids to remove.
+ removed_handlers_.push_back(fd);
+
+ // Wake up the event thread to clean up.
+ eventfd_write(event_fd_.Get(), 1);
+
+ return {};
+}
+
+void EpollEventDispatcher::EventThread() {
+ prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0);
+
+ const int error = dvrSetSchedulerClass(0, "graphics");
+ LOG_ALWAYS_FATAL_IF(
+ error < 0,
+ "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s",
+ strerror(-error));
+
+ const size_t kMaxNumEvents = 128;
+ epoll_event events[kMaxNumEvents];
+
+ while (!exit_thread_.load()) {
+ const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1);
+ if (num_events < 0 && errno != EINTR)
+ break;
+
+ ALOGD_IF(TRACE, "EpollEventDispatcher::EventThread: num_events=%d",
+ num_events);
+
+ for (int i = 0; i < num_events; i++) {
+ ALOGD_IF(
+ TRACE,
+ "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x",
+ i, events[i].data.ptr, events[i].events);
+
+ if (events[i].data.ptr == this) {
+ // Clear pending event on event_fd_. Serialize the read with respect to
+ // writes from other threads.
+ std::lock_guard<std::mutex> lock(lock_);
+ eventfd_t value;
+ eventfd_read(event_fd_.Get(), &value);
+ } else {
+ auto handler = reinterpret_cast<Handler*>(events[i].data.ptr);
+ if (handler)
+ (*handler)(events[i].events);
+ }
+ }
+
+ // Remove any handlers that have been posted for removal. This is done here
+ // instead of in RemoveEventHandler() to prevent races between the dispatch
+ // thread and the code requesting the removal. Handlers are guaranteed to
+ // stay alive between exiting epoll_wait() and the dispatch loop above.
+ std::lock_guard<std::mutex> lock(lock_);
+ for (auto handler_fd : removed_handlers_) {
+ ALOGD_IF(TRACE,
+ "EpollEventDispatcher::EventThread: removing handler: fd=%d",
+ handler_fd);
+ handlers_.erase(handler_fd);
+ }
+ removed_handlers_.clear();
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.h b/libs/vr/libvrflinger/epoll_event_dispatcher.h
new file mode 100644
index 0000000000..eb687f4e86
--- /dev/null
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.h
@@ -0,0 +1,63 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
+
+#include <sys/epoll.h>
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+namespace android {
+namespace dvr {
+
+class EpollEventDispatcher {
+ public:
+ // Function type for event handlers. The handler receives a bitmask of the
+ // epoll events that occurred on the file descriptor associated with the
+ // handler.
+ using Handler = std::function<void(int)>;
+
+ EpollEventDispatcher();
+ ~EpollEventDispatcher();
+
+ // |handler| is called on the internal dispatch thread when |fd| is signaled
+ // by events in |event_mask|.
+ pdx::Status<void> AddEventHandler(int fd, int event_mask, Handler handler);
+ pdx::Status<void> RemoveEventHandler(int fd);
+
+ void Stop();
+
+ private:
+ void EventThread();
+
+ std::thread thread_;
+ std::atomic<bool> exit_thread_{false};
+
+ // Protects handlers_ and removed_handlers_ and serializes operations on
+ // epoll_fd_ and event_fd_.
+ std::mutex lock_;
+
+ // Maintains a map of fds to event handlers. This is primarily to keep any
+ // references alive that may be bound in the std::function instances. It is
+ // not used at dispatch time to avoid performance problems with different
+ // versions of std::unordered_map.
+ std::unordered_map<int, Handler> handlers_;
+
+ // List of fds to be removed from the map. The actual removal is performed
+ // by the event dispatch thread to avoid races.
+ std::vector<int> removed_handlers_;
+
+ pdx::LocalHandle epoll_fd_;
+ pdx::LocalHandle event_fd_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
new file mode 100644
index 0000000000..34474d969a
--- /dev/null
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -0,0 +1,1134 @@
+#include "hardware_composer.h"
+
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <fcntl.h>
+#include <log/log.h>
+#include <poll.h>
+#include <sync/sync.h>
+#include <sys/eventfd.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/system_properties.h>
+#include <sys/timerfd.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <chrono>
+#include <functional>
+#include <map>
+
+#include <dvr/dvr_display_types.h>
+#include <dvr/performance_client_api.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/ion_buffer.h>
+#include <private/dvr/pose_client_internal.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::rpc::EmptyVariant;
+using android::pdx::rpc::IfAnyOf;
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// If the number of pending fences goes over this count at the point when we
+// are about to submit a new frame to HWC, we will drop the frame. This should
+// be a signal that the display driver has begun queuing frames. Note that with
+// smart displays (with RAM), the fence is signaled earlier than the next vsync,
+// at the point when the DMA to the display completes. Currently we use a smart
+// display and the EDS timing coincides with zero pending fences, so this is 0.
+constexpr int kAllowedPendingFenceCount = 0;
+
+// Offset before vsync to submit frames to hardware composer.
+constexpr int64_t kFramePostOffsetNs = 4000000; // 4ms
+
+const char kBacklightBrightnessSysFile[] =
+ "/sys/class/leds/lcd-backlight/brightness";
+
+const char kPrimaryDisplayVSyncEventFile[] =
+ "/sys/class/graphics/fb0/vsync_event";
+
+const char kPrimaryDisplayWaitPPEventFile[] = "/sys/class/graphics/fb0/wait_pp";
+
+const char kDvrPerformanceProperty[] = "sys.dvr.performance";
+
+const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
+
+// Get time offset from a vsync to when the pose for that vsync should be
+// predicted out to. For example, if scanout gets halfway through the frame
+// at the halfway point between vsyncs, then this could be half the period.
+// With global shutter displays, this should be changed to the offset to when
+// illumination begins. Low persistence adds a frame of latency, so we predict
+// to the center of the next frame.
+inline int64_t GetPosePredictionTimeOffset(int64_t vsync_period_ns) {
+ return (vsync_period_ns * 150) / 100;
+}
+
+// Attempts to set the scheduler class and partiton for the current thread.
+// Returns true on success or false on failure.
+bool SetThreadPolicy(const std::string& scheduler_class,
+ const std::string& partition) {
+ int error = dvrSetSchedulerClass(0, scheduler_class.c_str());
+ if (error < 0) {
+ ALOGE(
+ "SetThreadPolicy: Failed to set scheduler class \"%s\" for "
+ "thread_id=%d: %s",
+ scheduler_class.c_str(), gettid(), strerror(-error));
+ return false;
+ }
+ error = dvrSetCpuPartition(0, partition.c_str());
+ if (error < 0) {
+ ALOGE(
+ "SetThreadPolicy: Failed to set cpu partiton \"%s\" for thread_id=%d: "
+ "%s",
+ partition.c_str(), gettid(), strerror(-error));
+ return false;
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+// Layer static data.
+Hwc2::Composer* Layer::hwc2_hidl_;
+const HWCDisplayMetrics* Layer::display_metrics_;
+
+// HardwareComposer static data;
+constexpr size_t HardwareComposer::kMaxHardwareLayers;
+
+HardwareComposer::HardwareComposer()
+ : HardwareComposer(nullptr, RequestDisplayCallback()) {}
+
+HardwareComposer::HardwareComposer(
+ Hwc2::Composer* hwc2_hidl, RequestDisplayCallback request_display_callback)
+ : initialized_(false),
+ hwc2_hidl_(hwc2_hidl),
+ request_display_callback_(request_display_callback),
+ callbacks_(new ComposerCallback) {}
+
+HardwareComposer::~HardwareComposer(void) {
+ UpdatePostThreadState(PostThreadState::Quit, true);
+ if (post_thread_.joinable())
+ post_thread_.join();
+}
+
+bool HardwareComposer::Initialize() {
+ if (initialized_) {
+ ALOGE("HardwareComposer::Initialize: already initialized.");
+ return false;
+ }
+
+ HWC::Error error = HWC::Error::None;
+
+ Hwc2::Config config;
+ error = hwc2_hidl_->getActiveConfig(HWC_DISPLAY_PRIMARY, &config);
+
+ if (error != HWC::Error::None) {
+ ALOGE("HardwareComposer: Failed to get current display config : %d",
+ config);
+ return false;
+ }
+
+ error =
+ GetDisplayMetrics(HWC_DISPLAY_PRIMARY, config, &native_display_metrics_);
+
+ if (error != HWC::Error::None) {
+ ALOGE(
+ "HardwareComposer: Failed to get display attributes for current "
+ "configuration : %d",
+ error.value);
+ return false;
+ }
+
+ ALOGI(
+ "HardwareComposer: primary display attributes: width=%d height=%d "
+ "vsync_period_ns=%d DPI=%dx%d",
+ native_display_metrics_.width, native_display_metrics_.height,
+ native_display_metrics_.vsync_period_ns, native_display_metrics_.dpi.x,
+ native_display_metrics_.dpi.y);
+
+ // Set the display metrics but never use rotation to avoid the long latency of
+ // rotation processing in hwc.
+ display_transform_ = HWC_TRANSFORM_NONE;
+ display_metrics_ = native_display_metrics_;
+
+ // Pass hwc instance and metrics to setup globals for Layer.
+ Layer::InitializeGlobals(hwc2_hidl_, &native_display_metrics_);
+
+ post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ LOG_ALWAYS_FATAL_IF(
+ !post_thread_event_fd_,
+ "HardwareComposer: Failed to create interrupt event fd : %s",
+ strerror(errno));
+
+ post_thread_ = std::thread(&HardwareComposer::PostThread, this);
+
+ initialized_ = true;
+
+ return initialized_;
+}
+
+void HardwareComposer::Enable() {
+ UpdatePostThreadState(PostThreadState::Suspended, false);
+}
+
+void HardwareComposer::Disable() {
+ UpdatePostThreadState(PostThreadState::Suspended, true);
+}
+
+// Update the post thread quiescent state based on idle and suspended inputs.
+void HardwareComposer::UpdatePostThreadState(PostThreadStateType state,
+ bool suspend) {
+ std::unique_lock<std::mutex> lock(post_thread_mutex_);
+
+ // Update the votes in the state variable before evaluating the effective
+ // quiescent state. Any bits set in post_thread_state_ indicate that the post
+ // thread should be suspended.
+ if (suspend) {
+ post_thread_state_ |= state;
+ } else {
+ post_thread_state_ &= ~state;
+ }
+
+ const bool quit = post_thread_state_ & PostThreadState::Quit;
+ const bool effective_suspend = post_thread_state_ != PostThreadState::Active;
+ if (quit) {
+ post_thread_quiescent_ = true;
+ eventfd_write(post_thread_event_fd_.Get(), 1);
+ post_thread_wait_.notify_one();
+ } else if (effective_suspend && !post_thread_quiescent_) {
+ post_thread_quiescent_ = true;
+ eventfd_write(post_thread_event_fd_.Get(), 1);
+ } else if (!effective_suspend && post_thread_quiescent_) {
+ post_thread_quiescent_ = false;
+ eventfd_t value;
+ eventfd_read(post_thread_event_fd_.Get(), &value);
+ post_thread_wait_.notify_one();
+ }
+
+ // Wait until the post thread is in the requested state.
+ post_thread_ready_.wait(lock, [this, effective_suspend] {
+ return effective_suspend != post_thread_resumed_;
+ });
+}
+
+void HardwareComposer::OnPostThreadResumed() {
+ hwc2_hidl_->resetCommands();
+
+ // Connect to pose service.
+ pose_client_ = dvrPoseCreate();
+ ALOGE_IF(!pose_client_, "HardwareComposer: Failed to create pose client");
+
+ // HIDL HWC seems to have an internal race condition. If we submit a frame too
+ // soon after turning on VSync we don't get any VSync signals. Give poor HWC
+ // implementations a chance to enable VSync before we continue.
+ EnableVsync(false);
+ std::this_thread::sleep_for(100ms);
+ EnableVsync(true);
+ std::this_thread::sleep_for(100ms);
+
+ // TODO(skiazyk): We need to do something about accessing this directly,
+ // supposedly there is a backlight service on the way.
+ // TODO(steventhomas): When we change the backlight setting, will surface
+ // flinger (or something else) set it back to its original value once we give
+ // control of the display back to surface flinger?
+ SetBacklightBrightness(255);
+
+ // Trigger target-specific performance mode change.
+ property_set(kDvrPerformanceProperty, "performance");
+}
+
+void HardwareComposer::OnPostThreadPaused() {
+ retire_fence_fds_.clear();
+ display_surfaces_.clear();
+
+ for (size_t i = 0; i < kMaxHardwareLayers; ++i) {
+ layers_[i].Reset();
+ }
+ active_layer_count_ = 0;
+
+ if (pose_client_) {
+ dvrPoseDestroy(pose_client_);
+ pose_client_ = nullptr;
+ }
+
+ EnableVsync(false);
+
+ hwc2_hidl_->resetCommands();
+
+ // Trigger target-specific performance mode change.
+ property_set(kDvrPerformanceProperty, "idle");
+}
+
+HWC::Error HardwareComposer::Validate(hwc2_display_t display) {
+ uint32_t num_types;
+ uint32_t num_requests;
+ HWC::Error error =
+ hwc2_hidl_->validateDisplay(display, &num_types, &num_requests);
+
+ if (error == HWC2_ERROR_HAS_CHANGES) {
+ // TODO(skiazyk): We might need to inspect the requested changes first, but
+ // so far it seems like we shouldn't ever hit a bad state.
+ // error = hwc2_funcs_.accept_display_changes_fn_(hardware_composer_device_,
+ // display);
+ error = hwc2_hidl_->acceptDisplayChanges(display);
+ }
+
+ return error;
+}
+
+int32_t HardwareComposer::EnableVsync(bool enabled) {
+ return (int32_t)hwc2_hidl_->setVsyncEnabled(
+ HWC_DISPLAY_PRIMARY,
+ (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
+ : HWC2_VSYNC_DISABLE));
+}
+
+HWC::Error HardwareComposer::Present(hwc2_display_t display) {
+ int32_t present_fence;
+ HWC::Error error = hwc2_hidl_->presentDisplay(display, &present_fence);
+
+ // According to the documentation, this fence is signaled at the time of
+ // vsync/DMA for physical displays.
+ if (error == HWC::Error::None) {
+ ATRACE_INT("HardwareComposer: VsyncFence", present_fence);
+ retire_fence_fds_.emplace_back(present_fence);
+ } else {
+ ATRACE_INT("HardwareComposer: PresentResult", error);
+ }
+
+ return error;
+}
+
+HWC::Error HardwareComposer::GetDisplayAttribute(hwc2_display_t display,
+ hwc2_config_t config,
+ hwc2_attribute_t attribute,
+ int32_t* out_value) const {
+ return hwc2_hidl_->getDisplayAttribute(
+ display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value);
+}
+
+HWC::Error HardwareComposer::GetDisplayMetrics(
+ hwc2_display_t display, hwc2_config_t config,
+ HWCDisplayMetrics* out_metrics) const {
+ HWC::Error error;
+
+ error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_WIDTH,
+ &out_metrics->width);
+ if (error != HWC::Error::None) {
+ ALOGE(
+ "HardwareComposer::GetDisplayMetrics: Failed to get display width: %s",
+ error.to_string().c_str());
+ return error;
+ }
+
+ error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_HEIGHT,
+ &out_metrics->height);
+ if (error != HWC::Error::None) {
+ ALOGE(
+ "HardwareComposer::GetDisplayMetrics: Failed to get display height: %s",
+ error.to_string().c_str());
+ return error;
+ }
+
+ error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_VSYNC_PERIOD,
+ &out_metrics->vsync_period_ns);
+ if (error != HWC::Error::None) {
+ ALOGE(
+ "HardwareComposer::GetDisplayMetrics: Failed to get display height: %s",
+ error.to_string().c_str());
+ return error;
+ }
+
+ error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_X,
+ &out_metrics->dpi.x);
+ if (error != HWC::Error::None) {
+ ALOGE(
+ "HardwareComposer::GetDisplayMetrics: Failed to get display DPI X: %s",
+ error.to_string().c_str());
+ return error;
+ }
+
+ error = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_Y,
+ &out_metrics->dpi.y);
+ if (error != HWC::Error::None) {
+ ALOGE(
+ "HardwareComposer::GetDisplayMetrics: Failed to get display DPI Y: %s",
+ error.to_string().c_str());
+ return error;
+ }
+
+ return HWC::Error::None;
+}
+
+std::string HardwareComposer::Dump() { return hwc2_hidl_->dumpDebugInfo(); }
+
+void HardwareComposer::PostLayers() {
+ ATRACE_NAME("HardwareComposer::PostLayers");
+
+ // Setup the hardware composer layers with current buffers.
+ for (size_t i = 0; i < active_layer_count_; i++) {
+ layers_[i].Prepare();
+ }
+
+ HWC::Error error = Validate(HWC_DISPLAY_PRIMARY);
+ if (error != HWC::Error::None) {
+ ALOGE("HardwareComposer::PostLayers: Validate failed: %s",
+ error.to_string().c_str());
+ return;
+ }
+
+ // Now that we have taken in a frame from the application, we have a chance
+ // to drop the frame before passing the frame along to HWC.
+ // If the display driver has become backed up, we detect it here and then
+ // react by skipping this frame to catch up latency.
+ while (!retire_fence_fds_.empty() &&
+ (!retire_fence_fds_.front() ||
+ sync_wait(retire_fence_fds_.front().Get(), 0) == 0)) {
+ // There are only 2 fences in here, no performance problem to shift the
+ // array of ints.
+ retire_fence_fds_.erase(retire_fence_fds_.begin());
+ }
+
+ const bool is_frame_pending = IsFramePendingInDriver();
+ const bool is_fence_pending =
+ retire_fence_fds_.size() > kAllowedPendingFenceCount;
+
+ if (is_fence_pending || is_frame_pending) {
+ ATRACE_INT("frame_skip_count", ++frame_skip_count_);
+
+ ALOGW_IF(is_frame_pending, "Warning: frame already queued, dropping frame");
+ ALOGW_IF(is_fence_pending,
+ "Warning: dropping a frame to catch up with HWC (pending = %zd)",
+ retire_fence_fds_.size());
+
+ for (size_t i = 0; i < active_layer_count_; i++) {
+ layers_[i].Drop();
+ }
+ return;
+ } else {
+ // Make the transition more obvious in systrace when the frame skip happens
+ // above.
+ ATRACE_INT("frame_skip_count", 0);
+ }
+
+#if TRACE
+ for (size_t i = 0; i < active_layer_count_; i++)
+ ALOGI("HardwareComposer::PostLayers: layer=%zu composition=%s", i,
+ layers_[i].GetCompositionType().to_string().c_str());
+#endif
+
+ error = Present(HWC_DISPLAY_PRIMARY);
+ if (error != HWC::Error::None) {
+ ALOGE("HardwareComposer::PostLayers: Present failed: %s",
+ error.to_string().c_str());
+ return;
+ }
+
+ std::vector<Hwc2::Layer> out_layers;
+ std::vector<int> out_fences;
+ error = hwc2_hidl_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers,
+ &out_fences);
+ ALOGE_IF(error != HWC::Error::None,
+ "HardwareComposer::PostLayers: Failed to get release fences: %s",
+ error.to_string().c_str());
+
+ // Perform post-frame bookkeeping. Unused layers are a no-op.
+ uint32_t num_elements = out_layers.size();
+ for (size_t i = 0; i < num_elements; ++i) {
+ for (size_t j = 0; j < active_layer_count_; ++j) {
+ if (layers_[j].GetLayerHandle() == out_layers[i]) {
+ layers_[j].Finish(out_fences[i]);
+ }
+ }
+ }
+}
+
+void HardwareComposer::SetDisplaySurfaces(
+ std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces) {
+ ALOGI("HardwareComposer::SetDisplaySurfaces: surface count=%zd",
+ surfaces.size());
+ const bool display_idle = surfaces.size() == 0;
+ {
+ std::unique_lock<std::mutex> lock(post_thread_mutex_);
+ pending_surfaces_ = std::move(surfaces);
+ }
+
+ // Set idle state based on whether there are any surfaces to handle.
+ UpdatePostThreadState(PostThreadState::Idle, display_idle);
+
+ // XXX: TEMPORARY
+ // Request control of the display based on whether there are any surfaces to
+ // handle. This callback sets the post thread active state once the transition
+ // is complete in SurfaceFlinger.
+ // TODO(eieio): Unify the control signal used to move SurfaceFlinger into VR
+ // mode. Currently this is hooked up to persistent VR mode, but perhaps this
+ // makes more sense to control it from VrCore, which could in turn base its
+ // decision on persistent VR mode.
+ if (request_display_callback_)
+ request_display_callback_(!display_idle);
+}
+
+int HardwareComposer::PostThreadPollInterruptible(
+ const pdx::LocalHandle& event_fd, int requested_events) {
+ pollfd pfd[2] = {
+ {
+ .fd = event_fd.Get(),
+ .events = static_cast<short>(requested_events),
+ .revents = 0,
+ },
+ {
+ .fd = post_thread_event_fd_.Get(),
+ .events = POLLPRI | POLLIN,
+ .revents = 0,
+ },
+ };
+ int ret, error;
+ do {
+ ret = poll(pfd, 2, -1);
+ error = errno;
+ ALOGW_IF(ret < 0,
+ "HardwareComposer::PostThreadPollInterruptible: Error during "
+ "poll(): %s (%d)",
+ strerror(error), error);
+ } while (ret < 0 && error == EINTR);
+
+ if (ret < 0) {
+ return -error;
+ } else if (pfd[0].revents != 0) {
+ return 0;
+ } else if (pfd[1].revents != 0) {
+ ALOGI("VrHwcPost thread interrupted");
+ return kPostThreadInterrupted;
+ } else {
+ return 0;
+ }
+}
+
+// Reads the value of the display driver wait_pingpong state. Returns 0 or 1
+// (the value of the state) on success or a negative error otherwise.
+// TODO(eieio): This is pretty driver specific, this should be moved to a
+// separate class eventually.
+int HardwareComposer::ReadWaitPPState() {
+ // Gracefully handle when the kernel does not support this feature.
+ if (!primary_display_wait_pp_fd_)
+ return 0;
+
+ const int wait_pp_fd = primary_display_wait_pp_fd_.Get();
+ int ret, error;
+
+ ret = lseek(wait_pp_fd, 0, SEEK_SET);
+ if (ret < 0) {
+ error = errno;
+ ALOGE("HardwareComposer::ReadWaitPPState: Failed to seek wait_pp fd: %s",
+ strerror(error));
+ return -error;
+ }
+
+ char data = -1;
+ ret = read(wait_pp_fd, &data, sizeof(data));
+ if (ret < 0) {
+ error = errno;
+ ALOGE("HardwareComposer::ReadWaitPPState: Failed to read wait_pp state: %s",
+ strerror(error));
+ return -error;
+ }
+
+ switch (data) {
+ case '0':
+ return 0;
+ case '1':
+ return 1;
+ default:
+ ALOGE(
+ "HardwareComposer::ReadWaitPPState: Unexpected value for wait_pp: %d",
+ data);
+ return -EINVAL;
+ }
+}
+
+// Reads the timestamp of the last vsync from the display driver.
+// TODO(eieio): This is pretty driver specific, this should be moved to a
+// separate class eventually.
+int HardwareComposer::ReadVSyncTimestamp(int64_t* timestamp) {
+ const int event_fd = primary_display_vsync_event_fd_.Get();
+ int ret, error;
+
+ // The driver returns data in the form "VSYNC=<timestamp ns>".
+ std::array<char, 32> data;
+ data.fill('\0');
+
+ // Seek back to the beginning of the event file.
+ ret = lseek(event_fd, 0, SEEK_SET);
+ if (ret < 0) {
+ error = errno;
+ ALOGE(
+ "HardwareComposer::ReadVSyncTimestamp: Failed to seek vsync event fd: "
+ "%s",
+ strerror(error));
+ return -error;
+ }
+
+ // Read the vsync event timestamp.
+ ret = read(event_fd, data.data(), data.size());
+ if (ret < 0) {
+ error = errno;
+ ALOGE_IF(
+ error != EAGAIN,
+ "HardwareComposer::ReadVSyncTimestamp: Error while reading timestamp: "
+ "%s",
+ strerror(error));
+ return -error;
+ }
+
+ ret = sscanf(data.data(), "VSYNC=%" PRIu64,
+ reinterpret_cast<uint64_t*>(timestamp));
+ if (ret < 0) {
+ error = errno;
+ ALOGE(
+ "HardwareComposer::ReadVSyncTimestamp: Error while parsing timestamp: "
+ "%s",
+ strerror(error));
+ return -error;
+ }
+
+ return 0;
+}
+
+// Blocks until the next vsync event is signaled by the display driver.
+// TODO(eieio): This is pretty driver specific, this should be moved to a
+// separate class eventually.
+int HardwareComposer::BlockUntilVSync() {
+ // Vsync is signaled by POLLPRI on the fb vsync node.
+ return PostThreadPollInterruptible(primary_display_vsync_event_fd_, POLLPRI);
+}
+
+// Waits for the next vsync and returns the timestamp of the vsync event. If
+// vsync already passed since the last call, returns the latest vsync timestamp
+// instead of blocking. This method updates the last_vsync_timeout_ in the
+// process.
+//
+// TODO(eieio): This is pretty driver specific, this should be moved to a
+// separate class eventually.
+int HardwareComposer::WaitForVSync(int64_t* timestamp) {
+ int error;
+
+ // Get the current timestamp and decide what to do.
+ while (true) {
+ int64_t current_vsync_timestamp;
+ error = ReadVSyncTimestamp(&current_vsync_timestamp);
+ if (error < 0 && error != -EAGAIN)
+ return error;
+
+ if (error == -EAGAIN) {
+ // Vsync was turned off, wait for the next vsync event.
+ error = BlockUntilVSync();
+ if (error < 0 || error == kPostThreadInterrupted)
+ return error;
+
+ // Try again to get the timestamp for this new vsync interval.
+ continue;
+ }
+
+ // Check that we advanced to a later vsync interval.
+ if (TimestampGT(current_vsync_timestamp, last_vsync_timestamp_)) {
+ *timestamp = last_vsync_timestamp_ = current_vsync_timestamp;
+ return 0;
+ }
+
+ // See how close we are to the next expected vsync. If we're within 1ms,
+ // sleep for 1ms and try again.
+ const int64_t ns_per_frame = display_metrics_.vsync_period_ns;
+ const int64_t threshold_ns = 1000000; // 1ms
+
+ const int64_t next_vsync_est = last_vsync_timestamp_ + ns_per_frame;
+ const int64_t distance_to_vsync_est = next_vsync_est - GetSystemClockNs();
+
+ if (distance_to_vsync_est > threshold_ns) {
+ // Wait for vsync event notification.
+ error = BlockUntilVSync();
+ if (error < 0 || error == kPostThreadInterrupted)
+ return error;
+ } else {
+ // Sleep for a short time (1 millisecond) before retrying.
+ error = SleepUntil(GetSystemClockNs() + threshold_ns);
+ if (error < 0 || error == kPostThreadInterrupted)
+ return error;
+ }
+ }
+}
+
+int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) {
+ const int timer_fd = vsync_sleep_timer_fd_.Get();
+ const itimerspec wakeup_itimerspec = {
+ .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+ .it_value = NsToTimespec(wakeup_timestamp),
+ };
+ int ret =
+ timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &wakeup_itimerspec, nullptr);
+ int error = errno;
+ if (ret < 0) {
+ ALOGE("HardwareComposer::SleepUntil: Failed to set timerfd: %s",
+ strerror(error));
+ return -error;
+ }
+
+ return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN);
+}
+
+void HardwareComposer::PostThread() {
+ // NOLINTNEXTLINE(runtime/int)
+ prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrHwcPost"), 0, 0, 0);
+
+ // Set the scheduler to SCHED_FIFO with high priority. If this fails here
+ // there may have been a startup timing issue between this thread and
+ // performanced. Try again later when this thread becomes active.
+ bool thread_policy_setup =
+ SetThreadPolicy("graphics:high", "/system/performance");
+
+#if ENABLE_BACKLIGHT_BRIGHTNESS
+ // TODO(hendrikw): This isn't required at the moment. It's possible that there
+ // is another method to access this when needed.
+ // Open the backlight brightness control sysfs node.
+ backlight_brightness_fd_ = LocalHandle(kBacklightBrightnessSysFile, O_RDWR);
+ ALOGW_IF(!backlight_brightness_fd_,
+ "HardwareComposer: Failed to open backlight brightness control: %s",
+ strerror(errno));
+#endif // ENABLE_BACKLIGHT_BRIGHTNESS
+
+ // Open the vsync event node for the primary display.
+ // TODO(eieio): Move this into a platform-specific class.
+ primary_display_vsync_event_fd_ =
+ LocalHandle(kPrimaryDisplayVSyncEventFile, O_RDONLY);
+ ALOGE_IF(!primary_display_vsync_event_fd_,
+ "HardwareComposer: Failed to open vsync event node for primary "
+ "display: %s",
+ strerror(errno));
+
+ // Open the wait pingpong status node for the primary display.
+ // TODO(eieio): Move this into a platform-specific class.
+ primary_display_wait_pp_fd_ =
+ LocalHandle(kPrimaryDisplayWaitPPEventFile, O_RDONLY);
+ ALOGW_IF(
+ !primary_display_wait_pp_fd_,
+ "HardwareComposer: Failed to open wait_pp node for primary display: %s",
+ strerror(errno));
+
+ // Create a timerfd based on CLOCK_MONOTINIC.
+ vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
+ LOG_ALWAYS_FATAL_IF(
+ !vsync_sleep_timer_fd_,
+ "HardwareComposer: Failed to create vsync sleep timerfd: %s",
+ strerror(errno));
+
+ const int64_t ns_per_frame = display_metrics_.vsync_period_ns;
+ const int64_t photon_offset_ns = GetPosePredictionTimeOffset(ns_per_frame);
+
+ // TODO(jbates) Query vblank time from device, when such an API is available.
+ // This value (6.3%) was measured on A00 in low persistence mode.
+ int64_t vblank_ns = ns_per_frame * 63 / 1000;
+ int64_t right_eye_photon_offset_ns = (ns_per_frame - vblank_ns) / 2;
+
+ // Check property for overriding right eye offset value.
+ right_eye_photon_offset_ns =
+ property_get_int64(kRightEyeOffsetProperty, right_eye_photon_offset_ns);
+
+ bool was_running = false;
+
+ while (1) {
+ ATRACE_NAME("HardwareComposer::PostThread");
+
+ while (post_thread_quiescent_) {
+ std::unique_lock<std::mutex> lock(post_thread_mutex_);
+ ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
+
+ // Tear down resources.
+ OnPostThreadPaused();
+
+ was_running = false;
+ post_thread_resumed_ = false;
+ post_thread_ready_.notify_all();
+
+ if (post_thread_state_ & PostThreadState::Quit) {
+ ALOGI("HardwareComposer::PostThread: Quitting.");
+ return;
+ }
+
+ post_thread_wait_.wait(lock, [this] { return !post_thread_quiescent_; });
+
+ post_thread_resumed_ = true;
+ post_thread_ready_.notify_all();
+
+ ALOGI("HardwareComposer::PostThread: Exiting quiescent state.");
+ }
+
+ if (!was_running) {
+ // Setup resources.
+ OnPostThreadResumed();
+ was_running = true;
+
+ // Try to setup the scheduler policy if it failed during startup. Only
+ // attempt to do this on transitions from inactive to active to avoid
+ // spamming the system with RPCs and log messages.
+ if (!thread_policy_setup) {
+ thread_policy_setup =
+ SetThreadPolicy("graphics:high", "/system/performance");
+ }
+ }
+
+ int64_t vsync_timestamp = 0;
+ {
+ std::array<char, 128> buf;
+ snprintf(buf.data(), buf.size(), "wait_vsync|vsync=%d|",
+ vsync_count_ + 1);
+ ATRACE_NAME(buf.data());
+
+ const int error = WaitForVSync(&vsync_timestamp);
+ ALOGE_IF(
+ error < 0,
+ "HardwareComposer::PostThread: Failed to wait for vsync event: %s",
+ strerror(-error));
+ // Don't bother processing this frame if a pause was requested
+ if (error == kPostThreadInterrupted)
+ continue;
+ }
+
+ ++vsync_count_;
+
+ if (pose_client_) {
+ // Signal the pose service with vsync info.
+ // Display timestamp is in the middle of scanout.
+ privateDvrPoseNotifyVsync(pose_client_, vsync_count_,
+ vsync_timestamp + photon_offset_ns,
+ ns_per_frame, right_eye_photon_offset_ns);
+ }
+
+ const bool layer_config_changed = UpdateLayerConfig();
+
+ // Signal all of the vsync clients. Because absolute time is used for the
+ // wakeup time below, this can take a little time if necessary.
+ if (vsync_callback_)
+ vsync_callback_(HWC_DISPLAY_PRIMARY, vsync_timestamp,
+ /*frame_time_estimate*/ 0, vsync_count_);
+
+ {
+ // Sleep until shortly before vsync.
+ ATRACE_NAME("sleep");
+
+ const int64_t display_time_est_ns = vsync_timestamp + ns_per_frame;
+ const int64_t now_ns = GetSystemClockNs();
+ const int64_t sleep_time_ns =
+ display_time_est_ns - now_ns - kFramePostOffsetNs;
+ const int64_t wakeup_time_ns = display_time_est_ns - kFramePostOffsetNs;
+
+ ATRACE_INT64("sleep_time_ns", sleep_time_ns);
+ if (sleep_time_ns > 0) {
+ int error = SleepUntil(wakeup_time_ns);
+ ALOGE_IF(error < 0, "HardwareComposer::PostThread: Failed to sleep: %s",
+ strerror(-error));
+ if (error == kPostThreadInterrupted) {
+ if (layer_config_changed) {
+ // If the layer config changed we need to validateDisplay() even if
+ // we're going to drop the frame, to flush the Composer object's
+ // internal command buffer and apply our layer changes.
+ Validate(HWC_DISPLAY_PRIMARY);
+ }
+ continue;
+ }
+ }
+ }
+
+ PostLayers();
+ }
+}
+
+// Checks for changes in the surface stack and updates the layer config to
+// accomodate the new stack.
+bool HardwareComposer::UpdateLayerConfig() {
+ std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces;
+ {
+ std::unique_lock<std::mutex> lock(post_thread_mutex_);
+ if (pending_surfaces_.empty())
+ return false;
+
+ surfaces = std::move(pending_surfaces_);
+ }
+
+ ATRACE_NAME("UpdateLayerConfig_HwLayers");
+
+ display_surfaces_.clear();
+
+ Layer* target_layer;
+ size_t layer_index;
+ for (layer_index = 0;
+ layer_index < std::min(surfaces.size(), kMaxHardwareLayers);
+ layer_index++) {
+ // The bottom layer is opaque, other layers blend.
+ HWC::BlendMode blending =
+ layer_index == 0 ? HWC::BlendMode::None : HWC::BlendMode::Coverage;
+ layers_[layer_index].Setup(surfaces[layer_index], blending,
+ display_transform_, HWC::Composition::Device,
+ layer_index);
+ display_surfaces_.push_back(surfaces[layer_index]);
+ }
+
+ // Clear unused layers.
+ for (size_t i = layer_index; i < kMaxHardwareLayers; i++)
+ layers_[i].Reset();
+
+ active_layer_count_ = layer_index;
+ ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers",
+ active_layer_count_);
+
+ // Any surfaces left over could not be assigned a hardware layer and will
+ // not be displayed.
+ ALOGW_IF(surfaces.size() != display_surfaces_.size(),
+ "HardwareComposer::UpdateLayerConfig: More surfaces than layers: "
+ "pending_surfaces=%zu display_surfaces=%zu",
+ surfaces.size(), display_surfaces_.size());
+
+ return true;
+}
+
+void HardwareComposer::SetVSyncCallback(VSyncCallback callback) {
+ vsync_callback_ = callback;
+}
+
+void HardwareComposer::HwcRefresh(hwc2_callback_data_t /*data*/,
+ hwc2_display_t /*display*/) {
+ // TODO(eieio): implement invalidate callbacks.
+}
+
+void HardwareComposer::HwcVSync(hwc2_callback_data_t /*data*/,
+ hwc2_display_t /*display*/,
+ int64_t /*timestamp*/) {
+ ATRACE_NAME(__PRETTY_FUNCTION__);
+ // Intentionally empty. HWC may require a callback to be set to enable vsync
+ // signals. We bypass this callback thread by monitoring the vsync event
+ // directly, but signals still need to be enabled.
+}
+
+void HardwareComposer::HwcHotplug(hwc2_callback_data_t /*callbackData*/,
+ hwc2_display_t /*display*/,
+ hwc2_connection_t /*connected*/) {
+ // TODO(eieio): implement display hotplug callbacks.
+}
+
+void HardwareComposer::OnHardwareComposerRefresh() {
+ // TODO(steventhomas): Handle refresh.
+}
+
+void HardwareComposer::SetBacklightBrightness(int brightness) {
+ if (backlight_brightness_fd_) {
+ std::array<char, 32> text;
+ const int length = snprintf(text.data(), text.size(), "%d", brightness);
+ write(backlight_brightness_fd_.Get(), text.data(), length);
+ }
+}
+
+void Layer::InitializeGlobals(Hwc2::Composer* hwc2_hidl,
+ const HWCDisplayMetrics* metrics) {
+ hwc2_hidl_ = hwc2_hidl;
+ display_metrics_ = metrics;
+}
+
+void Layer::Reset() {
+ if (hwc2_hidl_ != nullptr && hardware_composer_layer_) {
+ hwc2_hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
+ hardware_composer_layer_ = 0;
+ }
+
+ z_order_ = 0;
+ blending_ = HWC::BlendMode::None;
+ transform_ = HWC::Transform::None;
+ composition_type_ = HWC::Composition::Invalid;
+ target_composition_type_ = composition_type_;
+ source_ = EmptyVariant{};
+ acquire_fence_.Close();
+ surface_rect_functions_applied_ = false;
+}
+
+void Layer::Setup(const std::shared_ptr<DirectDisplaySurface>& surface,
+ HWC::BlendMode blending, HWC::Transform transform,
+ HWC::Composition composition_type, size_t z_order) {
+ Reset();
+ z_order_ = z_order;
+ blending_ = blending;
+ transform_ = transform;
+ composition_type_ = HWC::Composition::Invalid;
+ target_composition_type_ = composition_type;
+ source_ = SourceSurface{surface};
+ CommonLayerSetup();
+}
+
+void Layer::Setup(const std::shared_ptr<IonBuffer>& buffer,
+ HWC::BlendMode blending, HWC::Transform transform,
+ HWC::Composition composition_type, size_t z_order) {
+ Reset();
+ z_order_ = z_order;
+ blending_ = blending;
+ transform_ = transform;
+ composition_type_ = HWC::Composition::Invalid;
+ target_composition_type_ = composition_type;
+ source_ = SourceBuffer{buffer};
+ CommonLayerSetup();
+}
+
+void Layer::UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer) {
+ if (source_.is<SourceBuffer>())
+ std::get<SourceBuffer>(source_) = {buffer};
+}
+
+void Layer::SetBlending(HWC::BlendMode blending) { blending_ = blending; }
+void Layer::SetZOrder(size_t z_order) { z_order_ = z_order; }
+
+IonBuffer* Layer::GetBuffer() {
+ struct Visitor {
+ IonBuffer* operator()(SourceSurface& source) { return source.GetBuffer(); }
+ IonBuffer* operator()(SourceBuffer& source) { return source.GetBuffer(); }
+ IonBuffer* operator()(EmptyVariant) { return nullptr; }
+ };
+ return source_.Visit(Visitor{});
+}
+
+void Layer::UpdateLayerSettings() {
+ if (!IsLayerSetup()) {
+ ALOGE(
+ "HardwareComposer::Layer::UpdateLayerSettings: Attempt to update "
+ "unused Layer!");
+ return;
+ }
+
+ HWC::Error error;
+ hwc2_display_t display = HWC_DISPLAY_PRIMARY;
+
+ error = hwc2_hidl_->setLayerCompositionType(
+ display, hardware_composer_layer_,
+ composition_type_.cast<Hwc2::IComposerClient::Composition>());
+ ALOGE_IF(
+ error != HWC::Error::None,
+ "Layer::UpdateLayerSettings: Error setting layer composition type: %s",
+ error.to_string().c_str());
+
+ error = hwc2_hidl_->setLayerBlendMode(
+ display, hardware_composer_layer_,
+ blending_.cast<Hwc2::IComposerClient::BlendMode>());
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::UpdateLayerSettings: Error setting layer blend mode: %s",
+ error.to_string().c_str());
+
+ // TODO(eieio): Use surface attributes or some other mechanism to control
+ // the layer display frame.
+ error = hwc2_hidl_->setLayerDisplayFrame(
+ display, hardware_composer_layer_,
+ {0, 0, display_metrics_->width, display_metrics_->height});
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::UpdateLayerSettings: Error setting layer display frame: %s",
+ error.to_string().c_str());
+
+ error = hwc2_hidl_->setLayerVisibleRegion(
+ display, hardware_composer_layer_,
+ {{0, 0, display_metrics_->width, display_metrics_->height}});
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::UpdateLayerSettings: Error setting layer visible region: %s",
+ error.to_string().c_str());
+
+ error =
+ hwc2_hidl_->setLayerPlaneAlpha(display, hardware_composer_layer_, 1.0f);
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::UpdateLayerSettings: Error setting layer plane alpha: %s",
+ error.to_string().c_str());
+
+ error =
+ hwc2_hidl_->setLayerZOrder(display, hardware_composer_layer_, z_order_);
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::UpdateLayerSettings: Error setting z_ order: %s",
+ error.to_string().c_str());
+}
+
+void Layer::CommonLayerSetup() {
+ HWC::Error error =
+ hwc2_hidl_->createLayer(HWC_DISPLAY_PRIMARY, &hardware_composer_layer_);
+ ALOGE_IF(
+ error != HWC::Error::None,
+ "Layer::CommonLayerSetup: Failed to create layer on primary display: %s",
+ error.to_string().c_str());
+ UpdateLayerSettings();
+}
+
+void Layer::Prepare() {
+ int right, bottom;
+ sp<GraphicBuffer> handle;
+
+ // Acquire the next buffer according to the type of source.
+ IfAnyOf<SourceSurface, SourceBuffer>::Call(&source_, [&](auto& source) {
+ std::tie(right, bottom, handle, acquire_fence_) = source.Acquire();
+ });
+
+ // When a layer is first setup there may be some time before the first buffer
+ // arrives. Setup the HWC layer as a solid color to stall for time until the
+ // first buffer arrives. Once the first buffer arrives there will always be a
+ // buffer for the frame even if it is old.
+ if (!handle.get()) {
+ if (composition_type_ == HWC::Composition::Invalid) {
+ composition_type_ = HWC::Composition::SolidColor;
+ hwc2_hidl_->setLayerCompositionType(
+ HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+ composition_type_.cast<Hwc2::IComposerClient::Composition>());
+ Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0};
+ hwc2_hidl_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+ layer_color);
+ } else {
+ // The composition type is already set. Nothing else to do until a
+ // buffer arrives.
+ }
+ } else {
+ if (composition_type_ != target_composition_type_) {
+ composition_type_ = target_composition_type_;
+ hwc2_hidl_->setLayerCompositionType(
+ HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+ composition_type_.cast<Hwc2::IComposerClient::Composition>());
+ }
+
+ HWC::Error error{HWC::Error::None};
+ error = hwc2_hidl_->setLayerBuffer(HWC_DISPLAY_PRIMARY,
+ hardware_composer_layer_, 0, handle,
+ acquire_fence_.Get());
+
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::Prepare: Error setting layer buffer: %s",
+ error.to_string().c_str());
+
+ if (!surface_rect_functions_applied_) {
+ const float float_right = right;
+ const float float_bottom = bottom;
+ error = hwc2_hidl_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY,
+ hardware_composer_layer_,
+ {0, 0, float_right, float_bottom});
+
+ ALOGE_IF(error != HWC::Error::None,
+ "Layer::Prepare: Error setting layer source crop: %s",
+ error.to_string().c_str());
+
+ surface_rect_functions_applied_ = true;
+ }
+ }
+}
+
+void Layer::Finish(int release_fence_fd) {
+ IfAnyOf<SourceSurface, SourceBuffer>::Call(
+ &source_, [release_fence_fd](auto& source) {
+ source.Finish(LocalHandle(release_fence_fd));
+ });
+}
+
+void Layer::Drop() { acquire_fence_.Close(); }
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
new file mode 100644
index 0000000000..8ba72ab773
--- /dev/null
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -0,0 +1,457 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
+
+#include <ui/GraphicBuffer.h>
+#include "DisplayHardware/ComposerHal.h"
+#include "hwc_types.h"
+
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+#include <array>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+#include <dvr/pose_client.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/variant.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include "acquired_buffer.h"
+#include "display_surface.h"
+
+// Hardware composer HAL doesn't define HWC_TRANSFORM_NONE as of this writing.
+#ifndef HWC_TRANSFORM_NONE
+#define HWC_TRANSFORM_NONE static_cast<hwc_transform_t>(0)
+#endif
+
+namespace android {
+namespace dvr {
+
+// Basic display metrics for physical displays. Dimensions and densities are
+// relative to the physical display orientation, which may be different from the
+// logical display orientation exposed to applications.
+struct HWCDisplayMetrics {
+ int width;
+ int height;
+ struct {
+ int x;
+ int y;
+ } dpi;
+ int vsync_period_ns;
+};
+
+// Layer represents the connection between a hardware composer layer and the
+// source supplying buffers for the layer's contents.
+class Layer {
+ public:
+ Layer() {}
+
+ // Sets up the global state used by all Layer instances. This must be called
+ // before using any Layer methods.
+ static void InitializeGlobals(Hwc2::Composer* hwc2_hidl,
+ const HWCDisplayMetrics* metrics);
+
+ // Releases any shared pointers and fence handles held by this instance.
+ void Reset();
+
+ // Sets up the layer to use a display surface as its content source. The Layer
+ // automatically handles ACQUIRE/RELEASE phases for the surface's buffer train
+ // every frame.
+ //
+ // |blending| receives HWC_BLENDING_* values.
+ // |transform| receives HWC_TRANSFORM_* values.
+ // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
+ // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
+ // |index| is the index of this surface in the DirectDisplaySurface array.
+ void Setup(const std::shared_ptr<DirectDisplaySurface>& surface,
+ HWC::BlendMode blending, HWC::Transform transform,
+ HWC::Composition composition_type, size_t z_roder);
+
+ // Sets up the layer to use a direct buffer as its content source. No special
+ // handling of the buffer is performed; responsibility for updating or
+ // changing the buffer each frame is on the caller.
+ //
+ // |blending| receives HWC_BLENDING_* values.
+ // |transform| receives HWC_TRANSFORM_* values.
+ // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
+ // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
+ void Setup(const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
+ HWC::Transform transform, HWC::Composition composition_type,
+ size_t z_order);
+
+ // Layers that use a direct IonBuffer should call this each frame to update
+ // which buffer will be used for the next PostLayers.
+ void UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer);
+
+ // Sets up the hardware composer layer for the next frame. When the layer is
+ // associated with a display surface, this method automatically ACQUIRES a new
+ // buffer if one is available.
+ void Prepare();
+
+ // After calling prepare, if this frame is to be dropped instead of passing
+ // along to the HWC, call Drop to close the contained fence(s).
+ void Drop();
+
+ // Performs fence bookkeeping after the frame has been posted to hardware
+ // composer.
+ void Finish(int release_fence_fd);
+
+ // Sets the blending for the layer. |blending| receives HWC_BLENDING_* values.
+ void SetBlending(HWC::BlendMode blending);
+
+ // Sets the z-order of this layer
+ void SetZOrder(size_t z_order);
+
+ // Gets the current IonBuffer associated with this layer. Ownership of the
+ // buffer DOES NOT pass to the caller and the pointer is not guaranteed to
+ // remain valid across calls to Layer::Setup(), Layer::Prepare(), or
+ // Layer::Reset(). YOU HAVE BEEN WARNED.
+ IonBuffer* GetBuffer();
+
+ HWC::Composition GetCompositionType() const { return composition_type_; }
+ HWC::Layer GetLayerHandle() const { return hardware_composer_layer_; }
+ bool IsLayerSetup() const { return !source_.empty(); }
+
+ // Applies all of the settings to this layer using the hwc functions
+ void UpdateLayerSettings();
+
+ int GetSurfaceId() const {
+ int surface_id = -1;
+ pdx::rpc::IfAnyOf<SourceSurface>::Call(
+ &source_, [&surface_id](const SourceSurface& surface_source) {
+ surface_id = surface_source.surface->surface_id();
+ });
+ return surface_id;
+ }
+
+ private:
+ void CommonLayerSetup();
+
+ static Hwc2::Composer* hwc2_hidl_;
+ static const HWCDisplayMetrics* display_metrics_;
+
+ // The hardware composer layer and metrics to use during the prepare cycle.
+ hwc2_layer_t hardware_composer_layer_ = 0;
+
+ // Layer properties used to setup the hardware composer layer during the
+ // Prepare phase.
+ size_t z_order_ = 0;
+ HWC::BlendMode blending_ = HWC::BlendMode::None;
+ HWC::Transform transform_ = HWC::Transform::None;
+ HWC::Composition composition_type_ = HWC::Composition::Invalid;
+ HWC::Composition target_composition_type_ = HWC::Composition::Device;
+
+ // State when the layer is connected to a surface. Provides the same interface
+ // as SourceBuffer to simplify internal use by Layer.
+ struct SourceSurface {
+ std::shared_ptr<DirectDisplaySurface> surface;
+ AcquiredBuffer acquired_buffer;
+ pdx::LocalHandle release_fence;
+
+ SourceSurface(const std::shared_ptr<DirectDisplaySurface>& surface)
+ : surface(surface) {}
+
+ // Attempts to acquire a new buffer from the surface and return a tuple with
+ // width, height, buffer handle, and fence. If a new buffer is not available
+ // the previous buffer is returned or an empty value if no buffer has ever
+ // been posted. When a new buffer is acquired the previous buffer's release
+ // fence is passed out automatically.
+ std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() {
+ if (surface->IsBufferAvailable()) {
+ acquired_buffer.Release(std::move(release_fence));
+ acquired_buffer = surface->AcquireCurrentBuffer();
+ ATRACE_ASYNC_END("BufferPost", acquired_buffer.buffer()->id());
+ }
+ if (!acquired_buffer.IsEmpty()) {
+ return std::make_tuple(acquired_buffer.buffer()->width(),
+ acquired_buffer.buffer()->height(),
+ acquired_buffer.buffer()->buffer()->buffer(),
+ acquired_buffer.ClaimAcquireFence());
+ } else {
+ return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{});
+ }
+ }
+
+ void Finish(pdx::LocalHandle fence) { release_fence = std::move(fence); }
+
+ // Gets a pointer to the current acquired buffer or returns nullptr if there
+ // isn't one.
+ IonBuffer* GetBuffer() {
+ if (acquired_buffer.IsAvailable())
+ return acquired_buffer.buffer()->buffer();
+ else
+ return nullptr;
+ }
+
+ // Returns the surface id of the surface.
+ int GetSurfaceId() { return surface->surface_id(); }
+ };
+
+ // State when the layer is connected to a buffer. Provides the same interface
+ // as SourceSurface to simplify internal use by Layer.
+ struct SourceBuffer {
+ std::shared_ptr<IonBuffer> buffer;
+
+ std::tuple<int, int, sp<GraphicBuffer>, pdx::LocalHandle> Acquire() {
+ if (buffer)
+ return std::make_tuple(buffer->width(), buffer->height(),
+ buffer->buffer(), pdx::LocalHandle{});
+ else
+ return std::make_tuple(0, 0, nullptr, pdx::LocalHandle{});
+ }
+
+ void Finish(pdx::LocalHandle /*fence*/) {}
+
+ IonBuffer* GetBuffer() { return buffer.get(); }
+
+ int GetSurfaceId() const { return -1; }
+ };
+
+ // The underlying hardware composer layer is supplied buffers either from a
+ // surface buffer train or from a buffer directly.
+ pdx::rpc::Variant<SourceSurface, SourceBuffer> source_;
+
+ pdx::LocalHandle acquire_fence_;
+ bool surface_rect_functions_applied_ = false;
+
+ Layer(const Layer&) = delete;
+ void operator=(const Layer&) = delete;
+};
+
+// HardwareComposer encapsulates the hardware composer HAL, exposing a
+// simplified API to post buffers to the display.
+//
+// HardwareComposer is accessed by both the vr flinger dispatcher thread and the
+// surface flinger main thread, in addition to internally running a separate
+// thread for compositing/EDS and posting layers to the HAL. When changing how
+// variables are used or adding new state think carefully about which threads
+// will access the state and whether it needs to be synchronized.
+class HardwareComposer {
+ public:
+ // Type for vsync callback.
+ using VSyncCallback = std::function<void(int, int64_t, int64_t, uint32_t)>;
+ using RequestDisplayCallback = std::function<void(bool)>;
+
+ // Since there is no universal way to query the number of hardware layers,
+ // just set it to 4 for now.
+ static constexpr size_t kMaxHardwareLayers = 4;
+
+ HardwareComposer();
+ HardwareComposer(Hwc2::Composer* hidl,
+ RequestDisplayCallback request_display_callback);
+ ~HardwareComposer();
+
+ bool Initialize();
+
+ bool IsInitialized() const { return initialized_; }
+
+ // Start the post thread if there's work to do (i.e. visible layers). This
+ // should only be called from surface flinger's main thread.
+ void Enable();
+ // Pause the post thread, blocking until the post thread has signaled that
+ // it's paused. This should only be called from surface flinger's main thread.
+ void Disable();
+
+ // Get the HMD display metrics for the current display.
+ display::Metrics GetHmdDisplayMetrics() const;
+
+ HWC::Error GetDisplayAttribute(hwc2_display_t display, hwc2_config_t config,
+ hwc2_attribute_t attributes,
+ int32_t* out_value) const;
+ HWC::Error GetDisplayMetrics(hwc2_display_t display, hwc2_config_t config,
+ HWCDisplayMetrics* out_metrics) const;
+ std::string Dump();
+
+ void SetVSyncCallback(VSyncCallback callback);
+
+ // Metrics of the logical display, which is always landscape.
+ int DisplayWidth() const { return display_metrics_.width; }
+ int DisplayHeight() const { return display_metrics_.height; }
+ HWCDisplayMetrics display_metrics() const { return display_metrics_; }
+
+ // Metrics of the native display, which depends on the specific hardware
+ // implementation of the display.
+ HWCDisplayMetrics native_display_metrics() const {
+ return native_display_metrics_;
+ }
+
+ // Sets the display surfaces to compose the hardware layer stack.
+ void SetDisplaySurfaces(
+ std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces);
+
+ void OnHardwareComposerRefresh();
+
+ private:
+ int32_t EnableVsync(bool enabled);
+
+ class ComposerCallback : public Hwc2::IComposerCallback {
+ public:
+ ComposerCallback() {}
+
+ hardware::Return<void> onHotplug(Hwc2::Display /*display*/,
+ Connection /*connected*/) override {
+ // TODO(skiazyk): depending on how the server is implemented, we might
+ // have to set it up to synchronize with receiving this event, as it can
+ // potentially be a critical event for setting up state within the
+ // hwc2 module. That is, we (technically) should not call any other hwc
+ // methods until this method has been called after registering the
+ // callbacks.
+ return hardware::Void();
+ }
+
+ hardware::Return<void> onRefresh(Hwc2::Display /*display*/) override {
+ return hardware::Void();
+ }
+
+ hardware::Return<void> onVsync(Hwc2::Display /*display*/,
+ int64_t /*timestamp*/) override {
+ return hardware::Void();
+ }
+ };
+
+ HWC::Error Validate(hwc2_display_t display);
+ HWC::Error Present(hwc2_display_t display);
+
+ void SetBacklightBrightness(int brightness);
+
+ void PostLayers();
+ void PostThread();
+
+ // The post thread has two controlling states:
+ // 1. Idle: no work to do (no visible surfaces).
+ // 2. Suspended: explicitly halted (system is not in VR mode).
+ // When either #1 or #2 is true then the post thread is quiescent, otherwise
+ // it is active.
+ using PostThreadStateType = uint32_t;
+ struct PostThreadState {
+ enum : PostThreadStateType {
+ Active = 0,
+ Idle = (1 << 0),
+ Suspended = (1 << 1),
+ Quit = (1 << 2),
+ };
+ };
+
+ void UpdatePostThreadState(uint32_t state, bool suspend);
+
+ // Blocks until either event_fd becomes readable, or we're interrupted by a
+ // control thread. Any errors are returned as negative errno values. If we're
+ // interrupted, kPostThreadInterrupted will be returned.
+ int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd,
+ int requested_events);
+
+ // BlockUntilVSync, WaitForVSync, and SleepUntil are all blocking calls made
+ // on the post thread that can be interrupted by a control thread. If
+ // interrupted, these calls return kPostThreadInterrupted.
+ int ReadWaitPPState();
+ int BlockUntilVSync();
+ int ReadVSyncTimestamp(int64_t* timestamp);
+ int WaitForVSync(int64_t* timestamp);
+ int SleepUntil(int64_t wakeup_timestamp);
+
+ bool IsFramePendingInDriver() { return ReadWaitPPState() == 1; }
+
+ // Reconfigures the layer stack if the display surfaces changed since the last
+ // frame. Called only from the post thread.
+ bool UpdateLayerConfig();
+
+ // Called on the post thread when the post thread is resumed.
+ void OnPostThreadResumed();
+ // Called on the post thread when the post thread is paused or quits.
+ void OnPostThreadPaused();
+
+ bool initialized_;
+
+ // Hardware composer HAL device from SurfaceFlinger. VrFlinger does not own
+ // this pointer.
+ Hwc2::Composer* hwc2_hidl_;
+ RequestDisplayCallback request_display_callback_;
+ sp<ComposerCallback> callbacks_;
+
+ // Display metrics of the physical display.
+ HWCDisplayMetrics native_display_metrics_;
+ // Display metrics of the logical display, adjusted so that orientation is
+ // landscape.
+ HWCDisplayMetrics display_metrics_;
+ // Transform required to get from native to logical display orientation.
+ HWC::Transform display_transform_ = HWC::Transform::None;
+
+ // Pending surface list. Set by the display service when DirectSurfaces are
+ // added, removed, or change visibility. Written by the message dispatch
+ // thread and read by the post thread.
+ std::vector<std::shared_ptr<DirectDisplaySurface>> pending_surfaces_;
+
+ // The surfaces displayed by the post thread. Used exclusively by the post
+ // thread.
+ std::vector<std::shared_ptr<DirectDisplaySurface>> display_surfaces_;
+
+ // Layer array for handling buffer flow into hardware composer layers.
+ std::array<Layer, kMaxHardwareLayers> layers_;
+ size_t active_layer_count_ = 0;
+
+ // Handler to hook vsync events outside of this class.
+ VSyncCallback vsync_callback_;
+
+ // The layer posting thread. This thread wakes up a short time before vsync to
+ // hand buffers to hardware composer.
+ std::thread post_thread_;
+
+ // Post thread state machine and synchronization primitives.
+ PostThreadStateType post_thread_state_{PostThreadState::Idle};
+ std::atomic<bool> post_thread_quiescent_{true};
+ bool post_thread_resumed_{false};
+ pdx::LocalHandle post_thread_event_fd_;
+ std::mutex post_thread_mutex_;
+ std::condition_variable post_thread_wait_;
+ std::condition_variable post_thread_ready_;
+
+ // Backlight LED brightness sysfs node.
+ pdx::LocalHandle backlight_brightness_fd_;
+
+ // Primary display vsync event sysfs node.
+ pdx::LocalHandle primary_display_vsync_event_fd_;
+
+ // Primary display wait_pingpong state sysfs node.
+ pdx::LocalHandle primary_display_wait_pp_fd_;
+
+ // VSync sleep timerfd.
+ pdx::LocalHandle vsync_sleep_timer_fd_;
+
+ // The timestamp of the last vsync.
+ int64_t last_vsync_timestamp_ = 0;
+
+ // Vsync count since display on.
+ uint32_t vsync_count_ = 0;
+
+ // Counter tracking the number of skipped frames.
+ int frame_skip_count_ = 0;
+
+ // Fd array for tracking retire fences that are returned by hwc. This allows
+ // us to detect when the display driver begins queuing frames.
+ std::vector<pdx::LocalHandle> retire_fence_fds_;
+
+ // Pose client for frame count notifications. Pose client predicts poses
+ // out to display frame boundaries, so we need to tell it about vsyncs.
+ DvrPose* pose_client_ = nullptr;
+
+ static constexpr int kPostThreadInterrupted = 1;
+
+ static void HwcRefresh(hwc2_callback_data_t data, hwc2_display_t display);
+ static void HwcVSync(hwc2_callback_data_t data, hwc2_display_t display,
+ int64_t timestamp);
+ static void HwcHotplug(hwc2_callback_data_t callbackData,
+ hwc2_display_t display, hwc2_connection_t connected);
+
+ HardwareComposer(const HardwareComposer&) = delete;
+ void operator=(const HardwareComposer&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
diff --git a/libs/vr/libvrflinger/hwc_types.h b/libs/vr/libvrflinger/hwc_types.h
new file mode 100644
index 0000000000..cbf636c2dc
--- /dev/null
+++ b/libs/vr/libvrflinger/hwc_types.h
@@ -0,0 +1,300 @@
+#ifndef ANDROID_LIBVRFLINGER_HWCTYPES_H
+#define ANDROID_LIBVRFLINGER_HWCTYPES_H
+
+// General HWC type support. Hardware composer type support is a bit of a mess
+// between HWC1, HWC2 C/C++11, and HIDL types. Particularly bothersome is the
+// use of enum classes, which make analogous types between versions much
+// harder to deal with in a uniform way.
+//
+// These utilities help address some of these pains by providing a type-safe,
+// flexible interface to translate between different type spaces.
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include <string>
+#include <type_traits>
+
+namespace HWC {
+
+// Value types derived from HWC HAL types. Some of these are stand-alone,
+// while others are also wrapped in translator classes below.
+using ColorMode = int32_t; // android_color_mode_t;
+using Config = hwc2_config_t;
+using ColorTransform =
+ std::underlying_type<android_color_transform_t>::type; // int32_t;
+using Dataspace = std::underlying_type<android_dataspace_t>::type; // int32_t;
+using DisplayId = hwc2_display_t;
+using DisplayRequest = std::underlying_type<HWC2::DisplayRequest>::type;
+using Hdr = std::underlying_type<android_hdr_t>::type; // int32_t;
+using Layer = hwc2_layer_t;
+using PixelFormat =
+ std::underlying_type<android_pixel_format_t>::type; // int32_t;
+
+// Type traits and casting utilities.
+
+// SFINAE utility to evaluate type expressions.
+template <typename...>
+using TestTypeExpression = void;
+
+// Traits type to determine the underlying type of an enum, integer,
+// or wrapper class.
+template <typename T, typename = typename std::is_enum<T>::type,
+ typename = typename std::is_integral<T>::type, typename = void>
+struct UnderlyingType {
+ using Type = T;
+};
+// Partial specialization that matches enum types. Captures the underlying type
+// of the enum in member type Type.
+template <typename T>
+struct UnderlyingType<T, std::true_type, std::false_type> {
+ using Type = typename std::underlying_type<T>::type;
+};
+// Partial specialization that matches integral types. Captures the type of the
+// integer in member type Type.
+template <typename T>
+struct UnderlyingType<T, std::false_type, std::true_type> {
+ using Type = T;
+};
+// Partial specialization that matches the wrapper types below. Captures
+// wrapper member type ValueType in member type Type.
+template <typename T>
+struct UnderlyingType<T, std::false_type, std::false_type,
+ TestTypeExpression<typename T::ValueType>> {
+ using Type = typename T::ValueType;
+};
+
+// Enable if T is an enum with underlying type U.
+template <typename T, typename U, typename ReturnType = void>
+using EnableIfMatchingEnum = typename std::enable_if<
+ std::is_enum<T>::value &&
+ std::is_same<U, typename UnderlyingType<T>::Type>::value,
+ ReturnType>::type;
+
+// Enable if T and U are the same size/alignment and have the same underlying
+// type. Handles enum, integral, and wrapper classes below.
+template <typename T, typename U, typename Return = void>
+using EnableIfSafeCast = typename std::enable_if<
+ sizeof(T) == sizeof(U) && alignof(T) == alignof(U) &&
+ std::is_same<typename UnderlyingType<T>::Type,
+ typename UnderlyingType<U>::Type>::value,
+ Return>::type;
+
+// Safely cast between std::vectors of matching enum/integer/wraper types.
+// Normally this is not possible with pendantic compiler type checks. However,
+// given the same size, alignment, and underlying type this is safe due to
+// allocator requirements and array-like element access guarantees.
+template <typename T, typename U>
+EnableIfSafeCast<T, U, std::vector<T>*> VectorCast(std::vector<U>* in) {
+ return reinterpret_cast<std::vector<T>*>(in);
+}
+
+// Translator classes that wrap specific HWC types to make translating
+// between different types (especially enum class) in code cleaner.
+
+// Base type for the enum wrappers below. This type provides type definitions
+// and implicit conversion logic common to each wrapper type.
+template <typename EnumType>
+struct Wrapper {
+ // Alias type of this instantiantion of Wrapper. Useful for inheriting
+ // constructors in subclasses via "using Base::Base;" statements.
+ using Base = Wrapper<EnumType>;
+
+ // The enum type wrapped by this instantiation of Wrapper.
+ using BaseType = EnumType;
+
+ // The underlying type of the base enum type.
+ using ValueType = typename UnderlyingType<BaseType>::Type;
+
+ // A default constructor is not defined here. Subclasses should define one
+ // as appropriate to define the correct inital value for the enum type.
+
+ // Default copy constructor.
+ Wrapper(const Wrapper&) = default;
+
+ // Implicit conversion from ValueType.
+ Wrapper(ValueType value) : value(value) {}
+
+ // Implicit conversion from BaseType.
+ Wrapper(BaseType value) : value(static_cast<ValueType>(value)) {}
+
+ // Implicit conversion from an enum type of the same underlying type.
+ template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
+ Wrapper(const T& value) : value(static_cast<ValueType>(value)) {}
+
+ // Implicit conversion to BaseType.
+ operator BaseType() const { return static_cast<BaseType>(value); }
+
+ // Implicit conversion to ValueType.
+ operator ValueType() const { return value; }
+
+ template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
+ T cast() const {
+ return static_cast<T>(value);
+ }
+
+ // Converts to string using HWC2 stringification of BaseType.
+ std::string to_string() const {
+ return HWC2::to_string(static_cast<BaseType>(value));
+ }
+
+ bool operator!=(const Wrapper& other) const { return value != other.value; }
+ bool operator!=(ValueType other_value) const { return value != other_value; }
+ bool operator!=(BaseType other_value) const {
+ return static_cast<BaseType>(value) != other_value;
+ }
+ bool operator==(const Wrapper& other) const { return value == other.value; }
+ bool operator==(ValueType other_value) const { return value == other_value; }
+ bool operator==(BaseType other_value) const {
+ return static_cast<BaseType>(value) == other_value;
+ }
+
+ ValueType value;
+};
+
+struct Attribute final : public Wrapper<HWC2::Attribute> {
+ enum : ValueType {
+ Invalid = HWC2_ATTRIBUTE_INVALID,
+ Width = HWC2_ATTRIBUTE_WIDTH,
+ Height = HWC2_ATTRIBUTE_HEIGHT,
+ VsyncPeriod = HWC2_ATTRIBUTE_VSYNC_PERIOD,
+ DpiX = HWC2_ATTRIBUTE_DPI_X,
+ DpiY = HWC2_ATTRIBUTE_DPI_Y,
+ };
+
+ Attribute() : Base(Invalid) {}
+ using Base::Base;
+};
+
+struct BlendMode final : public Wrapper<HWC2::BlendMode> {
+ enum : ValueType {
+ Invalid = HWC2_BLEND_MODE_INVALID,
+ None = HWC2_BLEND_MODE_NONE,
+ Premultiplied = HWC2_BLEND_MODE_PREMULTIPLIED,
+ Coverage = HWC2_BLEND_MODE_COVERAGE,
+ };
+
+ BlendMode() : Base(Invalid) {}
+ using Base::Base;
+};
+
+struct Composition final : public Wrapper<HWC2::Composition> {
+ enum : ValueType {
+ Invalid = HWC2_COMPOSITION_INVALID,
+ Client = HWC2_COMPOSITION_CLIENT,
+ Device = HWC2_COMPOSITION_DEVICE,
+ SolidColor = HWC2_COMPOSITION_SOLID_COLOR,
+ Cursor = HWC2_COMPOSITION_CURSOR,
+ Sideband = HWC2_COMPOSITION_SIDEBAND,
+ };
+
+ Composition() : Base(Invalid) {}
+ using Base::Base;
+};
+
+struct DisplayType final : public Wrapper<HWC2::DisplayType> {
+ enum : ValueType {
+ Invalid = HWC2_DISPLAY_TYPE_INVALID,
+ Physical = HWC2_DISPLAY_TYPE_PHYSICAL,
+ Virtual = HWC2_DISPLAY_TYPE_VIRTUAL,
+ };
+
+ DisplayType() : Base(Invalid) {}
+ using Base::Base;
+};
+
+struct Error final : public Wrapper<HWC2::Error> {
+ enum : ValueType {
+ None = HWC2_ERROR_NONE,
+ BadConfig = HWC2_ERROR_BAD_CONFIG,
+ BadDisplay = HWC2_ERROR_BAD_DISPLAY,
+ BadLayer = HWC2_ERROR_BAD_LAYER,
+ BadParameter = HWC2_ERROR_BAD_PARAMETER,
+ HasChanges = HWC2_ERROR_HAS_CHANGES,
+ NoResources = HWC2_ERROR_NO_RESOURCES,
+ NotValidated = HWC2_ERROR_NOT_VALIDATED,
+ Unsupported = HWC2_ERROR_UNSUPPORTED,
+ };
+
+ Error() : Base(None) {}
+ using Base::Base;
+};
+
+struct LayerRequest final : public Wrapper<HWC2::LayerRequest> {
+ enum : ValueType {
+ ClearClientTarget = HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET,
+ };
+
+ LayerRequest() : Base(0) {}
+ using Base::Base;
+};
+
+struct PowerMode final : public Wrapper<HWC2::PowerMode> {
+ enum : ValueType {
+ Off = HWC2_POWER_MODE_OFF,
+ DozeSuspend = HWC2_POWER_MODE_DOZE_SUSPEND,
+ Doze = HWC2_POWER_MODE_DOZE,
+ On = HWC2_POWER_MODE_ON,
+ };
+
+ PowerMode() : Base(Off) {}
+ using Base::Base;
+};
+
+struct Transform final : public Wrapper<HWC2::Transform> {
+ enum : ValueType {
+ None = 0,
+ FlipH = HWC_TRANSFORM_FLIP_H,
+ FlipV = HWC_TRANSFORM_FLIP_V,
+ Rotate90 = HWC_TRANSFORM_ROT_90,
+ Rotate180 = HWC_TRANSFORM_ROT_180,
+ Rotate270 = HWC_TRANSFORM_ROT_270,
+ FlipHRotate90 = HWC_TRANSFORM_FLIP_H_ROT_90,
+ FlipVRotate90 = HWC_TRANSFORM_FLIP_V_ROT_90,
+ };
+
+ Transform() : Base(None) {}
+ using Base::Base;
+};
+
+struct Vsync final : public Wrapper<HWC2::Vsync> {
+ enum : ValueType {
+ Invalid = HWC2_VSYNC_INVALID,
+ Enable = HWC2_VSYNC_ENABLE,
+ Disable = HWC2_VSYNC_DISABLE,
+ };
+
+ Vsync() : Base(Invalid) {}
+ using Base::Base;
+};
+
+// Utility color type.
+struct Color final {
+ Color(const Color&) = default;
+ Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) {}
+ Color(hwc_color_t color) : r(color.r), g(color.g), b(color.b), a(color.a) {}
+
+ operator hwc_color_t() const { return {r, g, b, a}; }
+
+ uint8_t r __attribute__((aligned(1)));
+ uint8_t g __attribute__((aligned(1)));
+ uint8_t b __attribute__((aligned(1)));
+ uint8_t a __attribute__((aligned(1)));
+};
+
+// Utility rectangle type.
+struct Rect final {
+ // TODO(eieio): Implicit conversion to/from Android rect types.
+
+ int32_t left __attribute__((aligned(4)));
+ int32_t top __attribute__((aligned(4)));
+ int32_t right __attribute__((aligned(4)));
+ int32_t bottom __attribute__((aligned(4)));
+};
+
+} // namespace HWC
+
+#endif // ANDROID_LIBVRFLINGER_HWCTYPES_H
diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
new file mode 100644
index 0000000000..145852e949
--- /dev/null
+++ b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
@@ -0,0 +1,60 @@
+#ifndef ANDROID_DVR_VR_FLINGER_H_
+#define ANDROID_DVR_VR_FLINGER_H_
+
+#include <thread>
+#include <memory>
+
+#include <pdx/default_transport/service_dispatcher.h>
+#include <vr/vr_manager/vr_manager.h>
+
+namespace android {
+
+namespace Hwc2 {
+class Composer;
+} // namespace Hwc2
+
+namespace dvr {
+
+class DisplayService;
+
+class VrFlinger {
+ public:
+ using RequestDisplayCallback = std::function<void(bool)>;
+ static std::unique_ptr<VrFlinger> Create(
+ Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback);
+ ~VrFlinger();
+
+ // These functions are all called on surface flinger's main thread.
+ void OnBootFinished();
+ void GrantDisplayOwnership();
+ void SeizeDisplayOwnership();
+
+ // Called on a binder thread.
+ void OnHardwareComposerRefresh();
+
+ private:
+ VrFlinger();
+ bool Init(Hwc2::Composer* hidl,
+ RequestDisplayCallback request_display_callback);
+
+ // Needs to be a separate class for binder's ref counting
+ class PersistentVrStateCallback : public BnPersistentVrStateCallbacks {
+ public:
+ PersistentVrStateCallback(RequestDisplayCallback request_display_callback)
+ : request_display_callback_(request_display_callback) {}
+ void onPersistentVrStateChanged(bool enabled) override;
+ private:
+ RequestDisplayCallback request_display_callback_;
+ };
+
+ std::thread dispatcher_thread_;
+ std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher_;
+ std::shared_ptr<android::dvr::DisplayService> display_service_;
+ sp<PersistentVrStateCallback> persistent_vr_state_callback_;
+ RequestDisplayCallback request_display_callback_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_VR_FLINGER_H_
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
new file mode 100644
index 0000000000..b2dc1d86e8
--- /dev/null
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -0,0 +1,151 @@
+#include <dvr/vr_flinger.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <memory>
+
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <log/log.h>
+#include <private/dvr/display_client.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+
+#include <pdx/default_transport/service_dispatcher.h>
+
+#include <functional>
+
+#include "DisplayHardware/ComposerHal.h"
+#include "display_manager_service.h"
+#include "display_service.h"
+#include "vsync_service.h"
+
+namespace android {
+namespace dvr {
+
+std::unique_ptr<VrFlinger> VrFlinger::Create(
+ Hwc2::Composer* hidl, RequestDisplayCallback request_display_callback) {
+ std::unique_ptr<VrFlinger> vr_flinger(new VrFlinger);
+ if (vr_flinger->Init(hidl, request_display_callback))
+ return vr_flinger;
+ else
+ return nullptr;
+}
+
+VrFlinger::VrFlinger() {}
+
+VrFlinger::~VrFlinger() {
+ if (persistent_vr_state_callback_.get()) {
+ sp<IVrManager> vr_manager = interface_cast<IVrManager>(
+ defaultServiceManager()->checkService(String16("vrmanager")));
+ if (vr_manager.get()) {
+ vr_manager->unregisterPersistentVrStateListener(
+ persistent_vr_state_callback_);
+ }
+ }
+
+ if (dispatcher_)
+ dispatcher_->SetCanceled(true);
+ if (dispatcher_thread_.joinable())
+ dispatcher_thread_.join();
+}
+
+bool VrFlinger::Init(Hwc2::Composer* hidl,
+ RequestDisplayCallback request_display_callback) {
+ if (!hidl || !request_display_callback)
+ return false;
+
+ std::shared_ptr<android::pdx::Service> service;
+
+ ALOGI("Starting up VrFlinger...");
+
+ setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY);
+ set_sched_policy(0, SP_FOREGROUND);
+
+ // We need to be able to create endpoints with full perms.
+ umask(0000);
+
+ android::ProcessState::self()->startThreadPool();
+
+ request_display_callback_ = request_display_callback;
+
+ dispatcher_ = android::pdx::default_transport::ServiceDispatcher::Create();
+ CHECK_ERROR(!dispatcher_, error, "Failed to create service dispatcher.");
+
+ display_service_ =
+ android::dvr::DisplayService::Create(hidl, request_display_callback);
+ CHECK_ERROR(!display_service_, error, "Failed to create display service.");
+ dispatcher_->AddService(display_service_);
+
+ service = android::dvr::DisplayManagerService::Create(display_service_);
+ CHECK_ERROR(!service, error, "Failed to create display manager service.");
+ dispatcher_->AddService(service);
+
+ service = android::dvr::VSyncService::Create();
+ CHECK_ERROR(!service, error, "Failed to create vsync service.");
+ dispatcher_->AddService(service);
+
+ display_service_->SetVSyncCallback(
+ std::bind(&android::dvr::VSyncService::VSyncEvent,
+ std::static_pointer_cast<android::dvr::VSyncService>(service),
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3, std::placeholders::_4));
+
+ dispatcher_thread_ = std::thread([this]() {
+ prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
+ ALOGI("Entering message loop.");
+
+ int ret = dispatcher_->EnterDispatchLoop();
+ if (ret < 0) {
+ ALOGE("Dispatch loop exited because: %s\n", strerror(-ret));
+ }
+ });
+
+ return true;
+
+error:
+ return false;
+}
+
+void VrFlinger::OnBootFinished() {
+ sp<IVrManager> vr_manager = interface_cast<IVrManager>(
+ defaultServiceManager()->checkService(String16("vrmanager")));
+ if (vr_manager.get()) {
+ persistent_vr_state_callback_ =
+ new PersistentVrStateCallback(request_display_callback_);
+ vr_manager->registerPersistentVrStateListener(
+ persistent_vr_state_callback_);
+ } else {
+ ALOGE("Unable to register vr flinger for persistent vr mode changes");
+ }
+}
+
+void VrFlinger::GrantDisplayOwnership() {
+ display_service_->GrantDisplayOwnership();
+}
+
+void VrFlinger::SeizeDisplayOwnership() {
+ display_service_->SeizeDisplayOwnership();
+}
+
+void VrFlinger::OnHardwareComposerRefresh() {
+ display_service_->OnHardwareComposerRefresh();
+}
+
+void VrFlinger::PersistentVrStateCallback::onPersistentVrStateChanged(
+ bool enabled) {
+ ALOGV("Notified persistent vr mode is %s", enabled ? "on" : "off");
+ // TODO(eieio): Determine the correct signal to request display control.
+ // Persistent VR mode is not enough.
+ // request_display_callback_(enabled);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
new file mode 100644
index 0000000000..2a83933470
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.cpp
@@ -0,0 +1,213 @@
+#include "vsync_service.h"
+
+#include <hardware/hwcomposer.h>
+#include <log/log.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <utils/Trace.h>
+
+#include <dvr/dvr_display_types.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_protocol.h>
+
+using android::dvr::display::VSyncProtocol;
+using android::dvr::display::VSyncSchedInfo;
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+VSyncService::VSyncService()
+ : BASE("VSyncService", Endpoint::Create(VSyncProtocol::kClientPath)),
+ last_vsync_(0),
+ current_vsync_(0),
+ compositor_time_ns_(0),
+ current_vsync_count_(0) {}
+
+VSyncService::~VSyncService() {}
+
+void VSyncService::VSyncEvent(int display, int64_t timestamp_ns,
+ int64_t compositor_time_ns,
+ uint32_t vsync_count) {
+ ATRACE_NAME("VSyncService::VSyncEvent");
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ if (display == HWC_DISPLAY_PRIMARY) {
+ last_vsync_ = current_vsync_;
+ current_vsync_ = timestamp_ns;
+ compositor_time_ns_ = compositor_time_ns;
+ current_vsync_count_ = vsync_count;
+
+ NotifyWaiters();
+ UpdateClients();
+ }
+}
+
+std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) {
+ const MessageInfo& info = message.GetInfo();
+
+ auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid);
+ AddClient(client);
+
+ return client;
+}
+
+void VSyncService::OnChannelClose(pdx::Message& /*message*/,
+ const std::shared_ptr<Channel>& channel) {
+ auto client = std::static_pointer_cast<VSyncChannel>(channel);
+ if (!client) {
+ ALOGW("WARNING: VSyncChannel was NULL!!!\n");
+ return;
+ }
+
+ RemoveClient(client);
+}
+
+void VSyncService::AddWaiter(pdx::Message& message) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message));
+ waiters_.push_back(std::move(waiter));
+}
+
+void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ clients_.push_back(client);
+}
+
+void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) {
+ std::lock_guard<std::mutex> autolock(mutex_);
+ clients_.remove(client);
+}
+
+// Private. Assumes mutex is held.
+void VSyncService::NotifyWaiters() {
+ ATRACE_NAME("VSyncService::NotifyWaiters");
+ auto first = waiters_.begin();
+ auto last = waiters_.end();
+
+ while (first != last) {
+ (*first)->Notify(current_vsync_);
+ waiters_.erase(first++);
+ }
+}
+
+// Private. Assumes mutex is held.
+void VSyncService::UpdateClients() {
+ ATRACE_NAME("VSyncService::UpdateClients");
+ auto first = clients_.begin();
+ auto last = clients_.end();
+
+ while (first != last) {
+ (*first)->Signal();
+ first++;
+ }
+}
+
+pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) {
+ switch (message.GetOp()) {
+ case VSyncProtocol::Wait::Opcode:
+ AddWaiter(message);
+ return {};
+
+ case VSyncProtocol::GetLastTimestamp::Opcode:
+ DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>(
+ *this, &VSyncService::OnGetLastTimestamp, message);
+ return {};
+
+ case VSyncProtocol::GetSchedInfo::Opcode:
+ DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>(
+ *this, &VSyncService::OnGetSchedInfo, message);
+ return {};
+
+ case VSyncProtocol::Acknowledge::Opcode:
+ DispatchRemoteMethod<VSyncProtocol::Acknowledge>(
+ *this, &VSyncService::OnAcknowledge, message);
+ return {};
+
+ default:
+ return Service::HandleMessage(message);
+ }
+}
+
+pdx::Status<int64_t> VSyncService::OnGetLastTimestamp(pdx::Message& message) {
+ auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ // Getting the timestamp has the side effect of ACKing.
+ client->Ack();
+ return {current_vsync_};
+}
+
+pdx::Status<VSyncSchedInfo> VSyncService::OnGetSchedInfo(
+ pdx::Message& message) {
+ auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+ std::lock_guard<std::mutex> autolock(mutex_);
+
+ // Getting the timestamp has the side effect of ACKing.
+ client->Ack();
+
+ uint32_t next_vsync_count = current_vsync_count_ + 1;
+ int64_t current_time = GetSystemClockNs();
+ int64_t vsync_period_ns = 0;
+ int64_t next_warp;
+ if (current_vsync_ == 0 || last_vsync_ == 0) {
+ // Handle startup when current_vsync_ or last_vsync_ are 0.
+ // Normally should not happen because vsync_service is running before
+ // applications, but in case it does a sane time prevents applications
+ // from malfunctioning.
+ vsync_period_ns = 20000000;
+ next_warp = current_time;
+ } else {
+ // TODO(jbates) When we have an accurate reading of the true vsync
+ // period, use that instead of this estimated value.
+ vsync_period_ns = current_vsync_ - last_vsync_;
+ // Clamp the period, because when there are no surfaces the last_vsync_
+ // value will get stale. Note this is temporary and goes away as soon
+ // as we have an accurate vsync period reported by the system.
+ vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000));
+ next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_;
+ // If the request missed the present window, move up to the next vsync.
+ if (current_time > next_warp) {
+ next_warp += vsync_period_ns;
+ ++next_vsync_count;
+ }
+ }
+
+ return {{vsync_period_ns, next_warp, next_vsync_count}};
+}
+
+pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) {
+ auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+ std::lock_guard<std::mutex> autolock(mutex_);
+ client->Ack();
+ return {};
+}
+
+void VSyncWaiter::Notify(int64_t timestamp) {
+ timestamp_ = timestamp;
+ DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait,
+ message_);
+}
+
+pdx::Status<int64_t> VSyncWaiter::OnWait(pdx::Message& /*message*/) {
+ return {timestamp_};
+}
+
+void VSyncChannel::Ack() {
+ ALOGD_IF(TRACE, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_);
+ service_.ModifyChannelEvents(cid_, POLLPRI, 0);
+}
+
+void VSyncChannel::Signal() {
+ ALOGD_IF(TRACE, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_);
+ service_.ModifyChannelEvents(cid_, 0, POLLPRI);
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h
new file mode 100644
index 0000000000..215948eb05
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.h
@@ -0,0 +1,106 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
+
+#include <pdx/service.h>
+
+#include <list>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "display_service.h"
+
+namespace android {
+namespace dvr {
+
+// VSyncWaiter encapsulates a client blocked waiting for the next vsync.
+// It is used to enqueue the Message to reply to when the next vsync event
+// occurs.
+class VSyncWaiter {
+ public:
+ explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {}
+
+ void Notify(int64_t timestamp);
+
+ private:
+ pdx::Status<int64_t> OnWait(pdx::Message& message);
+
+ pdx::Message message_;
+ int64_t timestamp_ = 0;
+
+ VSyncWaiter(const VSyncWaiter&) = delete;
+ void operator=(const VSyncWaiter&) = delete;
+};
+
+// VSyncChannel manages the service-side per-client context for each client
+// using the service.
+class VSyncChannel : public pdx::Channel {
+ public:
+ VSyncChannel(pdx::Service& service, int pid, int cid)
+ : service_(service), pid_(pid), cid_(cid) {}
+
+ void Ack();
+ void Signal();
+
+ private:
+ pdx::Service& service_;
+ pid_t pid_;
+ int cid_;
+
+ VSyncChannel(const VSyncChannel&) = delete;
+ void operator=(const VSyncChannel&) = delete;
+};
+
+// VSyncService implements the displayd vsync service over ServiceFS.
+class VSyncService : public pdx::ServiceBase<VSyncService> {
+ public:
+ ~VSyncService() override;
+
+ pdx::Status<void> HandleMessage(pdx::Message& message) override;
+
+ std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
+ void OnChannelClose(pdx::Message& message,
+ const std::shared_ptr<pdx::Channel>& channel) override;
+
+ // Called by the hardware composer HAL, or similar, whenever a vsync event
+ // occurs. |compositor_time_ns| is the number of ns before the next vsync when
+ // the compositor will preempt the GPU to do EDS and lens warp.
+ void VSyncEvent(int display, int64_t timestamp_ns, int64_t compositor_time_ns,
+ uint32_t vsync_count);
+
+ private:
+ friend BASE;
+
+ VSyncService();
+
+ pdx::Status<int64_t> OnGetLastTimestamp(pdx::Message& message);
+ pdx::Status<display::VSyncSchedInfo> OnGetSchedInfo(pdx::Message& message);
+ pdx::Status<void> OnAcknowledge(pdx::Message& message);
+
+ void NotifierThreadFunction();
+
+ void AddWaiter(pdx::Message& message);
+ void NotifyWaiters();
+ void UpdateClients();
+
+ void AddClient(const std::shared_ptr<VSyncChannel>& client);
+ void RemoveClient(const std::shared_ptr<VSyncChannel>& client);
+
+ int64_t last_vsync_;
+ int64_t current_vsync_;
+ int64_t compositor_time_ns_;
+ uint32_t current_vsync_count_;
+
+ std::mutex mutex_;
+
+ std::list<std::unique_ptr<VSyncWaiter>> waiters_;
+ std::list<std::shared_ptr<VSyncChannel>> clients_;
+
+ VSyncService(const VSyncService&) = delete;
+ void operator=(VSyncService&) = delete;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
new file mode 100644
index 0000000000..abad78b338
--- /dev/null
+++ b/libs/vr/libvrsensor/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2015 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.
+
+sourceFiles = [
+ "pose_client.cpp",
+ "sensor_client.cpp",
+ "latency_model.cpp",
+]
+
+includeFiles = [
+ "include",
+]
+
+staticLibraries = [
+ "libbufferhub",
+ "libbufferhubqueue",
+ "libdvrcommon",
+ "libpdx_default_transport",
+]
+
+sharedLibraries = [
+ "libbase",
+ "libcutils",
+ "libhardware",
+ "liblog",
+ "libutils",
+ "libui",
+]
+
+cc_library {
+ srcs: sourceFiles,
+ export_include_dirs: includeFiles,
+ static_libs: staticLibraries,
+ shared_libs: sharedLibraries,
+ name: "libvrsensor",
+}
+
diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg
new file mode 100644
index 0000000000..2f8a3c018c
--- /dev/null
+++ b/libs/vr/libvrsensor/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
new file mode 100644
index 0000000000..ed75f84216
--- /dev/null
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -0,0 +1,205 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_H_
+#define ANDROID_DVR_POSE_CLIENT_H_
+
+#ifdef __ARM_NEON
+#include <arm_neon.h>
+#else
+#ifndef __FLOAT32X4T_86
+#define __FLOAT32X4T_86
+typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
+typedef struct float32x4x4_t { float32x4_t val[4]; };
+#endif
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrPose DvrPose;
+
+// Represents the current state provided by the pose service, containing a
+// rotation and translation.
+typedef struct __attribute__((packed, aligned(8))) DvrPoseState {
+ // A quaternion representing the rotation of the HMD in Start Space.
+ struct __attribute__((packed)) {
+ float x, y, z, w;
+ } head_from_start_rotation;
+ // The position of the HMD in Start Space.
+ struct __attribute__((packed)) {
+ float x, y, z;
+ } head_from_start_translation;
+ // Time in nanoseconds for the current pose.
+ uint64_t timestamp_ns;
+ // The rotational velocity of the HMD.
+ struct __attribute__((packed)) {
+ float x, y, z;
+ } sensor_from_start_rotation_velocity;
+} DvrPoseState;
+
+enum {
+ DVR_POSE_FLAG_VALID = (1UL << 0), // This pose is valid.
+ DVR_POSE_FLAG_HEAD = (1UL << 1), // This pose is the head.
+ DVR_POSE_FLAG_CONTROLLER = (1UL << 2), // This pose is a controller.
+};
+
+// Represents an estimated pose, accessed asynchronously through a shared ring
+// buffer. No assumptions should be made about the data in padding space.
+// The size of this struct is 128 bytes.
+typedef struct __attribute__((packed, aligned(16))) DvrPoseAsync {
+ // Left eye head-from-start orientation quaternion x,y,z,w.
+ float32x4_t orientation;
+ // Left eye head-from-start translation x,y,z,pad in meters.
+ float32x4_t translation;
+ // Right eye head-from-start orientation quaternion x,y,z,w.
+ float32x4_t right_orientation;
+ // Right eye head-from-start translation x,y,z,pad in meters.
+ float32x4_t right_translation;
+ // Start-space angular velocity x,y,z,pad in radians per second.
+ float32x4_t angular_velocity;
+ // Start-space positional velocity x,y,z,pad in meters per second.
+ float32x4_t velocity;
+ // Timestamp of when this pose is predicted for, typically halfway through
+ // scanout.
+ int64_t timestamp_ns;
+ // Bitmask of DVR_POSE_FLAG_* constants that apply to this pose.
+ //
+ // If DVR_POSE_FLAG_VALID is not set, the pose is indeterminate.
+ uint64_t flags;
+ // Reserved padding to 128 bytes.
+ uint8_t pad[16];
+} DvrPoseAsync;
+
+// Returned by the async pose ring buffer access API.
+typedef struct DvrPoseRingBufferInfo {
+ // Read-only pointer to the pose ring buffer. The current pose is in this
+ // buffer at element buffer[current_frame & (buffer_size - 1)]. The next
+ // frame's forecasted pose is at element
+ // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are
+ // predicted for when 50% of the corresponding frame's pixel data is visible
+ // to the user.
+ // The last value returned by dvrPresent is the count for the next frame,
+ // which is the earliest that the application could display something if they
+ // were to render promptly. (TODO(jbates) move this comment to dvrPresent).
+ volatile const DvrPoseAsync* buffer;
+ // Minimum number of accurate forecasted poses including the current frame's
+ // pose. This is the number of poses that are udpated by the pose service.
+ // If the application reads past this count, they will get a stale prediction
+ // from a previous frame. Guaranteed to be at least 2.
+ uint32_t min_future_count;
+ // Number of elements in buffer. At least 8 and greater than min_future_count.
+ // Guaranteed to be a power of two. The total size of the buffer in bytes is:
+ // total_count * sizeof(DvrPoseAsync)
+ uint32_t total_count;
+} DvrPoseRingBufferInfo;
+
+typedef enum DvrPoseMode {
+ DVR_POSE_MODE_6DOF = 0,
+ DVR_POSE_MODE_3DOF,
+ DVR_POSE_MODE_MOCK_FROZEN,
+ DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW,
+ DVR_POSE_MODE_MOCK_HEAD_TURN_FAST,
+ DVR_POSE_MODE_MOCK_ROTATE_SLOW,
+ DVR_POSE_MODE_MOCK_ROTATE_MEDIUM,
+ DVR_POSE_MODE_MOCK_ROTATE_FAST,
+ DVR_POSE_MODE_MOCK_CIRCLE_STRAFE,
+
+ // Always last.
+ DVR_POSE_MODE_COUNT,
+} DvrPoseMode;
+
+typedef enum DvrControllerId {
+ DVR_CONTROLLER_0 = 0,
+ DVR_CONTROLLER_1 = 1,
+} DvrControllerId;
+
+// Creates a new pose client.
+//
+// @return Pointer to the created pose client, nullptr on failure.
+DvrPose* dvrPoseCreate();
+
+// Destroys a pose client.
+//
+// @param client Pointer to the pose client to be destroyed.
+void dvrPoseDestroy(DvrPose* client);
+
+// Gets the pose for the given vsync count.
+//
+// @param client Pointer to the pose client.
+// @param vsync_count Vsync that this pose should be forward-predicted to.
+// Typically this is the count returned by dvrGetNextVsyncCount.
+// @param out_pose Struct to store pose state.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose);
+
+// Gets the current vsync count.
+uint32_t dvrPoseGetVsyncCount(DvrPose* client);
+
+// Gets the pose for the given controller at the given vsync count.
+//
+// @param client Pointer to the pose client.
+// @param controller_id The controller id.
+// @param vsync_count Vsync that this pose should be forward-predicted to.
+// Typically this is the count returned by dvrGetNextVsyncCount.
+// @param out_pose Struct to store pose state.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+ uint32_t vsync_count, DvrPoseAsync* out_pose);
+
+// Enables/disables logging for the controller fusion.
+//
+// @param client Pointer to the pose client.
+// @param enable True starts logging, False stops.
+// @return Zero on success, negative error code on failure.
+int dvrPoseLogController(DvrPose* client, bool enable);
+
+// DEPRECATED
+// Polls current pose state.
+//
+// @param client Pointer to the pose client.
+// @param state Struct to store polled state.
+// @return Zero on success, negative error code on failure.
+int dvrPosePoll(DvrPose* client, DvrPoseState* state);
+
+// Freezes the pose to the provided state.
+//
+// Future poll operations will return this state until a different state is
+// frozen or dvrPoseSetMode() is called with a different mode. The timestamp is
+// not frozen.
+//
+// @param client Pointer to the pose client.
+// @param frozen_state State pose to be frozen to.
+// @return Zero on success, negative error code on failure.
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state);
+
+// Sets the pose service mode.
+//
+// @param mode The requested pose mode.
+// @return Zero on success, negative error code on failure.
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode);
+
+// Gets the pose service mode.
+//
+// @param mode Return value for the current pose mode.
+// @return Zero on success, negative error code on failure.
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode);
+
+// Get access to the shared memory pose ring buffer.
+// A future pose at vsync <current> + <offset> is accessed at index:
+// index = (<current> + <offset>) % out_buffer_size
+// Where <current> was the last value returned by dvrPresent and
+// <offset> is less than or equal to |out_min_future_count|.
+// |out_buffer| will be set to a pointer to the buffer.
+// |out_fd| will be set to the gralloc buffer file descriptor, which is
+// required for binding this buffer for GPU use.
+// Returns 0 on success.
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_POSE_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
new file mode 100644
index 0000000000..40b4638d69
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
@@ -0,0 +1,29 @@
+#ifndef ANDROID_DVR_LATENCY_MODEL_H_
+#define ANDROID_DVR_LATENCY_MODEL_H_
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// This class models the latency from sensors. It will look at the first
+// window_size measurements and return their average after that.
+class LatencyModel {
+ public:
+ LatencyModel(size_t window_size);
+ ~LatencyModel() = default;
+
+ void AddLatency(int64_t latency_ns);
+ int64_t CurrentLatencyEstimate() const { return latency_; }
+
+ private:
+ size_t window_size_;
+ int64_t latency_sum_ = 0;
+ size_t num_summed_ = 0;
+ int64_t latency_ = 0;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_LATENCY_MODEL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
new file mode 100644
index 0000000000..0616d46106
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
@@ -0,0 +1,28 @@
+#ifndef ANDROID_DVR_POSE_IPC_H_
+#define ANDROID_DVR_POSE_IPC_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DVR_POSE_SERVICE_BASE "system/vr/pose"
+#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
+
+enum {
+ DVR_POSE_POLL = 0,
+ DVR_POSE_FREEZE,
+ DVR_POSE_SET_MODE,
+ DVR_POSE_GET_RING_BUFFER,
+ DVR_POSE_NOTIFY_VSYNC,
+ DVR_POSE_GET_MODE,
+ DVR_POSE_GET_CONTROLLER_RING_BUFFER,
+ DVR_POSE_LOG_CONTROLLER,
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_POSE_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
new file mode 100644
index 0000000000..66c4c7c49d
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
+
+#include <stdint.h>
+
+#include <dvr/pose_client.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/sensor_constants.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Sensord head pose ring buffer.
+typedef struct __attribute__((packed, aligned(16))) DvrPoseRingBuffer {
+ // Ring buffer always at the beginning of the structure, as consumers may
+ // not have access to this parent structure definition.
+ DvrPoseAsync ring[kPoseAsyncBufferTotalCount];
+ // Current vsync_count (where sensord is writing poses from).
+ uint32_t vsync_count;
+} DvrPoseMetadata;
+
+// Called by displayd to give vsync count info to the pose service.
+// |display_timestamp| Display timestamp is in the middle of scanout.
+// |display_period_ns| Nanos between vsyncs.
+// |right_eye_photon_offset_ns| Nanos to shift the prediction timestamp for
+// the right eye head pose (relative to the left eye prediction).
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+ int64_t display_timestamp,
+ int64_t display_period_ns,
+ int64_t right_eye_photon_offset_ns);
+
+// Get file descriptor for access to the shared memory pose buffer. This can be
+// used with GL extensions that support shared memory buffer objects. The caller
+// takes ownership of the returned fd and must close it or pass on ownership.
+int privateDvrPoseGetRingBufferFd(DvrPose* client,
+ android::pdx::LocalHandle* fd);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h
new file mode 100644
index 0000000000..b2ebd95060
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor-ipc.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_DVR_SENSOR_IPC_H_
+#define ANDROID_DVR_SENSOR_IPC_H_
+
+#define DVR_SENSOR_SERVICE_BASE "system/vr/sensors"
+
+#define DVR_SENSOR_SERVICE_CLIENT (DVR_SENSOR_SERVICE_BASE "/client")
+
+/*
+ * Endpoint ops
+ */
+enum {
+ DVR_SENSOR_START = 0,
+ DVR_SENSOR_STOP,
+ DVR_SENSOR_POLL,
+};
+
+#endif // ANDROID_DVR_SENSOR_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_client.h b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h
new file mode 100644
index 0000000000..15a9b8f5d1
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor_client.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_SENSOR_CLIENT_H_
+#define ANDROID_DVR_SENSOR_CLIENT_H_
+
+#include <hardware/sensors.h>
+#include <pdx/client.h>
+#include <poll.h>
+
+namespace android {
+namespace dvr {
+
+// SensorClient is a remote interface to the sensor service in sensord.
+class SensorClient : public pdx::ClientBase<SensorClient> {
+ public:
+ ~SensorClient();
+
+ int StartSensor();
+ int StopSensor();
+ int Poll(sensors_event_t* events, int max_count);
+
+ private:
+ friend BASE;
+
+ // Set up a channel associated with the sensor of the indicated type.
+ // NOTE(segal): If our hardware ends up with multiple sensors of the same
+ // type, we'll have to change this.
+ explicit SensorClient(int sensor_type);
+
+ int sensor_type_;
+
+ SensorClient(const SensorClient&);
+ SensorClient& operator=(const SensorClient&);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSOR_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h
new file mode 100644
index 0000000000..8fa87b39a0
--- /dev/null
+++ b/libs/vr/libvrsensor/include/private/dvr/sensor_constants.h
@@ -0,0 +1,23 @@
+#ifndef ANDROID_DVR_SENSOR_CONSTANTS_H_
+#define ANDROID_DVR_SENSOR_CONSTANTS_H_
+
+namespace android {
+namespace dvr {
+
+// Number of elements in the async pose buffer.
+// Must be power of two.
+// Macro so that shader code can easily include this value.
+#define kPoseAsyncBufferTotalCount 8
+
+// Mask for accessing the current ring buffer array element:
+// index = vsync_count & kPoseAsyncBufferIndexMask
+constexpr uint32_t kPoseAsyncBufferIndexMask = kPoseAsyncBufferTotalCount - 1;
+
+// Number of pose frames including the current frame that are kept updated with
+// pose forecast data. The other poses are left their last known estimates.
+constexpr uint32_t kPoseAsyncBufferMinFutureCount = 4;
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSOR_CONSTANTS_H_
diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp
new file mode 100644
index 0000000000..d3a45210a7
--- /dev/null
+++ b/libs/vr/libvrsensor/latency_model.cpp
@@ -0,0 +1,24 @@
+#include <private/dvr/latency_model.h>
+
+#include <cmath>
+
+namespace android {
+namespace dvr {
+
+LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {}
+
+void LatencyModel::AddLatency(int64_t latency_ns) {
+ // Not enough samples yet?
+ if (num_summed_ < window_size_) {
+ // Accumulate.
+ latency_sum_ += latency_ns;
+
+ // Have enough samples for latency estimate?
+ if (++num_summed_ == window_size_) {
+ latency_ = latency_sum_ / window_size_;
+ }
+ }
+}
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
new file mode 100644
index 0000000000..9eae3aab86
--- /dev/null
+++ b/libs/vr/libvrsensor/pose_client.cpp
@@ -0,0 +1,338 @@
+#define LOG_TAG "PoseClient"
+#include <dvr/pose_client.h>
+
+#include <stdint.h>
+
+#include <log/log.h>
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sensor_constants.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+
+#define arraysize(x) (static_cast<int32_t>(std::extent<decltype(x)>::value))
+
+namespace android {
+namespace dvr {
+
+// PoseClient is a remote interface to the pose service in sensord.
+class PoseClient : public pdx::ClientBase<PoseClient> {
+ public:
+ ~PoseClient() override {}
+
+ // Casts C handle into an instance of this class.
+ static PoseClient* FromC(DvrPose* client) {
+ return reinterpret_cast<PoseClient*>(client);
+ }
+
+ // Polls the pose service for the current state and stores it in *state.
+ // Returns zero on success, a negative error code otherwise.
+ int Poll(DvrPoseState* state) {
+ Transaction trans{*this};
+ Status<int> status =
+ trans.Send<int>(DVR_POSE_POLL, nullptr, 0, state, sizeof(*state));
+ ALOGE_IF(!status, "Pose poll() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
+ if (!mapped_pose_buffer_) {
+ int ret = GetRingBuffer(nullptr);
+ if (ret < 0)
+ return ret;
+ }
+ *out_pose =
+ mapped_pose_buffer_->ring[vsync_count & kPoseAsyncBufferIndexMask];
+ return 0;
+ }
+
+ uint32_t GetVsyncCount() {
+ if (!mapped_pose_buffer_) {
+ int ret = GetRingBuffer(nullptr);
+ if (ret < 0)
+ return 0;
+ }
+ return mapped_pose_buffer_->vsync_count;
+ }
+
+ int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
+ DvrPoseAsync* out_pose) {
+ if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+ return -EINVAL;
+ }
+ if (!controllers_[controller_id].mapped_pose_buffer) {
+ int ret = GetControllerRingBuffer(controller_id);
+ if (ret < 0)
+ return ret;
+ }
+ *out_pose =
+ controllers_[controller_id]
+ .mapped_pose_buffer[vsync_count & kPoseAsyncBufferIndexMask];
+ return 0;
+ }
+
+ int LogController(bool enable) {
+ Transaction trans{*this};
+ Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
+ sizeof(enable), nullptr, 0);
+ ALOGE_IF(!status, "Pose LogController() failed because: %s",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ // Freezes the pose to the provided state. Future poll operations will return
+ // this state until a different state is frozen or SetMode() is called with a
+ // different mode.
+ // Returns zero on success, a negative error code otherwise.
+ int Freeze(const DvrPoseState& frozen_state) {
+ Transaction trans{*this};
+ Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
+ sizeof(frozen_state), nullptr, 0);
+ ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ // Sets the data mode for the pose service.
+ int SetMode(DvrPoseMode mode) {
+ Transaction trans{*this};
+ Status<int> status =
+ trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
+ ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ // Gets the data mode for the pose service.
+ int GetMode(DvrPoseMode* out_mode) {
+ int mode;
+ Transaction trans{*this};
+ Status<int> status =
+ trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
+ ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
+ status.GetErrorMessage().c_str());
+ if (status)
+ *out_mode = DvrPoseMode(mode);
+ return ReturnStatusOrError(status);
+ }
+
+ int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
+ if (pose_buffer_.get()) {
+ if (out_info) {
+ GetPoseRingBufferInfo(out_info);
+ }
+ return 0;
+ }
+
+ Transaction trans{*this};
+ Status<LocalChannelHandle> status =
+ trans.Send<LocalChannelHandle>(DVR_POSE_GET_RING_BUFFER);
+ if (!status) {
+ ALOGE("Pose GetRingBuffer() failed because: %s",
+ status.GetErrorMessage().c_str());
+ return -status.error();
+ }
+
+ auto buffer = BufferConsumer::Import(status.take());
+ if (!buffer) {
+ ALOGE("Pose failed to import ring buffer");
+ return -EIO;
+ }
+ void* addr = nullptr;
+ int ret = buffer->GetBlobReadOnlyPointer(sizeof(DvrPoseRingBuffer), &addr);
+ if (ret < 0 || !addr) {
+ ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+ return -EIO;
+ }
+ pose_buffer_.swap(buffer);
+ mapped_pose_buffer_ = static_cast<const DvrPoseRingBuffer*>(addr);
+ ALOGI("Mapped pose data translation %f,%f,%f quat %f,%f,%f,%f",
+ mapped_pose_buffer_->ring[0].translation[0],
+ mapped_pose_buffer_->ring[0].translation[1],
+ mapped_pose_buffer_->ring[0].translation[2],
+ mapped_pose_buffer_->ring[0].orientation[0],
+ mapped_pose_buffer_->ring[0].orientation[1],
+ mapped_pose_buffer_->ring[0].orientation[2],
+ mapped_pose_buffer_->ring[0].orientation[3]);
+ if (out_info) {
+ GetPoseRingBufferInfo(out_info);
+ }
+ return 0;
+ }
+
+ int GetControllerRingBuffer(int32_t controller_id) {
+ if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+ return -EINVAL;
+ }
+ ControllerClientState& client_state = controllers_[controller_id];
+ if (client_state.pose_buffer.get()) {
+ return 0;
+ }
+
+ Transaction trans{*this};
+ Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
+ DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
+ sizeof(controller_id), nullptr, 0);
+ if (!status) {
+ return -status.error();
+ }
+
+ auto buffer = BufferConsumer::Import(status.take());
+ if (!buffer) {
+ ALOGE("Pose failed to import ring buffer");
+ return -EIO;
+ }
+ constexpr size_t size = kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync);
+ void* addr = nullptr;
+ int ret = buffer->GetBlobReadOnlyPointer(size, &addr);
+ if (ret < 0 || !addr) {
+ ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+ return -EIO;
+ }
+ client_state.pose_buffer.swap(buffer);
+ client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
+ ALOGI(
+ "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
+ controller_id, client_state.mapped_pose_buffer[0].translation[0],
+ client_state.mapped_pose_buffer[0].translation[1],
+ client_state.mapped_pose_buffer[0].translation[2],
+ client_state.mapped_pose_buffer[0].orientation[0],
+ client_state.mapped_pose_buffer[0].orientation[1],
+ client_state.mapped_pose_buffer[0].orientation[2],
+ client_state.mapped_pose_buffer[0].orientation[3]);
+ return 0;
+ }
+
+ int NotifyVsync(uint32_t vsync_count, int64_t display_timestamp,
+ int64_t display_period_ns,
+ int64_t right_eye_photon_offset_ns) {
+ const struct iovec data[] = {
+ {.iov_base = &vsync_count, .iov_len = sizeof(vsync_count)},
+ {.iov_base = &display_timestamp, .iov_len = sizeof(display_timestamp)},
+ {.iov_base = &display_period_ns, .iov_len = sizeof(display_period_ns)},
+ {.iov_base = &right_eye_photon_offset_ns,
+ .iov_len = sizeof(right_eye_photon_offset_ns)},
+ };
+ Transaction trans{*this};
+ Status<int> status =
+ trans.SendVector<int>(DVR_POSE_NOTIFY_VSYNC, data, nullptr);
+ ALOGE_IF(!status, "Pose NotifyVsync() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+ }
+
+ int GetRingBufferFd(LocalHandle* fd) {
+ int ret = GetRingBuffer(nullptr);
+ if (ret < 0)
+ return ret;
+ *fd = pose_buffer_->GetBlobFd();
+ return 0;
+ }
+
+ private:
+ friend BASE;
+
+ // Set up a channel to the pose service.
+ PoseClient()
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DVR_POSE_SERVICE_CLIENT)) {
+ // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
+ // block while waiting for the pose service to come back up.
+ EnableAutoReconnect(kInfiniteTimeout);
+ }
+
+ PoseClient(const PoseClient&) = delete;
+ PoseClient& operator=(const PoseClient&) = delete;
+
+ void GetPoseRingBufferInfo(DvrPoseRingBufferInfo* out_info) const {
+ out_info->min_future_count = kPoseAsyncBufferMinFutureCount;
+ out_info->total_count = kPoseAsyncBufferTotalCount;
+ out_info->buffer = mapped_pose_buffer_->ring;
+ }
+
+ std::unique_ptr<BufferConsumer> pose_buffer_;
+ const DvrPoseRingBuffer* mapped_pose_buffer_ = nullptr;
+
+ struct ControllerClientState {
+ std::unique_ptr<BufferConsumer> pose_buffer;
+ const DvrPoseAsync* mapped_pose_buffer = nullptr;
+ };
+ ControllerClientState controllers_[2];
+};
+
+} // namespace dvr
+} // namespace android
+
+using android::dvr::PoseClient;
+
+struct DvrPose {};
+
+extern "C" {
+
+DvrPose* dvrPoseCreate() {
+ PoseClient* client = PoseClient::Create().release();
+ return reinterpret_cast<DvrPose*>(client);
+}
+
+void dvrPoseDestroy(DvrPose* client) { delete PoseClient::FromC(client); }
+
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose) {
+ return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
+}
+
+uint32_t dvrPoseGetVsyncCount(DvrPose* client) {
+ return PoseClient::FromC(client)->GetVsyncCount();
+}
+
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+ uint32_t vsync_count, DvrPoseAsync* out_pose) {
+ return PoseClient::FromC(client)->GetControllerPose(controller_id,
+ vsync_count, out_pose);
+}
+
+int dvrPoseLogController(DvrPose* client, bool enable) {
+ return PoseClient::FromC(client)->LogController(enable);
+}
+
+int dvrPosePoll(DvrPose* client, DvrPoseState* state) {
+ return PoseClient::FromC(client)->Poll(state);
+}
+
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state) {
+ return PoseClient::FromC(client)->Freeze(*frozen_state);
+}
+
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode) {
+ return PoseClient::FromC(client)->SetMode(mode);
+}
+
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode) {
+ return PoseClient::FromC(client)->GetMode(mode);
+}
+
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info) {
+ return PoseClient::FromC(client)->GetRingBuffer(out_info);
+}
+
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+ int64_t display_timestamp,
+ int64_t display_period_ns,
+ int64_t right_eye_photon_offset_ns) {
+ return PoseClient::FromC(client)->NotifyVsync(vsync_count, display_timestamp,
+ display_period_ns,
+ right_eye_photon_offset_ns);
+}
+
+int privateDvrPoseGetRingBufferFd(DvrPose* client, LocalHandle* fd) {
+ return PoseClient::FromC(client)->GetRingBufferFd(fd);
+}
+
+} // extern "C"
diff --git a/libs/vr/libvrsensor/sensor_client.cpp b/libs/vr/libvrsensor/sensor_client.cpp
new file mode 100644
index 0000000000..04e88cc9a1
--- /dev/null
+++ b/libs/vr/libvrsensor/sensor_client.cpp
@@ -0,0 +1,79 @@
+#define LOG_TAG "SensorClient"
+#include <private/dvr/sensor_client.h>
+
+#include <log/log.h>
+#include <poll.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/sensor-ipc.h>
+
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+SensorClient::SensorClient(int sensor_type)
+ : BASE(pdx::default_transport::ClientChannelFactory::Create(
+ DVR_SENSOR_SERVICE_CLIENT)),
+ sensor_type_(sensor_type) {}
+
+SensorClient::~SensorClient() {}
+
+int SensorClient::StartSensor() {
+ Transaction trans{*this};
+ auto status = trans.Send<int>(DVR_SENSOR_START, &sensor_type_,
+ sizeof(sensor_type_), nullptr, 0);
+ ALOGE_IF(!status, "startSensor() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+}
+
+int SensorClient::StopSensor() {
+ Transaction trans{*this};
+ auto status = trans.Send<int>(DVR_SENSOR_STOP);
+ ALOGE_IF(!status, "stopSensor() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return ReturnStatusOrError(status);
+}
+
+int SensorClient::Poll(sensors_event_t* events, int max_events) {
+ int num_events = 0;
+ struct iovec rvec[] = {
+ {.iov_base = &num_events, .iov_len = sizeof(int)},
+ {.iov_base = events, .iov_len = max_events * sizeof(sensors_event_t)},
+ };
+ Transaction trans{*this};
+ auto status = trans.SendVector<int>(DVR_SENSOR_POLL, nullptr, rvec);
+ ALOGE_IF(!status, "Sensor poll() failed because: %s\n",
+ status.GetErrorMessage().c_str());
+ return !status ? -status.error() : num_events;
+}
+
+} // namespace dvr
+} // namespace android
+
+// Entrypoints to simplify using the library when programmatically dynamicly
+// loading it.
+// Allows us to call this library without linking it, as, for instance,
+// when compiling GVR in Google3.
+// NOTE(segal): It's kind of a hack.
+
+extern "C" uint64_t dvrStartSensor(int type) {
+ android::dvr::SensorClient* service =
+ android::dvr::SensorClient::Create(type).release();
+ service->StartSensor();
+ return (uint64_t)service;
+}
+
+extern "C" void dvrStopSensor(uint64_t service) {
+ android::dvr::SensorClient* iss =
+ reinterpret_cast<android::dvr::SensorClient*>(service);
+ iss->StopSensor();
+ delete iss;
+}
+
+extern "C" int dvrPollSensor(uint64_t service, int max_count,
+ sensors_event_t* events) {
+ return reinterpret_cast<android::dvr::SensorClient*>(service)->Poll(
+ events, max_count);
+}