diff options
21 files changed, 200 insertions, 33 deletions
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index fda6ea189e..9e9df5216f 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -711,7 +711,9 @@ void SkiaRenderEngine::drawLayersInternal( SkCanvas* canvas = dstCanvas; SkiaCapture::OffscreenState offscreenCaptureState; const LayerSettings* blurCompositionLayer = nullptr; - if (mBlurFilter) { + + // TODO (b/270314344): Enable blurs in protected context. + if (mBlurFilter && !mInProtectedContext) { bool requiresCompositionLayer = false; for (const auto& layer : layers) { // if the layer doesn't have blur or it is not visible then continue @@ -805,7 +807,8 @@ void SkiaRenderEngine::drawLayersInternal( const auto [bounds, roundRectClip] = getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop, layer.geometry.roundedCornersRadius); - if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) { + // TODO (b/270314344): Enable blurs in protected context. + if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha) && !mInProtectedContext) { std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; // if multiple layers have blur, then we need to take a snapshot now because @@ -813,8 +816,20 @@ void SkiaRenderEngine::drawLayersInternal( if (!blurInput) { blurInput = activeSurface->makeImageSnapshot(); } + // rect to be blurred in the coordinate space of blurInput - const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect()); + SkRect blurRect = canvas->getTotalMatrix().mapRect(bounds.rect()); + + // Some layers may be much bigger than the screen. If we used + // `blurRect` directly, this would allocate a large buffer with no + // benefit. Apply the clip, which already takes the display size + // into account. The clipped size will then be used to calculate the + // size of the buffer we will create for blurring. + if (!blurRect.intersect(SkRect::Make(canvas->getDeviceClipBounds()))) { + // This should not happen, but if it did, we would use the full + // sized layer, which should still be fine. + ALOGW("blur bounds does not intersect display clip!"); + } // if the clip needs to be applied then apply it now and make sure // it is restored before we attempt to draw any shadows. diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp index e3f709b7f5..9deba01dc8 100644 --- a/libs/ultrahdr/Android.bp +++ b/libs/ultrahdr/Android.bp @@ -14,11 +14,10 @@ package { // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_native_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_native_license"], + default_applicable_licenses: [ + "frameworks_native_license", + "adobe_hdr_gain_map_license", + ], } cc_library { diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp new file mode 100644 index 0000000000..e999a8bd28 --- /dev/null +++ b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp @@ -0,0 +1,19 @@ +// Copyright 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +license { + name: "adobe_hdr_gain_map_license", + license_kinds: ["legacy_by_exception_only"], + license_text: ["NOTICE"], +} diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE b/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE new file mode 100644 index 0000000000..3f6c5944c7 --- /dev/null +++ b/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE @@ -0,0 +1 @@ +This product includes Gain Map technology under license by Adobe. diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index fba64c7569..052efb6bbb 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -71,6 +71,7 @@ filegroup { cc_library_shared { name: "libgpuservice", defaults: ["libgpuservice_production_defaults"], + export_include_dirs: ["include"], srcs: [ ":libgpuservice_sources", ], diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index ba957f4b46..48d793a4d4 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -16,7 +16,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include "GpuService.h" +#include "gpuservice/GpuService.h" #include <android-base/stringprintf.h> #include <android-base/properties.h> @@ -35,6 +35,7 @@ #include <vkjson.h> #include <thread> +#include <memory> namespace android { @@ -58,18 +59,21 @@ GpuService::GpuService() mGpuStats(std::make_unique<GpuStats>()), mGpuMemTracer(std::make_unique<GpuMemTracer>()) { - std::thread gpuMemAsyncInitThread([this]() { + mGpuMemAsyncInitThread = std::make_unique<std::thread>([this] (){ mGpuMem->initialize(); mGpuMemTracer->initialize(mGpuMem); }); - gpuMemAsyncInitThread.detach(); - std::thread gpuWorkAsyncInitThread([this]() { + mGpuWorkAsyncInitThread = std::make_unique<std::thread>([this]() { mGpuWork->initialize(); }); - gpuWorkAsyncInitThread.detach(); }; +GpuService::~GpuService() { + mGpuWorkAsyncInitThread->join(); + mGpuMemAsyncInitThread->join(); +} + void GpuService::setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h index 0e559f2c34..54f8f666bc 100644 --- a/services/gpuservice/GpuService.h +++ b/services/gpuservice/include/gpuservice/GpuService.h @@ -24,6 +24,7 @@ #include <serviceutils/PriorityDumper.h> #include <mutex> +#include <thread> #include <vector> namespace android { @@ -41,6 +42,7 @@ public: static const char* const SERVICE_NAME ANDROID_API; GpuService() ANDROID_API; + ~GpuService(); protected: status_t shellCommand(int in, int out, int err, std::vector<String16>& args) override; @@ -90,6 +92,8 @@ private: std::unique_ptr<GpuMemTracer> mGpuMemTracer; std::mutex mLock; std::string mDeveloperDriverPath; + std::unique_ptr<std::thread> mGpuMemAsyncInitThread; + std::unique_ptr<std::thread> mGpuWorkAsyncInitThread; }; } // namespace android diff --git a/services/gpuservice/main_gpuservice.cpp b/services/gpuservice/main_gpuservice.cpp index 64aafcab6a..200237219e 100644 --- a/services/gpuservice/main_gpuservice.cpp +++ b/services/gpuservice/main_gpuservice.cpp @@ -18,7 +18,7 @@ #include <binder/IServiceManager.h> #include <binder/ProcessState.h> #include <sys/resource.h> -#include "GpuService.h" +#include "gpuservice/GpuService.h" using namespace android; diff --git a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp index c2574a3fd3..241b8646e1 100644 --- a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp +++ b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp @@ -16,7 +16,7 @@ #include <fuzzbinder/libbinder_driver.h> -#include "GpuService.h" +#include "gpuservice/GpuService.h" using ::android::fuzzService; using ::android::GpuService; diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp index 51642f9472..c870b17b79 100644 --- a/services/gpuservice/tests/unittests/Android.bp +++ b/services/gpuservice/tests/unittests/Android.bp @@ -28,6 +28,7 @@ cc_test { "GpuMemTest.cpp", "GpuMemTracerTest.cpp", "GpuStatsTest.cpp", + "GpuServiceTest.cpp", ], header_libs: ["bpf_headers"], shared_libs: [ @@ -45,6 +46,7 @@ cc_test { "libstatslog", "libstatspull", "libutils", + "libgpuservice", ], static_libs: [ "libgmock", diff --git a/services/gpuservice/tests/unittests/GpuServiceTest.cpp b/services/gpuservice/tests/unittests/GpuServiceTest.cpp new file mode 100644 index 0000000000..62b3e53f53 --- /dev/null +++ b/services/gpuservice/tests/unittests/GpuServiceTest.cpp @@ -0,0 +1,52 @@ +#undef LOG_TAG +#define LOG_TAG "gpuservice_unittest" + +#include "gpuservice/GpuService.h" + +#include <gtest/gtest.h> +#include <log/log_main.h> + +#include <chrono> +#include <thread> + +namespace android { +namespace { + +class GpuServiceTest : public testing::Test { +public: + GpuServiceTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + ~GpuServiceTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + +}; + + +/* +* The behaviour before this test + fixes was UB caused by threads accessing deallocated memory. +* +* This test creates the service (which initializes the culprit threads), +* deallocates it immediately and sleeps. +* +* GpuService's destructor gets called and joins the threads. +* If we haven't crashed by the time the sleep time has elapsed, we're good +* Let the test pass. +*/ +TEST_F(GpuServiceTest, onInitializeShouldNotCauseUseAfterFree) { + sp<GpuService> service = new GpuService(); + service.clear(); + std::this_thread::sleep_for(std::chrono::seconds(3)); + + // If we haven't crashed yet due to threads accessing freed up memory, let the test pass + EXPECT_TRUE(true); +} + +} // namespace +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 0fb79d59fc..5c524d3104 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -662,7 +662,15 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, } else { // This pointer was already sent to the window. Use ACTION_HOVER_MOVE. if (CC_UNLIKELY(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE)) { - LOG(FATAL) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription(); + android::base::LogSeverity severity = android::base::LogSeverity::FATAL; + if (entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) { + // The Accessibility injected touch exploration event stream + // has known inconsistencies, so log ERROR instead of + // crashing the device with FATAL. + // TODO(b/286037469): Move a11y severity back to FATAL. + severity = android::base::LogSeverity::ERROR; + } + LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription(); } touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_IS; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 017f10baf8..6ff420d951 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -3604,6 +3604,29 @@ TEST_F(InputDispatcherTest, HoverExitIsSentToRemovedWindow) { } /** + * Test that invalid HOVER events sent by accessibility do not cause a fatal crash. + */ +TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 1200, 800)); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + MotionEventBuilder hoverEnterBuilder = + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400)) + .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, hoverEnterBuilder.build())); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, hoverEnterBuilder.build())); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)); +} + +/** * If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT. */ TEST_F(InputDispatcherTest, TouchDownAfterMouseHover) { diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index f7049b98e7..c0eb36dc02 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -20,6 +20,7 @@ #include "AidlComposerHal.h" +#include <SurfaceFlingerProperties.h> #include <android-base/file.h> #include <android/binder_ibinder_platform.h> #include <android/binder_manager.h> @@ -249,15 +250,18 @@ AidlComposer::AidlComposer(const std::string& serviceName) { ALOGE("getInterfaceVersion for AidlComposer constructor failed %s", status.getDescription().c_str()); } - if (version == 1) { - mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888, - GraphicBuffer::USAGE_HW_COMPOSER | - GraphicBuffer::USAGE_SW_READ_OFTEN | - GraphicBuffer::USAGE_SW_WRITE_OFTEN, - "AidlComposer"); - if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) { - LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots"); - return; + mSupportsBufferSlotsToClear = version > 1; + if (!mSupportsBufferSlotsToClear) { + if (sysprop::clear_slots_with_set_layer_buffer(false)) { + mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888, + GraphicBuffer::USAGE_HW_COMPOSER | + GraphicBuffer::USAGE_SW_READ_OFTEN | + GraphicBuffer::USAGE_SW_WRITE_OFTEN, + "AidlComposer"); + if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) { + LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots"); + return; + } } } @@ -844,12 +848,12 @@ Error AidlComposer::setLayerBufferSlotsToClear(Display display, Layer layer, Error error = Error::NONE; mMutex.lock_shared(); if (auto writer = getWriter(display)) { - // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder - // buffer, using the slot that needs to cleared... tricky. - if (mClearSlotBuffer == nullptr) { + if (mSupportsBufferSlotsToClear) { writer->get().setLayerBufferSlotsToClear(translate<int64_t>(display), translate<int64_t>(layer), slotsToClear); - } else { + // Backwards compatible way of clearing buffer slots is to set the layer buffer with a + // placeholder buffer, using the slot that needs to cleared... tricky. + } else if (mClearSlotBuffer != nullptr) { for (uint32_t slot : slotsToClear) { // Don't clear the active buffer slot because we need to restore the active buffer // after clearing the requested buffer slots with a placeholder buffer. diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index ce05b382cc..b8ae26f879 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -284,6 +284,8 @@ private: // threading annotations. ftl::SharedMutex mMutex; + // Whether or not explicitly clearing buffer slots is supported. + bool mSupportsBufferSlotsToClear; // Buffer slots for layers are cleared by setting the slot buffer to this buffer. sp<GraphicBuffer> mClearSlotBuffer; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index e0f6c45f70..9b41da5754 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -24,12 +24,14 @@ #include "HidlComposerHal.h" +#include <SurfaceFlingerProperties.h> #include <android/binder_manager.h> #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> #include <hidl/HidlTransportSupport.h> #include <hidl/HidlTransportUtils.h> #include <log/log.h> #include <utils/Trace.h> + #include "HWC2.h" #include "Hal.h" @@ -189,6 +191,9 @@ std::vector<To> translate(const hidl_vec<From>& in) { } sp<GraphicBuffer> allocateClearSlotBuffer() { + if (!sysprop::clear_slots_with_set_layer_buffer(false)) { + return nullptr; + } sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888, GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_SW_READ_OFTEN | @@ -246,7 +251,7 @@ HidlComposer::HidlComposer(const std::string& serviceName) LOG_ALWAYS_FATAL("failed to create composer client"); } - if (!mClearSlotBuffer) { + if (!mClearSlotBuffer && sysprop::clear_slots_with_set_layer_buffer(false)) { LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots"); return; } @@ -716,7 +721,11 @@ Error HidlComposer::setLayerBufferSlotsToClear(Display display, Layer layer, if (slotsToClear.empty()) { return Error::NONE; } - // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder + // This can be null when the HAL hasn't explicitly enabled this feature. + if (mClearSlotBuffer == nullptr) { + return Error::NONE; + } + // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder // buffer, using the slot that needs to cleared... tricky. for (uint32_t slot : slotsToClear) { // Don't clear the active buffer slot because we need to restore the active buffer after diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 20fa091730..96c8b54005 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -375,5 +375,9 @@ bool ignore_hdr_camera_layers(bool defaultValue) { return SurfaceFlingerProperties::ignore_hdr_camera_layers().value_or(defaultValue); } +bool clear_slots_with_set_layer_buffer(bool defaultValue) { + return SurfaceFlingerProperties::clear_slots_with_set_layer_buffer().value_or(defaultValue); +} + } // namespace sysprop } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 080feee686..951f8f8cb3 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -102,6 +102,8 @@ bool enable_sdr_dimming(bool defaultValue); bool ignore_hdr_camera_layers(bool defaultValue); +bool clear_slots_with_set_layer_buffer(bool defaultValue); + } // namespace sysprop } // namespace android #endif // SURFACEFLINGERPROPERTIES_H_ diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 4d03be04b3..9b9ac96cf7 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -292,7 +292,7 @@ private: // MessageQueue overrides: void scheduleFrame() override {} - void postMessage(sp<MessageHandler>&&) override {} + void postMessage(sp<MessageHandler>&& handler) override { handler->handleMessage(Message()); } }; } // namespace scheduler diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index bcbe21a483..689f51ad5b 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -470,4 +470,18 @@ prop { scope: Public access: Readonly prop_name: "ro.surface_flinger.ignore_hdr_camera_layers" -}
\ No newline at end of file +} + +# When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for +# buffers when they are evicted from the app cache by using additional setLayerBuffer commands. +# Ideally, this behavior would always be enabled to reduce graphics memory consumption. However, +# Some HAL implementations may not support the additional setLayerBuffer commands used to clear +# the cache slots. +prop { + api_name: "clear_slots_with_set_layer_buffer" + type: Boolean + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer" +} + diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index 348a462038..9660ff3de6 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -1,6 +1,10 @@ props { module: "android.sysprop.SurfaceFlingerProperties" prop { + api_name: "clear_slots_with_set_layer_buffer" + prop_name: "ro.surface_flinger.clear_slots_with_set_layer_buffer" + } + prop { api_name: "color_space_agnostic_dataspace" type: Long prop_name: "ro.surface_flinger.color_space_agnostic_dataspace" |