diff options
Diffstat (limited to 'libs')
166 files changed, 6093 insertions, 3747 deletions
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 1a9766d72e..319716ec81 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["frameworks_native_libs_arect_license"], + default_team: "trendy_team_android_core_graphics_stack", } // Added automatically by a large-scale-change diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h index ce9cd1cebe..7da8d51ccd 100644 --- a/libs/battery/MultiStateCounter.h +++ b/libs/battery/MultiStateCounter.h @@ -31,6 +31,8 @@ namespace android { namespace battery { +#define REPORTED_INVALID_TIMESTAMP_DELTA_MS 60000 + typedef uint16_t state_t; template <class T> @@ -171,8 +173,12 @@ void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { if (timestamp >= lastStateChangeTimestamp) { states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp; } else { - ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n", - (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp); + if (timestamp < lastStateChangeTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) { + ALOGE("setState is called with an earlier timestamp: %lu, " + "previous timestamp: %lu\n", + (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp); + } + // The accumulated durations have become unreliable. For example, if the timestamp // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas, // we would get 4000, which is greater than (last - first). This could lead to @@ -232,8 +238,10 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { } } } else if (timestamp < lastUpdateTimestamp) { - ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n", - (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp); + if (timestamp < lastUpdateTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) { + ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n", + (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp); + } for (int i = 0; i < stateCount; i++) { states[i].timeInStateSinceUpdate = 0; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 2dd310e9ca..1ffdad50e3 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -2976,14 +2976,15 @@ status_t Parcel::restartWrite(size_t desired) return continueWrite(desired); } + releaseObjects(); + uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero); if (!data && desired > mDataCapacity) { + LOG_ALWAYS_FATAL("out of memory"); mError = NO_MEMORY; return NO_MEMORY; } - releaseObjects(); - if (data || desired == 0) { LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired); if (mDataCapacity > desired) { diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 5c280f4b2c..e378b864f7 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -115,6 +115,14 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { p.setDataPosition(pos); FUZZ_LOG() << "setDataPosition done"; }, + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024); + std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len); + FUZZ_LOG() << "about to setData: " <<(bytes.data() ? HexString(bytes.data(), bytes.size()) : "null"); + // TODO: allow all read and write operations + (*const_cast<::android::Parcel*>(&p)).setData(bytes.data(), bytes.size()); + FUZZ_LOG() << "setData done"; + }, PARCEL_READ_NO_STATUS(size_t, allowFds), PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders), diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index 3fe71cefce..3c8c41fdc8 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library_headers { diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp index 365fc457d1..03ab31e318 100644 --- a/libs/bufferstreams/Android.bp +++ b/libs/bufferstreams/Android.bp @@ -14,11 +14,13 @@ package { default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } aconfig_declarations { name: "bufferstreams_flags", package: "com.android.graphics.bufferstreams.flags", + container: "system", srcs: [ "aconfig/bufferstreams_flags.aconfig", ], diff --git a/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig index e258725e3d..d0f7812d21 100644 --- a/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig +++ b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.graphics.bufferstreams.flags" +container: "system" flag { name: "bufferstreams_steel_thread" diff --git a/libs/bufferstreams/aidl/Android.bp b/libs/bufferstreams/aidl/Android.bp new file mode 100644 index 0000000000..3f1fa4e532 --- /dev/null +++ b/libs/bufferstreams/aidl/Android.bp @@ -0,0 +1,43 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +aidl_interface { + name: "android.graphics.bufferstreams", + unstable: true, + flags: ["-Werror"], + srcs: ["android/graphics/bufferstreams/*.aidl"], + headers: [ + "HardwareBuffer_aidl", + ], + imports: [ + "android.hardware.common-V2", + ], + backend: { + cpp: { + enabled: false, + }, + java: { + enabled: false, + }, + ndk: { + enabled: false, + }, + rust: { + enabled: true, + additional_rustlibs: [ + "libnativewindow_rs", + ], + }, + }, +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl new file mode 100644 index 0000000000..5c905b1d17 --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.graphics.bufferstreams.IBufferOwner; +import android.hardware.HardwareBuffer; + +// Single mapping between a buffer reference and heavy-weight data (like the +// buffer itself) and data that is stable between frames. +parcelable BufferAttachment { + // The HardwareBuffer itself. + // + // This field is @nullable for codegen, since HardwareBuffer doesn't implement Default in Rust. + // In practice, it should never be null. + @nullable HardwareBuffer buffer; + // The buffer owner to which this buffer should be returned. + IBufferOwner owner; +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl new file mode 100644 index 0000000000..75041196a8 --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.graphics.bufferstreams.BufferAttachment; + +// A event that changes the state downstream buffer caches. Clients are responsible for forwarding +// these messages to their clients. +union BufferCacheUpdate { + // Event requiring downstream caches to add new entries. + CacheBuffers cacheBuffers; + // Event requiring downstream caches to remove entries. + ForgetBuffers forgetBuffers; + + parcelable CacheBuffers { + // Attachments to add. + List<BufferAttachment> attachments; + } + + parcelable ForgetBuffers { + // References to remove. + long[] bufferIds; + } +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl new file mode 100644 index 0000000000..1e0ec3b5db --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.os.ParcelFileDescriptor; + +// A Frame represents a single buffer passing through the stream. +parcelable Frame { + // The service must have provided an associated BufferAttachment and the client is required to + // maintain a cache between the two. + long bufferId; + // The expected present time of this frame, or -1 if immediate. + long presentTimeNs; + // The acquire fence of the buffer for this frame. + @nullable ParcelFileDescriptor fence; +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl new file mode 100644 index 0000000000..8b25a6298e --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.os.ParcelFileDescriptor; + +// Interface from a client back to the owner of a buffer. +interface IBufferOwner { + // Called when the buffer is done being processed by the stream to return its owner. + oneway void onBufferReleased(in long bufferId, in @nullable ParcelFileDescriptor releaseFence); +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl new file mode 100644 index 0000000000..52e8216e60 --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +import android.graphics.bufferstreams.BufferCacheUpdate; +import android.graphics.bufferstreams.IBufferSubscription; +import android.graphics.bufferstreams.Frame; + +// Interface provided by clients to a service, mirroring the non-IPC interface. +// +// Clients are required to maintain a local cache of Buffer IDs to BufferAttachments. +interface IBufferSubscriber { + // Provide a BufferSubscription object which the client can use to request frames. + oneway void onSubscribe(in IBufferSubscription subscription); + + // Notifies the client to update its local caches. + oneway void onBufferCacheUpdate(in BufferCacheUpdate update); + + // Notifies the client that a requested frame is available. + oneway void onNext(in Frame frame); + + // Notifies the client that a fatal error has occurred. No subsequent on_next events will be + // sent by the service. + // + // Clients must empty their caches. + oneway void onError(); + + // Notifies the client that no further on_next events will be sent by the service in response + // to it cancelling the subscription. + // + // Clients must empty their caches. + oneway void onComplete(); +} diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl new file mode 100644 index 0000000000..c37f4e68ea --- /dev/null +++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.bufferstreams; + +// Interface provided to a IBufferSubscriber to request frames or gracefully cancel their +// subscription. +interface IBufferSubscription { + // Request n more frames. + oneway void request(long n); + // Cancel the subscription. Requested frames may continue to arrive. + oneway void cancel(); +} diff --git a/libs/bufferstreams/examples/app/Android.bp b/libs/bufferstreams/examples/app/Android.bp index bb573c596c..5b3ec30bd5 100644 --- a/libs/bufferstreams/examples/app/Android.bp +++ b/libs/bufferstreams/examples/app/Android.bp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", +} + android_app { name: "BufferStreamsDemoApp", srcs: ["java/**/*.kt"], diff --git a/libs/bufferstreams/examples/app/jni/Android.bp b/libs/bufferstreams/examples/app/jni/Android.bp index 67910a1c4d..003f4ed2ad 100644 --- a/libs/bufferstreams/examples/app/jni/Android.bp +++ b/libs/bufferstreams/examples/app/jni/Android.bp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", +} + cc_library_shared { name: "libbufferstreamdemoapp", cflags: [ diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp index 7fcb222085..34feb5d2a5 100644 --- a/libs/bufferstreams/rust/Android.bp +++ b/libs/bufferstreams/rust/Android.bp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", +} + rust_defaults { name: "libbufferstreams_defaults", srcs: ["src/lib.rs"], diff --git a/libs/bufferstreams/rust/src/buffers/buffer_owner.rs b/libs/bufferstreams/rust/src/buffers/buffer_owner.rs index a4abb9d3b7..155a8bf238 100644 --- a/libs/bufferstreams/rust/src/buffers/buffer_owner.rs +++ b/libs/bufferstreams/rust/src/buffers/buffer_owner.rs @@ -16,7 +16,7 @@ use super::Buffer; /// Trait that represents an owner of a buffer that might need to handle events such as a buffer /// being dropped. -pub trait BufferOwner { +pub trait BufferOwner: Send + Sync { /// Called when a buffer is dropped. fn on_return(&self, buffer: &Buffer); } diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs index be1525d41f..17d4d8767f 100644 --- a/libs/bufferstreams/rust/src/lib.rs +++ b/libs/bufferstreams/rust/src/lib.rs @@ -23,8 +23,6 @@ pub mod subscriptions; use buffers::Buffer; pub use stream_config::*; -use std::time::Instant; - /// This function will print Hello World. #[no_mangle] pub extern "C" fn hello() -> bool { @@ -106,7 +104,8 @@ pub trait BufferSubscriber { /// BufferSubscriptions serve as the bridge between BufferPublishers and /// BufferSubscribers. BufferSubscribers receive a BufferSubscription when they /// subscribe to a BufferPublisher via on_subscribe. -/// This object is to be used by the BufferSubscriber to cancel its subscription +/// +/// This object is used by the BufferSubscriber to cancel its subscription /// or request more buffers. /// /// BufferSubcriptions are required to adhere to the following, based on the @@ -147,7 +146,7 @@ pub trait BufferSubscriber { /// no other Subscription exists at this point. /// * Calling Subscription.cancel MUST return normally. /// * Calling Subscription.request MUST return normally. -pub trait BufferSubscription { +pub trait BufferSubscription: Send + Sync + 'static { /// request fn request(&self, n: u64); /// cancel @@ -161,8 +160,8 @@ pub type BufferError = anyhow::Error; pub struct Frame { /// A buffer to be used this frame. pub buffer: Buffer, - /// The time at which the buffer was dispatched. - pub present_time: Instant, + /// The time at which this buffer is expected to be displayed. + pub present_time: i64, /// A fence used for reading/writing safely. pub fence: i32, } @@ -175,14 +174,12 @@ mod test { use anyhow::anyhow; use buffers::Buffer; use nativewindow::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags}; - use std::borrow::BorrowMut; - use std::error::Error; - use std::ops::Add; - use std::sync::Arc; - use std::time::Duration; + use std::{borrow::BorrowMut, error::Error, ops::Add, sync::Arc}; - use crate::publishers::testing::*; - use crate::subscribers::{testing::*, SharedSubscriber}; + use crate::{ + publishers::testing::*, + subscribers::{testing::*, SharedSubscriber}, + }; const STREAM_CONFIG: StreamConfig = StreamConfig { width: 1, @@ -200,7 +197,7 @@ mod test { .create_hardware_buffer() .expect("Unable to create hardware buffer for test"), ), - present_time: Instant::now() + Duration::from_secs(1), + present_time: 1, fence: 0, } } diff --git a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs index 846105dacd..73a15be897 100644 --- a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs +++ b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs @@ -14,8 +14,6 @@ //! -use std::time::Instant; - use crate::{ buffers::BufferPool, subscriptions::SharedBufferSubscription, BufferPublisher, BufferSubscriber, Frame, StreamConfig, @@ -43,7 +41,7 @@ impl BufferPoolPublisher { /// If the [SharedBufferSubscription] is ready for a [Frame], a buffer will be requested from /// [BufferPool] and sent over to the [BufferSubscriber]. - pub fn send_next_frame(&mut self, present_time: Instant) -> bool { + pub fn send_next_frame(&mut self, present_time: i64) -> bool { if let Some(subscriber) = self.subscriber.as_mut() { if self.subscription.take_request() { if let Some(buffer) = self.buffer_pool.next_buffer() { @@ -103,7 +101,7 @@ mod test { subscriber.map_inner(|s| s.request(1)); - assert!(buffer_pool_publisher.send_next_frame(Instant::now())); + assert!(buffer_pool_publisher.send_next_frame(1)); let events = subscriber.map_inner_mut(|s| s.take_events()); assert!(matches!(events.last().unwrap(), TestingSubscriberEvent::Next(_))); diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 918680d6a7..32b2b68b4d 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -5,16 +5,21 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { name: "ftl_test", test_suites: ["device-tests"], + header_libs: [ + "libbase_headers", + ], srcs: [ "algorithm_test.cpp", "cast_test.cpp", "concat_test.cpp", "enum_test.cpp", + "expected_test.cpp", "fake_guard_test.cpp", "flags_test.cpp", "function_test.cpp", diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp new file mode 100644 index 0000000000..8cb07e4696 --- /dev/null +++ b/libs/ftl/expected_test.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2024 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 <ftl/expected.h> +#include <gtest/gtest.h> + +#include <string> +#include <system_error> + +namespace android::test { + +using IntExp = ftl::Expected<int, std::errc>; +using StringExp = ftl::Expected<std::string, std::errc>; + +using namespace std::string_literals; + +TEST(Expected, Construct) { + // Default value. + EXPECT_TRUE(IntExp().has_value()); + EXPECT_EQ(IntExp(), IntExp(0)); + + EXPECT_TRUE(StringExp().has_value()); + EXPECT_EQ(StringExp(), StringExp("")); + + // Value. + ASSERT_TRUE(IntExp(42).has_value()); + EXPECT_EQ(42, IntExp(42).value()); + + ASSERT_TRUE(StringExp("test").has_value()); + EXPECT_EQ("test"s, StringExp("test").value()); + + // Error. + const auto exp = StringExp(ftl::Unexpected(std::errc::invalid_argument)); + ASSERT_FALSE(exp.has_value()); + EXPECT_EQ(std::errc::invalid_argument, exp.error()); +} + +TEST(Expected, HasError) { + EXPECT_FALSE(IntExp(123).has_error([](auto) { return true; })); + EXPECT_FALSE(IntExp(ftl::Unexpected(std::errc::io_error)).has_error([](auto) { return false; })); + + EXPECT_TRUE(StringExp(ftl::Unexpected(std::errc::permission_denied)).has_error([](auto e) { + return e == std::errc::permission_denied; + })); +} + +TEST(Expected, ValueOpt) { + EXPECT_EQ(ftl::Optional(-1), IntExp(-1).value_opt()); + EXPECT_EQ(std::nullopt, IntExp(ftl::Unexpected(std::errc::broken_pipe)).value_opt()); + + { + const StringExp exp("foo"s); + EXPECT_EQ(ftl::Optional('f'), + exp.value_opt().transform([](const auto& s) { return s.front(); })); + EXPECT_EQ("foo"s, exp.value()); + } + { + StringExp exp("foobar"s); + EXPECT_EQ(ftl::Optional(6), std::move(exp).value_opt().transform(&std::string::length)); + EXPECT_TRUE(exp.value().empty()); + } +} + +} // namespace android::test diff --git a/libs/ftl/future_test.cpp b/libs/ftl/future_test.cpp index 5a245b681c..1140639c87 100644 --- a/libs/ftl/future_test.cpp +++ b/libs/ftl/future_test.cpp @@ -102,4 +102,42 @@ TEST(Future, Chain) { decrement_thread.join(); } +TEST(Future, WaitFor) { + using namespace std::chrono_literals; + { + auto future = ftl::yield(42); + // Check that we can wait_for multiple times without invalidating the future + EXPECT_EQ(future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(future.get(), 42); + } + + { + std::condition_variable cv; + std::mutex m; + bool ready = false; + + std::packaged_task<int32_t()> get_int([&] { + std::unique_lock lk(m); + cv.wait(lk, [&] { return ready; }); + return 24; + }); + + auto get_future = ftl::Future(get_int.get_future()); + std::thread get_thread(std::move(get_int)); + + EXPECT_EQ(get_future.wait_for(0s), std::future_status::timeout); + { + std::unique_lock lk(m); + ready = true; + } + cv.notify_one(); + + EXPECT_EQ(get_future.wait_for(1s), std::future_status::ready); + EXPECT_EQ(get_future.get(), 24); + + get_thread.join(); + } +} + } // namespace android::test diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp index 634877f672..e96d70d8ad 100644 --- a/libs/ftl/small_map_test.cpp +++ b/libs/ftl/small_map_test.cpp @@ -189,9 +189,20 @@ TEST(SmallMap, Get) { } } -TEST(SmallMap, TryEmplace) { - SmallMap<int, std::string, 3> map; - using Pair = decltype(map)::value_type; +template <typename Capacity> +struct SmallMapTest : testing::Test { + static constexpr std::size_t kCapacity = Capacity{}(); +}; + +template <std::size_t N> +using Capacity = std::integral_constant<std::size_t, N>; + +using Capacities = testing::Types<Capacity<3>, Capacity<0>>; +TYPED_TEST_SUITE(SmallMapTest, Capacities, ); + +TYPED_TEST(SmallMapTest, TryEmplace) { + SmallMap<int, std::string, TestFixture::kCapacity> map; + using Pair = typename decltype(map)::value_type; { const auto [it, ok] = map.try_emplace(123, "abc"); @@ -207,14 +218,22 @@ TEST(SmallMap, TryEmplace) { const auto [it, ok] = map.try_emplace(-1); ASSERT_TRUE(ok); EXPECT_EQ(*it, Pair(-1, std::string())); - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } } { // Insertion fails if mapping exists. const auto [it, ok] = map.try_emplace(42, "!!!"); EXPECT_FALSE(ok); EXPECT_EQ(*it, Pair(42, "???")); - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } } { // Insertion at capacity promotes the map. @@ -240,9 +259,9 @@ struct String { } // namespace -TEST(SmallMap, TryReplace) { - SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); - using Pair = decltype(map)::value_type; +TYPED_TEST(SmallMapTest, TryReplace) { + SmallMap<int, String, TestFixture::kCapacity> map = ftl::init::map(1, "a")(2, "B"); + using Pair = typename decltype(map)::value_type; { // Replacing fails unless mapping exists. @@ -260,7 +279,12 @@ TEST(SmallMap, TryReplace) { EXPECT_EQ(*it, Pair(2, "b")); } - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } + EXPECT_TRUE(map.try_emplace(3, "abc").second); EXPECT_TRUE(map.try_emplace(4, "d").second); EXPECT_TRUE(map.dynamic()); @@ -284,9 +308,9 @@ TEST(SmallMap, TryReplace) { EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); } -TEST(SmallMap, EmplaceOrReplace) { - SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); - using Pair = decltype(map)::value_type; +TYPED_TEST(SmallMapTest, EmplaceOrReplace) { + SmallMap<int, String, TestFixture::kCapacity> map = ftl::init::map(1, "a")(2, "B"); + using Pair = typename decltype(map)::value_type; { // New mapping is emplaced. @@ -305,7 +329,12 @@ TEST(SmallMap, EmplaceOrReplace) { EXPECT_EQ(*it, Pair(2, "b")); } - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } + EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace. EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace. EXPECT_TRUE(map.dynamic()); diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index aab1276b47..f300da57d8 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library { diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp index 3c3b6af670..833718213a 100644 --- a/libs/gralloc/types/fuzzer/Android.bp +++ b/libs/gralloc/types/fuzzer/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_fuzz { diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp index 66eb0aa2fe..b796c03d6a 100644 --- a/libs/gralloc/types/tests/Android.bp +++ b/libs/gralloc/types/tests/Android.bp @@ -21,6 +21,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { @@ -30,5 +31,8 @@ cc_test { "libhidlbase", ], srcs: ["Gralloc4_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index eb4d3df21d..70cb36bad3 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -18,11 +18,13 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } aconfig_declarations { name: "libgui_flags", package: "com.android.graphics.libgui.flags", + container: "system", srcs: ["libgui_flags.aconfig"], } diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 19693e37cf..fb69fda32d 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -887,6 +887,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, int callbackTicket = 0; uint64_t currentFrameNumber = 0; BufferItem item; + int connectedApi; + sp<Fence> lastQueuedFence; + { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); @@ -1056,6 +1059,13 @@ status_t BufferQueueProducer::queueBuffer(int slot, callbackTicket = mNextCallbackTicket++; VALIDATE_CONSISTENCY(); + + connectedApi = mCore->mConnectedApi; + lastQueuedFence = std::move(mLastQueueBufferFence); + + mLastQueueBufferFence = std::move(acquireFence); + mLastQueuedCrop = item.mCrop; + mLastQueuedTransform = item.mTransform; } // Autolock scope // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because @@ -1079,9 +1089,6 @@ status_t BufferQueueProducer::queueBuffer(int 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 std::unique_lock<std::mutex> lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { @@ -1094,13 +1101,6 @@ 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.notify_all(); } @@ -1653,9 +1653,10 @@ status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) { status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); - BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); + BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); + if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; @@ -1679,10 +1680,11 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect, uint32_t* outTransform) { ATRACE_CALL(); - BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); - if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { + BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); + if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT || + mSlots[mCore->mLastQueuedSlot].mBufferState.isDequeued()) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; return NO_ERROR; diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 79c5fd1006..54290cd629 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -143,9 +143,9 @@ Choreographer::~Choreographer() { void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, - nsecs_t delay) { + nsecs_t delay, CallbackType callbackType) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay}; + FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay, callbackType}; { std::lock_guard<std::mutex> _l{mLock}; mFrameCallbacks.push(callback); @@ -285,18 +285,8 @@ void Choreographer::handleRefreshRateUpdates() { } } -void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, - VsyncEventData vsyncEventData) { - std::vector<FrameCallback> callbacks{}; - { - std::lock_guard<std::mutex> _l{mLock}; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { - callbacks.push_back(mFrameCallbacks.top()); - mFrameCallbacks.pop(); - } - } - mLastVsyncEventData = vsyncEventData; +void Choreographer::dispatchCallbacks(const std::vector<FrameCallback>& callbacks, + VsyncEventData vsyncEventData, nsecs_t timestamp) { for (const auto& cb : callbacks) { if (cb.vsyncCallback != nullptr) { ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64, @@ -319,6 +309,34 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t } } +void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, + VsyncEventData vsyncEventData) { + std::vector<FrameCallback> animationCallbacks{}; + std::vector<FrameCallback> inputCallbacks{}; + { + std::lock_guard<std::mutex> _l{mLock}; + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) { + if (mFrameCallbacks.top().callbackType == CALLBACK_INPUT) { + inputCallbacks.push_back(mFrameCallbacks.top()); + } else { + animationCallbacks.push_back(mFrameCallbacks.top()); + } + mFrameCallbacks.pop(); + } + } + mLastVsyncEventData = vsyncEventData; + // Callbacks with type CALLBACK_INPUT should always run first + { + ATRACE_FORMAT("CALLBACK_INPUT"); + dispatchCallbacks(inputCallbacks, vsyncEventData, timestamp); + } + { + ATRACE_FORMAT("CALLBACK_ANIMATION"); + dispatchCallbacks(animationCallbacks, vsyncEventData, timestamp); + } +} + void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this, to_string(displayId).c_str(), toString(connected)); @@ -344,6 +362,13 @@ void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { handleRefreshRateUpdates(); } +void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) { + ALOGV("choreographer %p ~ received hdcp levels change event (displayId=%s, connectedLevel=%d, " + "maxLevel=%d), ignoring.", + this, to_string(displayId).c_str(), connectedLevel, maxLevel); +} + void Choreographer::handleMessage(const Message& message) { switch (message.what) { case MSG_SCHEDULE_CALLBACKS: diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 5dd058cf9f..f3de96d2cd 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -195,6 +195,11 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId, std::move(mFrameRateOverrides)); break; + case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: + dispatchHdcpLevelsChanged(ev.header.displayId, + ev.hdcpLevelsChange.connectedLevel, + ev.hdcpLevelsChange.maxLevel); + break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); break; diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp index 11524e2b51..01aa7ed43c 100644 --- a/libs/gui/FrameRateUtils.cpp +++ b/libs/gui/FrameRateUtils.cpp @@ -42,6 +42,7 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrame if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE && + compatibility != ANATIVEWINDOW_FRAME_RATE_GTE && (!privileged || (compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT && compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 38fab9cdaa..1e0aacddab 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -199,7 +199,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeParcelable, trustedPresentationListener); SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio); SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio); - SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)) + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)); return NO_ERROR; } @@ -484,6 +484,12 @@ void layer_state_t::sanitize(int32_t permissions) { flags &= ~eLayerIsDisplayDecoration; ALOGE("Stripped attempt to set LayerIsDisplayDecoration in sanitize"); } + if ((mask & eCanOccludePresentation) && + !(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + flags &= ~eCanOccludePresentation; + mask &= ~eCanOccludePresentation; + ALOGE("Stripped attempt to set eCanOccludePresentation in sanitize"); + } } if (what & layer_state_t::eInputInfoChanged) { @@ -605,6 +611,10 @@ void layer_state_t::merge(const layer_state_t& other) { desiredHdrSdrRatio = other.desiredHdrSdrRatio; currentHdrSdrRatio = other.currentHdrSdrRatio; } + if (other.what & eDesiredHdrHeadroomChanged) { + what |= eDesiredHdrHeadroomChanged; + desiredHdrSdrRatio = other.desiredHdrSdrRatio; + } if (other.what & eCachingHintChanged) { what |= eCachingHintChanged; cachingHint = other.cachingHint; @@ -768,6 +778,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eDataspaceChanged, other, dataspace); CHECK_DIFF2(diff, eExtendedRangeBrightnessChanged, other, currentHdrSdrRatio, desiredHdrSdrRatio); + CHECK_DIFF(diff, eDesiredHdrHeadroomChanged, other, desiredHdrSdrRatio); CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint); CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata); if (other.what & eSurfaceDamageRegionChanged && diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 07a0cfed63..086544e48a 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -342,12 +342,23 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, getFrameTimestamp(outRequestedPresentTime, events->requestedPresentTime); getFrameTimestamp(outLatchTime, events->latchTime); - getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime); + + nsecs_t firstRefreshStartTime = NATIVE_WINDOW_TIMESTAMP_INVALID; + getFrameTimestamp(&firstRefreshStartTime, events->firstRefreshStartTime); + if (outFirstRefreshStartTime) { + *outFirstRefreshStartTime = firstRefreshStartTime; + } + getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime); getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime); - getFrameTimestampFence(outAcquireTime, events->acquireFence, + nsecs_t acquireTime = NATIVE_WINDOW_TIMESTAMP_INVALID; + getFrameTimestampFence(&acquireTime, events->acquireFence, events->hasAcquireInfo()); + if (outAcquireTime != nullptr) { + *outAcquireTime = acquireTime; + } + getFrameTimestampFence(outGpuCompositionDoneTime, events->gpuCompositionDoneFence, events->hasGpuCompositionDoneInfo()); @@ -356,6 +367,16 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, getFrameTimestampFence(outReleaseTime, events->releaseFence, events->hasReleaseInfo()); + // Fix up the GPU completion fence at this layer -- eglGetFrameTimestampsANDROID() expects + // that EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID > EGL_RENDERING_COMPLETE_TIME_ANDROID. + // This is typically true, but SurfaceFlinger may opt to cache prior GPU composition results, + // which breaks that assumption, so zero out GPU composition time. + if (outGpuCompositionDoneTime != nullptr + && *outGpuCompositionDoneTime > 0 && (acquireTime > 0 || firstRefreshStartTime > 0) + && *outGpuCompositionDoneTime <= std::max(acquireTime, firstRefreshStartTime)) { + *outGpuCompositionDoneTime = 0; + } + return NO_ERROR; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 8b6f2023dc..4f1356bebb 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -56,6 +56,7 @@ #include <android-base/thread_annotations.h> #include <gui/LayerStatePermissions.h> +#include <gui/ScreenCaptureResults.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> @@ -1807,6 +1808,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setExten return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredHdrHeadroom( + const sp<SurfaceControl>& sc, float desiredRatio) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eDesiredHdrHeadroomChanged; + s->desiredHdrSdrRatio = desiredRatio; + + registerSurfaceControlForCallback(sc); + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachingHint( const sp<SurfaceControl>& sc, gui::CachingHint cachingHint) { layer_state_t* s = getLayerState(sc); @@ -3117,7 +3132,6 @@ status_t SurfaceComposerClient::removeWindowInfosListener( ->removeWindowInfosListener(windowInfosListener, ComposerServiceAIDL::getComposerService()); } - // ---------------------------------------------------------------------------- status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, @@ -3139,11 +3153,19 @@ status_t ScreenshotClient::captureDisplay(DisplayId displayId, const gui::Captur } status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, - const sp<IScreenCaptureListener>& captureListener) { + const sp<IScreenCaptureListener>& captureListener, + bool sync) { sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService()); if (s == nullptr) return NO_INIT; - binder::Status status = s->captureLayers(captureArgs, captureListener); + binder::Status status; + if (sync) { + gui::ScreenCaptureResults captureResults; + status = s->captureLayersSync(captureArgs, &captureResults); + captureListener->onScreenCaptureCompleted(captureResults); + } else { + status = s->captureLayers(captureArgs, captureListener); + } return statusTFromBinderStatus(status); } diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index ba1d196e9c..86bf0ee745 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -109,7 +109,8 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.inputConfig == inputConfig && info.displayId == displayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType && - info.layoutParamsFlags == layoutParamsFlags; + info.layoutParamsFlags == layoutParamsFlags && + info.canOccludePresentation == canOccludePresentation; } status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { @@ -158,8 +159,9 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->write(touchableRegion) ?: parcel->writeBool(replaceTouchableRegionWithCrop) ?: parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: - parcel->writeStrongBinder(windowToken); - parcel->writeStrongBinder(focusTransferTarget); + parcel->writeStrongBinder(windowToken) ?: + parcel->writeStrongBinder(focusTransferTarget) ?: + parcel->writeBool(canOccludePresentation); // clang-format on return status; } @@ -210,7 +212,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readBool(&replaceTouchableRegionWithCrop) ?: parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: parcel->readNullableStrongBinder(&windowToken) ?: - parcel->readNullableStrongBinder(&focusTransferTarget); + parcel->readNullableStrongBinder(&focusTransferTarget) ?: + parcel->readBool(&canOccludePresentation); // clang-format on @@ -258,10 +261,7 @@ void WindowInfoHandle::updateFrom(sp<WindowInfoHandle> handle) { mInfo = handle->mInfo; } -std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) { - const WindowInfo& info = *window.getInfo(); - std::string transform; - info.transform.dump(transform, "transform", " "); +std::ostream& operator<<(std::ostream& out, const WindowInfo& info) { out << "name=" << info.name << ", id=" << info.id << ", displayId=" << info.displayId << ", inputConfig=" << info.inputConfig.string() << ", alpha=" << info.alpha << ", frame=[" << info.frame.left << "," << info.frame.top << "][" << info.frame.right << "," @@ -272,8 +272,17 @@ std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) { << ", ownerUid=" << info.ownerUid.toString() << ", dispatchingTimeout=" << std::chrono::duration_cast<std::chrono::milliseconds>(info.dispatchingTimeout).count() << "ms, token=" << info.token.get() - << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode) << "\n" - << transform; + << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode); + if (info.canOccludePresentation) out << ", canOccludePresentation"; + std::string transform; + info.transform.dump(transform, "transform", " "); + out << "\n" << transform; + return out; +} + +std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) { + const WindowInfo& info = *window.getInfo(); + out << info; return out; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index e3122bc300..51e01930d3 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -46,6 +46,7 @@ import android.gui.LayerCaptureArgs; import android.gui.LayerDebugInfo; import android.gui.OverlayProperties; import android.gui.PullAtomData; +import android.gui.ScreenCaptureResults; import android.gui.ARect; import android.gui.SchedulingPolicy; import android.gui.StalledTransactionInfo; @@ -245,6 +246,16 @@ interface ISurfaceComposer { /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. * This requires READ_FRAME_BUFFER permission. This function will fail if there + * is a secure window on screen. This is a blocking call and will return the + * ScreenCaptureResults, including the captured buffer. Because this is blocking, the + * caller doesn't care about the fence and the binder thread in SurfaceFlinger will wait + * on the fence to fire before returning the results. + */ + ScreenCaptureResults captureLayersSync(in LayerCaptureArgs args); + + /** + * Capture a subtree of the layer hierarchy, potentially ignoring the root node. + * This requires READ_FRAME_BUFFER permission. This function will fail if there * is a secure window on screen */ oneway void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener); diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp deleted file mode 100644 index cd738acde2..0000000000 --- a/libs/gui/fuzzer/Android.bp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -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"], -} - -cc_defaults { - name: "libgui_fuzzer_defaults", - defaults: ["android.hardware.power-ndk_shared"], - static_libs: [ - "android.hidl.token@1.0-utils", - "libbinder_random_parcel", - "libgui_aidl_static", - "libgui_window_info_static", - "libpdx", - "libgmock", - "libgui_mocks", - "libgmock_ndk", - "libgmock_main", - "libgtest_ndk_c++", - "libgmock_main_ndk", - "librenderengine_mocks", - "perfetto_trace_protos", - "libcompositionengine_mocks", - "perfetto_trace_protos", - ], - shared_libs: [ - "android.hardware.configstore@1.0", - "android.hardware.configstore-utils", - "android.hardware.graphics.bufferqueue@1.0", - "android.hardware.graphics.bufferqueue@2.0", - "android.hidl.token@1.0", - "libSurfaceFlingerProp", - "libgui", - "libbase", - "liblog", - "libEGL", - "libGLESv2", - "libbinder", - "libcutils", - "libhidlbase", - "libinput", - "libui", - "libutils", - "libnativewindow", - "libvndksupport", - ], - header_libs: [ - "libdvr_headers", - "libui_fuzzableDataspaces_headers", - ], - fuzz_config: { - cc: [ - "android-media-fuzzing-reports@google.com", - ], - componentid: 155276, - hotlists: [ - "4593311", - ], - description: "The fuzzer targets the APIs of libgui library", - vector: "local_no_privileges_required", - service_privilege: "privileged", - users: "multi_user", - fuzzed_code_usage: "shipped", - }, -} - -cc_fuzz { - name: "libgui_surfaceComposer_fuzzer", - srcs: [ - "libgui_surfaceComposer_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - "service_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_surfaceComposerClient_fuzzer", - srcs: [ - "libgui_surfaceComposerClient_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - "service_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_parcelable_fuzzer", - srcs: [ - "libgui_parcelable_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_bufferQueue_fuzzer", - srcs: [ - "libgui_bufferQueue_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_consumer_fuzzer", - srcs: [ - "libgui_consumer_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - ], -} - -cc_fuzz { - name: "libgui_displayEvent_fuzzer", - srcs: [ - "libgui_displayEvent_fuzzer.cpp", - ], - defaults: [ - "libgui_fuzzer_defaults", - ], -} diff --git a/libs/gui/fuzzer/README.md b/libs/gui/fuzzer/README.md deleted file mode 100644 index 96e27c989f..0000000000 --- a/libs/gui/fuzzer/README.md +++ /dev/null @@ -1,219 +0,0 @@ -# Fuzzers for Libgui - -## Table of contents -+ [libgui_surfaceComposer_fuzzer](#SurfaceComposer) -+ [libgui_surfaceComposerClient_fuzzer](#SurfaceComposerClient) -+ [libgui_parcelable_fuzzer](#Libgui_Parcelable) -+ [libgui_bufferQueue_fuzzer](#BufferQueue) -+ [libgui_consumer_fuzzer](#Libgui_Consumer) -+ [libgui_displayEvent_fuzzer](#LibGui_DisplayEvent) - -# <a name="libgui_surfaceComposer_fuzzer"></a> Fuzzer for SurfaceComposer - -SurfaceComposer supports the following parameters: -1. SurfaceWidth (parameter name:`width`) -2. SurfaceHeight (parameter name:`height`) -3. TransactionStateFlags (parameter name:`flags`) -4. TransformHint (parameter name:`outTransformHint`) -5. SurfacePixelFormat (parameter name:`format`) -6. LayerId (parameter name:`outLayerId`) -7. SurfaceComposerTags (parameter name:`surfaceTag`) -8. PowerBoostID (parameter name:`boostId`) -9. VsyncSource (parameter name:`vsyncSource`) -10. EventRegistrationFlags (parameter name:`eventRegistration`) -11. FrameRateCompatibility (parameter name:`frameRateCompatibility`) -12. ChangeFrameRateStrategy (parameter name:`changeFrameRateStrategy`) -13. HdrTypes (parameter name:`hdrTypes`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`surfaceTag` | 0.`BnSurfaceComposer::BOOT_FINISHED`, 1.`BnSurfaceComposer::CREATE_CONNECTION`, 2.`BnSurfaceComposer::GET_STATIC_DISPLAY_INFO`, 3.`BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION`, 4.`BnSurfaceComposer::CREATE_DISPLAY`, 5.`BnSurfaceComposer::DESTROY_DISPLAY`, 6.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN`, 7.`BnSurfaceComposer::SET_TRANSACTION_STATE`, 8.`BnSurfaceComposer::AUTHENTICATE_SURFACE`, 9.`BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS`, 10.`BnSurfaceComposer::GET_DISPLAY_STATE`, 11.`BnSurfaceComposer::CAPTURE_DISPLAY`, 12.`BnSurfaceComposer::CAPTURE_LAYERS`, 13.`BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS`, 14.`BnSurfaceComposer::GET_ANIMATION_FRAME_STATS`, 15.`BnSurfaceComposer::SET_POWER_MODE`, 16.`BnSurfaceComposer::GET_DISPLAY_STATS`, 17.`BnSurfaceComposer::SET_ACTIVE_COLOR_MODE`, 18.`BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS`, 19.`BnSurfaceComposer::INJECT_VSYNC`, 20.`BnSurfaceComposer::GET_LAYER_DEBUG_INFO`, 21.`BnSurfaceComposer::GET_COMPOSITION_PREFERENCE`, 22.`BnSurfaceComposer::GET_COLOR_MANAGEMENT`, 23.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES`, 24.`BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED`, 25.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE`, 26.`BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT`, 27.`BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY`, 28.`BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES`, 29.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS`, 30.`BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER`, 31.`BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER`, 32.`BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS`, 33.`BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS`, 34.`BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT`, 35.`BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS`, 36.`BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID`, 37.`BnSurfaceComposer::NOTIFY_POWER_BOOST`, 38.`BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS`, 39.`BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE`, 40.`BnSurfaceComposer::SET_GAME_CONTENT_TYPE`, 41.`BnSurfaceComposer::SET_FRAME_RATE`, 42.`BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN`, 43.`BnSurfaceComposer::SET_FRAME_TIMELINE_INFO`, 44.`BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER`, 45.`BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY`, 46.`BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT`, 47.`BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO`, 48.`BnSurfaceComposer::ADD_FPS_LISTENER`, 49.`BnSurfaceComposer::REMOVE_FPS_LISTENER`, 50.`BnSurfaceComposer::OVERRIDE_HDR_TYPES`, 51.`BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER`, 52.`BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER`, 53.`BnSurfaceComposer::ON_PULL_ATOM`, 54.`BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER`, 55.`BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER` | Value obtained from FuzzedDataProvider| -|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider| -|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider| -|`eventRegistration`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride` |Value obtained from FuzzedDataProvider| -|`frameRateCompatibility`| 0.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT`, 1.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE` |Value obtained from FuzzedDataProvider| -|`changeFrameRateStrategy`| 0.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS`, 1.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS` |Value obtained from FuzzedDataProvider| -|`hdrTypes`| 0.`ui::Hdr::DOLBY_VISION`, 1.`ui::Hdr::HDR10`, 2.`ui::Hdr::HLG`, 3.`ui::Hdr::HDR10_PLUS` |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_surfaceComposer_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_surfaceComposer_fuzzer/libgui_surfaceComposer_fuzzer -``` - -# <a name="libgui_surfaceComposerClient_fuzzer"></a> Fuzzer for SurfaceComposerClient - -SurfaceComposerClient supports the following data sources: -1. SurfaceWidth (parameter name:`width`) -2. SurfaceHeight (parameter name:`height`) -3. TransactionStateFlags (parameter name:`flags`) -4. TransformHint (parameter name:`outTransformHint`) -5. SurfacePixelFormat (parameter name:`format`) -6. LayerId (parameter name:`outLayerId`) -7. SurfaceComposerClientTags (parameter name:`surfaceTag`) -8. DefaultMode (parameter name:`defaultMode`) -9. PrimaryRefreshRateMin (parameter name:`primaryRefreshRateMin`) -10. PrimaryRefreshRateMax (parameter name:`primaryRefreshRateMax`) -11. AppRefreshRateMin (parameter name:`appRefreshRateMin`) -12. AppRefreshRateMax (parameter name:`appRefreshRateMax`) -13. DisplayPowerMode (parameter name:`mode`) -14. CacheId (parameter name:`cacheId`) -15. DisplayBrightness (parameter name:`brightness`) -16. PowerBoostID (parameter name:`boostId`) -17. AtomId (parameter name:`atomId`) -18. ComponentMask (parameter name:`componentMask`) -19. MaxFrames (parameter name:`maxFrames`) -20. TaskId (parameter name:`taskId`) -21. Alpha (parameter name:`aplha`) -22. CornerRadius (parameter name:`cornerRadius`) -23. BackgroundBlurRadius (parameter name:`backgroundBlurRadius`) -24. Half3Color (parameter name:`color`) -25. LayerStack (parameter name:`layerStack`) -26. Dataspace (parameter name:`dataspace`) -27. Api (parameter name:`api`) -28. Priority (parameter name:`priority`) -29. TouchableRegionPointX (parameter name:`pointX`) -30. TouchableRegionPointY (parameter name:`pointY`) -31. ColorMode (parameter name:`colorMode`) -32. WindowInfoFlags (parameter name:`flags`) -33. WindowInfoTransformOrientation (parameter name:`transform`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`surfaceTag`| 0.`Tag::CREATE_SURFACE`, 1.`Tag::CREATE_WITH_SURFACE_PARENT`, 2.`Tag::CLEAR_LAYER_FRAME_STATS`, 3.`Tag::GET_LAYER_FRAME_STATS`, 4.`Tag::MIRROR_SURFACE`, 5.`Tag::LAST` |Value obtained from FuzzedDataProvider| -|`mode`| 0.`gui::TouchOcclusionMode::BLOCK_UNTRUSTED`, 1.`gui::TouchOcclusionMode::USE_OPACITY`, 2.`gui::TouchOcclusionMode::ALLOW` |Value obtained from FuzzedDataProvider| -|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider| -|`colorMode`|0.`ui::ColorMode::NATIVE`, 1.`ui::ColorMode::STANDARD_BT601_625`, 2.`ui::ColorMode::STANDARD_BT601_625_UNADJUSTED`, 3.`ui::ColorMode::STANDARD_BT601_525`, 4.`ui::ColorMode::STANDARD_BT601_525_UNADJUSTED`, 5.`ui::ColorMode::STANDARD_BT709`, 6.`ui::ColorMode::DCI_P3`, 7.`ui::ColorMode::SRGB`, 8.`ui::ColorMode::ADOBE_RGB`, 9.`ui::ColorMode::DISPLAY_P3`, 10.`ui::ColorMode::BT2020`, 11.`ui::ColorMode::BT2100_PQ`, 12.`ui::ColorMode::BT2100_HLG`, 13.`ui::ColorMode::DISPLAY_BT2020` |Value obtained from FuzzedDataProvider| -|`flags`|0 .`gui::WindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON`, 1.`gui::WindowInfo::Flag::DIM_BEHIND`, 2.`gui::WindowInfo::Flag::BLUR_BEHIND`, 3.`gui::WindowInfo::Flag::NOT_FOCUSABLE`, 4.`gui::WindowInfo::Flag::NOT_TOUCHABLE`, 5.`gui::WindowInfo::Flag::NOT_TOUCH_MODAL`, 6.`gui::WindowInfo::Flag::TOUCHABLE_WHEN_WAKING`, 7.`gui::WindowInfo::Flag::KEEP_SCREEN_ON`, 8.`gui::WindowInfo::Flag::LAYOUT_IN_SCREEN`, 9.`gui::WindowInfo::Flag::LAYOUT_NO_LIMITS`, 10.`gui::WindowInfo::Flag::FULLSCREEN`, 11.`gui::WindowInfo::Flag::FORCE_NOT_FULLSCREEN`, 12.`gui::WindowInfo::Flag::DITHER`, 13.`gui::WindowInfo::Flag::SECURE`, 14.`gui::WindowInfo::Flag::SCALED`, 15.`gui::WindowInfo::Flag::IGNORE_CHEEK_PRESSES`, 16.`gui::WindowInfo::Flag::LAYOUT_INSET_DECOR`, 17.`gui::WindowInfo::Flag::ALT_FOCUSABLE_IM`, 18.`gui::WindowInfo::Flag::WATCH_OUTSIDE_TOUCH`, 19.`gui::WindowInfo::Flag::SHOW_WHEN_LOCKED`, 20.`gui::WindowInfo::Flag::SHOW_WALLPAPER`, 21.`gui::WindowInfo::Flag::TURN_SCREEN_ON`, 22.`gui::WindowInfo::Flag::DISMISS_KEYGUARD`, 23.`gui::WindowInfo::Flag::SPLIT_TOUCH`, 24.`gui::WindowInfo::Flag::HARDWARE_ACCELERATED`, 25.`gui::WindowInfo::Flag::LAYOUT_IN_OVERSCAN`, 26.`gui::WindowInfo::Flag::TRANSLUCENT_STATUS`, 27.`gui::WindowInfo::Flag::TRANSLUCENT_NAVIGATION`, 28.`gui::WindowInfo::Flag::LOCAL_FOCUS_MODE`, 29.`gui::WindowInfo::Flag::SLIPPERY`, 30.`gui::WindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR`, 31.`gui::WindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS`, |Value obtained from FuzzedDataProvider| -|`dataspace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| -|`transform`| 0.`ui::Transform::ROT_0`, 1.`ui::Transform::FLIP_H`, 2.`ui::Transform::FLIP_V`, 3.`ui::Transform::ROT_90`, 4.`ui::Transform::ROT_180`, 5.`ui::Transform::ROT_270` |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_surfaceComposerClient_fuzzer -``` -2. To run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_surfaceComposerClient_fuzzer/libgui_surfaceComposerClient_fuzzer -``` - -# <a name="libgui_parcelable_fuzzer"></a> Fuzzer for Libgui_Parcelable - -Libgui_Parcelable supports the following parameters: -1. LayerMetadataKey (parameter name:`key`) -2. Dataspace (parameter name:`mDataspace`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`key`| 0.`view::LayerMetadataKey::METADATA_OWNER_UID`, 1.`view::LayerMetadataKey::METADATA_WINDOW_TYPE`, 2.`view::LayerMetadataKey::METADATA_TASK_ID`, 3.`view::LayerMetadataKey::METADATA_MOUSE_CURSOR`, 4.`view::LayerMetadataKey::METADATA_ACCESSIBILITY_ID`, 5.`view::LayerMetadataKey::METADATA_OWNER_PID`, 6.`view::LayerMetadataKey::METADATA_DEQUEUE_TIME`, 7.`view::LayerMetadataKey::METADATA_GAME_MODE`, |Value obtained from FuzzedDataProvider| -|`mDataSpace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_fuzzer/libgui_fuzzer -``` - -# <a name="libgui_bufferQueue_fuzzer"></a> Fuzzer for BufferQueue - -BufferQueue supports the following parameters: -1. SurfaceWidth (parameter name:`width`) -2. SurfaceHeight (parameter name:`height`) -3. TransactionStateFlags (parameter name:`flags`) -4. TransformHint (parameter name:`outTransformHint`) -5. SurfacePixelFormat (parameter name:`format`) -6. LayerId (parameter name:`layerId`) -7. BufferId (parameter name:`bufferId`) -8. FrameNumber (parameter name:`frameNumber`) -9. FrameRate (parameter name:`frameRate`) -10. Compatability (parameter name:`compatability`) -11. LatchTime (parameter name:`latchTime`) -12. AcquireTime (parameter name:`acquireTime`) -13. RefreshTime (parameter name:`refreshTime`) -14. DequeueTime (parameter name:`dequeueTime`) -15. Slot (parameter name:`slot`) -16. MaxBuffers (parameter name:`maxBuffers`) -17. GenerationNumber (parameter name:`generationNumber`) -18. Api (parameter name:`api`) -19. Usage (parameter name:`usage`) -20. MaxFrameNumber (parameter name:`maxFrameNumber`) -21. BufferCount (parameter name:`bufferCount`) -22. MaxAcquredBufferCount (parameter name:`maxAcquredBufferCount`) -23. Status (parameter name:`status`) -24. ApiConnection (parameter name:`apiConnection`) -25. Dataspace (parameter name:`dataspace`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`status`| 0.`OK`, 1.`NO_MEMORY`, 2.`NO_INIT`, 3.`BAD_VALUE`, 4.`DEAD_OBJECT`, 5.`INVALID_OPERATION`, 6.`TIMED_OUT`, 7.`WOULD_BLOCK`, 8.`UNKNOWN_ERROR`, 9.`ALREADY_EXISTS`, |Value obtained from FuzzedDataProvider| -|`apiConnection`| 0.`BufferQueueCore::CURRENTLY_CONNECTED_API`, 1.`BufferQueueCore::NO_CONNECTED_API`, 2.`NATIVE_WINDOW_API_EGL`, 3.`NATIVE_WINDOW_API_CPU`, 4.`NATIVE_WINDOW_API_MEDIA`, 5.`NATIVE_WINDOW_API_CAMERA`, |Value obtained from FuzzedDataProvider| -|`dataspace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_bufferQueue_fuzzer -``` -2. To run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_bufferQueue_fuzzer/libgui_bufferQueue_fuzzer -``` - -# <a name="libgui_consumer_fuzzer"></a> Fuzzer for Libgui_Consumer - -Libgui_Consumer supports the following parameters: -1. GraphicWidth (parameter name:`graphicWidth`) -2. GraphicHeight (parameter name:`graphicHeight`) -4. TransformHint (parameter name:`outTransformHint`) -5. GraphicPixelFormat (parameter name:`format`) -6. Usage (parameter name:`usage`) - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_consumer_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_consumer_fuzzer/libgui_consumer_fuzzer -``` - -# <a name="libgui_displayEvent_fuzzer"></a> Fuzzer for LibGui_DisplayEvent - -LibGui_DisplayEvent supports the following parameters: -1. DisplayEventType (parameter name:`type`) -2. Events (parameter name:`events`) -3. VsyncSource (parameter name:`vsyncSource`) -4. EventRegistrationFlags (parameter name:`flags`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider| -|`flags`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride`, |Value obtained from FuzzedDataProvider| -|`type`| 0.`DisplayEventReceiver::DISPLAY_EVENT_NULL`, 1.`DisplayEventReceiver::DISPLAY_EVENT_VSYNC`, 2.`DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG`, 3.`DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE`, 4.`DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE`, 5.`DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH`, |Value obtained from FuzzedDataProvider| -|`events`| 0.`Looper::EVENT_INPUT`, 1.`Looper::EVENT_OUTPUT`, 2.`Looper::EVENT_ERROR`, 3.`Looper::EVENT_HANGUP`, 4.`Looper::EVENT_INVALID`, |Value obtained from FuzzedDataProvider| - -#### Steps to run -1. Build the fuzzer -``` - $ mm -j$(nproc) libgui_displayEvent_fuzzer -``` -2. Run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/libgui_displayEvent_fuzzer/libgui_displayEvent_fuzzer -``` diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp deleted file mode 100644 index 2e270b721f..0000000000 --- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include <android-base/stringprintf.h> -#include <gui/BufferQueueConsumer.h> -#include <gui/BufferQueueCore.h> -#include <gui/BufferQueueProducer.h> -#include <gui/bufferqueue/2.0/types.h> -#include <system/window.h> - -#include <libgui_fuzzer_utils.h> - -using namespace android; -using namespace hardware::graphics::bufferqueue; -using namespace V1_0::utils; -using namespace V2_0::utils; - -constexpr int32_t kMaxBytes = 256; - -constexpr int32_t kError[] = { - OK, NO_MEMORY, NO_INIT, BAD_VALUE, DEAD_OBJECT, INVALID_OPERATION, - TIMED_OUT, WOULD_BLOCK, UNKNOWN_ERROR, ALREADY_EXISTS, -}; - -constexpr int32_t kAPIConnection[] = { - BufferQueueCore::CURRENTLY_CONNECTED_API, - BufferQueueCore::NO_CONNECTED_API, - NATIVE_WINDOW_API_EGL, - NATIVE_WINDOW_API_CPU, - NATIVE_WINDOW_API_MEDIA, - NATIVE_WINDOW_API_CAMERA, -}; - -class BufferQueueFuzzer { -public: - BufferQueueFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - void invokeTypes(); - void invokeH2BGraphicBufferV1(); - void invokeH2BGraphicBufferV2(); - void invokeBufferQueueConsumer(); - void invokeBufferQueueProducer(); - void invokeBlastBufferQueue(); - void invokeQuery(sp<BufferQueueProducer>); - void invokeQuery(sp<V1_0::utils::H2BGraphicBufferProducer>); - void invokeQuery(sp<V2_0::utils::H2BGraphicBufferProducer>); - void invokeAcquireBuffer(sp<BufferQueueConsumer>); - void invokeOccupancyTracker(sp<BufferQueueConsumer>); - sp<SurfaceControl> makeSurfaceControl(); - sp<BLASTBufferQueue> makeBLASTBufferQueue(sp<SurfaceControl>); - - FuzzedDataProvider mFdp; -}; - -class ManageResourceHandle { -public: - ManageResourceHandle(FuzzedDataProvider* fdp) { - mNativeHandle = native_handle_create(0 /*numFds*/, 1 /*numInts*/); - mShouldOwn = fdp->ConsumeBool(); - mStream = NativeHandle::create(mNativeHandle, mShouldOwn); - } - ~ManageResourceHandle() { - if (!mShouldOwn) { - native_handle_close(mNativeHandle); - native_handle_delete(mNativeHandle); - } - } - sp<NativeHandle> getStream() { return mStream; } - -private: - bool mShouldOwn; - sp<NativeHandle> mStream; - native_handle_t* mNativeHandle; -}; - -sp<SurfaceControl> BufferQueueFuzzer::makeSurfaceControl() { - sp<IBinder> handle; - const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient()); - sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient); - sp<BnGraphicBufferProducer> producer; - uint32_t layerId = mFdp.ConsumeIntegral<uint32_t>(); - std::string layerName = base::StringPrintf("#%d", layerId); - return sp<SurfaceControl>::make(client, handle, layerId, layerName, - mFdp.ConsumeIntegral<int32_t>(), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>(), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); -} - -sp<BLASTBufferQueue> BufferQueueFuzzer::makeBLASTBufferQueue(sp<SurfaceControl> surface) { - return sp<BLASTBufferQueue>::make(mFdp.ConsumeRandomLengthString(kMaxBytes), surface, - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>()); -} - -void BufferQueueFuzzer::invokeBlastBufferQueue() { - sp<SurfaceControl> surface = makeSurfaceControl(); - sp<BLASTBufferQueue> queue = makeBLASTBufferQueue(surface); - - BufferItem item; - queue->onFrameAvailable(item); - queue->onFrameReplaced(item); - uint64_t bufferId = mFdp.ConsumeIntegral<uint64_t>(); - queue->onFrameDequeued(bufferId); - queue->onFrameCancelled(bufferId); - - SurfaceComposerClient::Transaction next; - uint64_t frameNumber = mFdp.ConsumeIntegral<uint64_t>(); - queue->mergeWithNextTransaction(&next, frameNumber); - queue->applyPendingTransactions(frameNumber); - - queue->update(surface, mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>()); - queue->setFrameRate(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeIntegral<int8_t>(), - mFdp.ConsumeBool() /*shouldBeSeamless*/); - FrameTimelineInfo info; - queue->setFrameTimelineInfo(mFdp.ConsumeIntegral<uint64_t>(), info); - - ManageResourceHandle handle(&mFdp); - queue->setSidebandStream(handle.getStream()); - - queue->getLastTransformHint(); - queue->getLastAcquiredFrameNum(); - - CompositorTiming compTiming; - sp<Fence> previousFence = new Fence(memfd_create("pfd", MFD_ALLOW_SEALING)); - sp<Fence> gpuFence = new Fence(memfd_create("gfd", MFD_ALLOW_SEALING)); - FrameEventHistoryStats frameStats(frameNumber, mFdp.ConsumeIntegral<uint64_t>(), gpuFence, - compTiming, mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>()); - std::vector<SurfaceControlStats> stats; - sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); - SurfaceControlStats controlStats(surface, mFdp.ConsumeIntegral<int64_t>(), - mFdp.ConsumeIntegral<int64_t>(), presentFence, previousFence, - mFdp.ConsumeIntegral<uint32_t>(), frameStats, - mFdp.ConsumeIntegral<uint32_t>()); - stats.push_back(controlStats); -} - -void BufferQueueFuzzer::invokeQuery(sp<BufferQueueProducer> producer) { - int32_t value; - producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); -} - -void BufferQueueFuzzer::invokeQuery(sp<V1_0::utils::H2BGraphicBufferProducer> producer) { - int32_t value; - producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); -} - -void BufferQueueFuzzer::invokeQuery(sp<V2_0::utils::H2BGraphicBufferProducer> producer) { - int32_t value; - producer->query(mFdp.ConsumeIntegral<int32_t>(), &value); -} - -void BufferQueueFuzzer::invokeBufferQueueProducer() { - sp<BufferQueueCore> core(new BufferQueueCore()); - sp<BufferQueueProducer> producer(new BufferQueueProducer(core)); - const sp<android::IProducerListener> listener; - android::IGraphicBufferProducer::QueueBufferOutput output; - uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); - producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); - - sp<GraphicBuffer> buffer; - int32_t slot = mFdp.ConsumeIntegral<int32_t>(); - uint32_t maxBuffers = mFdp.ConsumeIntegral<uint32_t>(); - producer->requestBuffer(slot, &buffer); - producer->setMaxDequeuedBufferCount(maxBuffers); - producer->setAsyncMode(mFdp.ConsumeBool() /*async*/); - - android::IGraphicBufferProducer::QueueBufferInput input; - producer->attachBuffer(&slot, buffer); - producer->queueBuffer(slot, input, &output); - - int32_t format = mFdp.ConsumeIntegral<int32_t>(); - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); - uint64_t outBufferAge; - FrameEventHistoryDelta outTimestamps; - sp<android::Fence> fence; - producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, - &outTimestamps); - producer->detachBuffer(slot); - producer->detachNextBuffer(&buffer, &fence); - producer->cancelBuffer(slot, fence); - - invokeQuery(producer); - - ManageResourceHandle handle(&mFdp); - producer->setSidebandStream(handle.getStream()); - - producer->allocateBuffers(width, height, format, usage); - producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); - producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); - producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); - producer->setLegacyBufferDrop(mFdp.ConsumeBool() /*drop*/); - producer->setAutoPrerotation(mFdp.ConsumeBool() /*autoPrerotation*/); - - producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); - producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); - producer->disconnect(api); -} - -void BufferQueueFuzzer::invokeAcquireBuffer(sp<BufferQueueConsumer> consumer) { - BufferItem item; - consumer->acquireBuffer(&item, mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint64_t>()); -} - -void BufferQueueFuzzer::invokeOccupancyTracker(sp<BufferQueueConsumer> consumer) { - String8 outResult; - String8 prefix((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); - consumer->dumpState(prefix, &outResult); - - std::vector<OccupancyTracker::Segment> outHistory; - consumer->getOccupancyHistory(mFdp.ConsumeBool() /*forceFlush*/, &outHistory); -} - -void BufferQueueFuzzer::invokeBufferQueueConsumer() { - sp<BufferQueueCore> core(new BufferQueueCore()); - sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core)); - sp<android::IConsumerListener> listener; - consumer->consumerConnect(listener, mFdp.ConsumeBool() /*controlledByApp*/); - invokeAcquireBuffer(consumer); - - int32_t slot = mFdp.ConsumeIntegral<int32_t>(); - sp<GraphicBuffer> buffer = - new GraphicBuffer(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint64_t>()); - consumer->attachBuffer(&slot, buffer); - consumer->detachBuffer(slot); - - consumer->setDefaultBufferSize(mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); - consumer->setMaxBufferCount(mFdp.ConsumeIntegral<int32_t>()); - consumer->setMaxAcquiredBufferCount(mFdp.ConsumeIntegral<int32_t>()); - - String8 name((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); - consumer->setConsumerName(name); - consumer->setDefaultBufferFormat(mFdp.ConsumeIntegral<int32_t>()); - android_dataspace dataspace = - static_cast<android_dataspace>(mFdp.PickValueInArray(kDataspaces)); - consumer->setDefaultBufferDataSpace(dataspace); - - consumer->setTransformHint(mFdp.ConsumeIntegral<uint32_t>()); - consumer->setConsumerUsageBits(mFdp.ConsumeIntegral<uint64_t>()); - consumer->setConsumerIsProtected(mFdp.ConsumeBool() /*isProtected*/); - invokeOccupancyTracker(consumer); - - sp<Fence> releaseFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); - consumer->releaseBuffer(mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint64_t>(), - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); - consumer->consumerDisconnect(); -} - -void BufferQueueFuzzer::invokeTypes() { - HStatus hStatus; - int32_t status = mFdp.PickValueInArray(kError); - bool bufferNeedsReallocation = mFdp.ConsumeBool(); - bool releaseAllBuffers = mFdp.ConsumeBool(); - b2h(status, &hStatus, &bufferNeedsReallocation, &releaseAllBuffers); - h2b(hStatus, &status); - - HConnectionType type; - int32_t apiConnection = mFdp.PickValueInArray(kAPIConnection); - b2h(apiConnection, &type); - h2b(type, &apiConnection); -} - -void BufferQueueFuzzer::invokeH2BGraphicBufferV1() { - sp<V1_0::utils::H2BGraphicBufferProducer> producer( - new V1_0::utils::H2BGraphicBufferProducer(new FakeGraphicBufferProducerV1())); - const sp<android::IProducerListener> listener; - android::IGraphicBufferProducer::QueueBufferOutput output; - uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); - producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); - - sp<GraphicBuffer> buffer; - int32_t slot = mFdp.ConsumeIntegral<int32_t>(); - producer->requestBuffer(slot, &buffer); - producer->setMaxDequeuedBufferCount(mFdp.ConsumeIntegral<int32_t>()); - producer->setAsyncMode(mFdp.ConsumeBool()); - - android::IGraphicBufferProducer::QueueBufferInput input; - input.fence = new Fence(memfd_create("ffd", MFD_ALLOW_SEALING)); - producer->attachBuffer(&slot, buffer); - producer->queueBuffer(slot, input, &output); - - int32_t format = mFdp.ConsumeIntegral<int32_t>(); - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); - uint64_t outBufferAge; - FrameEventHistoryDelta outTimestamps; - sp<android::Fence> fence; - producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, - &outTimestamps); - producer->detachBuffer(slot); - producer->cancelBuffer(slot, fence); - - invokeQuery(producer); - - ManageResourceHandle handle(&mFdp); - producer->setSidebandStream(handle.getStream()); - - producer->allocateBuffers(width, height, format, usage); - producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); - producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); - producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); - - producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); - producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); - producer->disconnect(api); -} - -void BufferQueueFuzzer::invokeH2BGraphicBufferV2() { - sp<V2_0::utils::H2BGraphicBufferProducer> producer( - new V2_0::utils::H2BGraphicBufferProducer(new FakeGraphicBufferProducerV2())); - const sp<android::IProducerListener> listener; - android::IGraphicBufferProducer::QueueBufferOutput output; - uint32_t api = mFdp.ConsumeIntegral<uint32_t>(); - producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output); - - sp<GraphicBuffer> buffer; - int32_t slot = mFdp.ConsumeIntegral<int32_t>(); - producer->requestBuffer(slot, &buffer); - producer->setMaxDequeuedBufferCount(mFdp.ConsumeIntegral<uint32_t>()); - producer->setAsyncMode(mFdp.ConsumeBool()); - - android::IGraphicBufferProducer::QueueBufferInput input; - input.fence = new Fence(memfd_create("ffd", MFD_ALLOW_SEALING)); - producer->attachBuffer(&slot, buffer); - producer->queueBuffer(slot, input, &output); - - int32_t format = mFdp.ConsumeIntegral<int32_t>(); - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - uint64_t usage = mFdp.ConsumeIntegral<uint64_t>(); - uint64_t outBufferAge; - FrameEventHistoryDelta outTimestamps; - sp<android::Fence> fence; - producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge, - &outTimestamps); - producer->detachBuffer(slot); - producer->cancelBuffer(slot, fence); - - invokeQuery(producer); - - ManageResourceHandle handle(&mFdp); - producer->setSidebandStream(handle.getStream()); - - producer->allocateBuffers(width, height, format, usage); - producer->allowAllocation(mFdp.ConsumeBool() /*allow*/); - producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/); - producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/); - - producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>()); - producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>()); - producer->disconnect(api); -} - -void BufferQueueFuzzer::process() { - invokeBlastBufferQueue(); - invokeH2BGraphicBufferV1(); - invokeH2BGraphicBufferV2(); - invokeTypes(); - invokeBufferQueueConsumer(); - invokeBufferQueueProducer(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - BufferQueueFuzzer bufferQueueFuzzer(data, size); - bufferQueueFuzzer.process(); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp b/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp deleted file mode 100644 index 24a046d3a9..0000000000 --- a/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2022 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/BufferQueueConsumer.h> -#include <gui/BufferQueueCore.h> -#include <gui/BufferQueueProducer.h> -#include <gui/GLConsumer.h> -#include <libgui_fuzzer_utils.h> - -using namespace android; - -constexpr int32_t kMinBuffer = 0; -constexpr int32_t kMaxBuffer = 100000; - -class ConsumerFuzzer { -public: - ConsumerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - FuzzedDataProvider mFdp; -}; - -void ConsumerFuzzer::process() { - sp<BufferQueueCore> core(new BufferQueueCore()); - sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); - - uint64_t maxBuffers = mFdp.ConsumeIntegralInRange<uint64_t>(kMinBuffer, kMaxBuffer); - sp<CpuConsumer> cpu( - new CpuConsumer(consumer, maxBuffers, mFdp.ConsumeBool() /*controlledByApp*/)); - CpuConsumer::LockedBuffer lockBuffer; - cpu->lockNextBuffer(&lockBuffer); - cpu->unlockBuffer(lockBuffer); - cpu->abandon(); - - uint32_t tex = mFdp.ConsumeIntegral<uint32_t>(); - sp<GLConsumer> glComsumer(new GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, - mFdp.ConsumeBool() /*useFenceSync*/, - mFdp.ConsumeBool() /*isControlledByApp*/)); - sp<Fence> releaseFence = new Fence(memfd_create("rfd", MFD_ALLOW_SEALING)); - glComsumer->setReleaseFence(releaseFence); - glComsumer->updateTexImage(); - glComsumer->releaseTexImage(); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint64_t>()); - float mtx[16]; - glComsumer->getTransformMatrix(mtx); - glComsumer->computeTransformMatrix(mtx, buffer, getRect(&mFdp), - mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeBool() /*filtering*/); - glComsumer->scaleDownCrop(getRect(&mFdp), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); - - glComsumer->setDefaultBufferSize(mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()); - glComsumer->setFilteringEnabled(mFdp.ConsumeBool() /*enabled*/); - - glComsumer->setConsumerUsageBits(mFdp.ConsumeIntegral<uint64_t>()); - glComsumer->attachToContext(tex); - glComsumer->abandon(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - ConsumerFuzzer consumerFuzzer(data, size); - consumerFuzzer.process(); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp deleted file mode 100644 index 0d2a52b576..0000000000 --- a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2022 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/gui/ISurfaceComposer.h> - -#include <libgui_fuzzer_utils.h> - -using namespace android; - -constexpr gui::ISurfaceComposer::VsyncSource kVsyncSource[] = { - gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, - gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger, -}; - -constexpr gui::ISurfaceComposer::EventRegistration kEventRegistration[] = { - gui::ISurfaceComposer::EventRegistration::modeChanged, - gui::ISurfaceComposer::EventRegistration::frameRateOverride, -}; - -constexpr uint32_t kDisplayEvent[] = { - DisplayEventReceiver::DISPLAY_EVENT_NULL, - DisplayEventReceiver::DISPLAY_EVENT_VSYNC, - DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, - DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, - DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, - DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, -}; - -constexpr int32_t kEvents[] = { - Looper::EVENT_INPUT, Looper::EVENT_OUTPUT, Looper::EVENT_ERROR, - Looper::EVENT_HANGUP, Looper::EVENT_INVALID, -}; - -DisplayEventReceiver::Event buildDisplayEvent(FuzzedDataProvider* fdp, uint32_t type, - DisplayEventReceiver::Event event) { - switch (type) { - case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: { - event.vsync.count = fdp->ConsumeIntegral<uint32_t>(); - event.vsync.vsyncData.frameInterval = fdp->ConsumeIntegral<uint64_t>(); - event.vsync.vsyncData.preferredFrameTimelineIndex = fdp->ConsumeIntegral<uint32_t>(); - for (size_t idx = 0; idx < gui::VsyncEventData::kFrameTimelinesCapacity; ++idx) { - event.vsync.vsyncData.frameTimelines[idx].vsyncId = fdp->ConsumeIntegral<int64_t>(); - event.vsync.vsyncData.frameTimelines[idx].deadlineTimestamp = - fdp->ConsumeIntegral<uint64_t>(); - event.vsync.vsyncData.frameTimelines[idx].expectedPresentationTime = - fdp->ConsumeIntegral<uint64_t>(); - } - break; - - } - case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: { - event.hotplug = - DisplayEventReceiver::Event::Hotplug{fdp->ConsumeBool() /*connected*/, - fdp->ConsumeIntegral< - int32_t>() /*connectionError*/}; - break; - } - case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: { - event.modeChange = - DisplayEventReceiver::Event::ModeChange{fdp->ConsumeIntegral<int32_t>(), - fdp->ConsumeIntegral<int64_t>()}; - break; - } - case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE: - case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: { - event.frameRateOverride = - DisplayEventReceiver::Event::FrameRateOverride{fdp->ConsumeIntegral<uint32_t>(), - fdp->ConsumeFloatingPoint< - float>()}; - break; - } - } - return event; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - FuzzedDataProvider fdp(data, size); - sp<Looper> looper; - sp<FakeDisplayEventDispatcher> dispatcher( - new FakeDisplayEventDispatcher(looper, fdp.PickValueInArray(kVsyncSource), - fdp.PickValueInArray(kEventRegistration))); - - dispatcher->initialize(); - DisplayEventReceiver::Event event; - uint32_t type = fdp.PickValueInArray(kDisplayEvent); - PhysicalDisplayId displayId; - event.header = - DisplayEventReceiver::Event::Header{type, displayId, fdp.ConsumeIntegral<int64_t>()}; - event = buildDisplayEvent(&fdp, type, event); - - dispatcher->injectEvent(event); - dispatcher->handleEvent(0, fdp.PickValueInArray(kEvents), nullptr); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h deleted file mode 100644 index 9933680c4b..0000000000 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include <android/gui/BnRegionSamplingListener.h> -#include <android/gui/BnSurfaceComposer.h> -#include <android/gui/BnSurfaceComposerClient.h> -#include <android/gui/IDisplayEventConnection.h> -#include <android/gui/ISurfaceComposerClient.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <gmock/gmock.h> -#include <gui/BLASTBufferQueue.h> -#include <gui/DisplayEventDispatcher.h> -#include <gui/IGraphicBufferProducer.h> -#include <gui/LayerDebugInfo.h> -#include <gui/LayerState.h> -#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> -#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> -#include <ui/fuzzer/FuzzableDataspaces.h> - -namespace android { - -constexpr uint32_t kOrientation[] = { - ui::Transform::ROT_0, ui::Transform::FLIP_H, ui::Transform::FLIP_V, - ui::Transform::ROT_90, ui::Transform::ROT_180, ui::Transform::ROT_270, -}; - -Rect getRect(FuzzedDataProvider* fdp) { - const int32_t left = fdp->ConsumeIntegral<int32_t>(); - const int32_t top = fdp->ConsumeIntegral<int32_t>(); - const int32_t right = fdp->ConsumeIntegral<int32_t>(); - const int32_t bottom = fdp->ConsumeIntegral<int32_t>(); - return Rect(left, top, right, bottom); -} - -gui::DisplayBrightness getBrightness(FuzzedDataProvider* fdp) { - static constexpr float kMinBrightness = 0; - static constexpr float kMaxBrightness = 1; - gui::DisplayBrightness brightness; - brightness.sdrWhitePoint = - fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); - brightness.sdrWhitePointNits = - fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); - brightness.displayBrightness = - fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); - brightness.displayBrightnessNits = - fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness); - return brightness; -} - -class FakeBnSurfaceComposer : public gui::BnSurfaceComposer { -public: - MOCK_METHOD(binder::Status, bootFinished, (), (override)); - MOCK_METHOD(binder::Status, createDisplayEventConnection, - (gui::ISurfaceComposer::VsyncSource, gui::ISurfaceComposer::EventRegistration, - const sp<IBinder>& /*layerHandle*/, sp<gui::IDisplayEventConnection>*), - (override)); - MOCK_METHOD(binder::Status, createConnection, (sp<gui::ISurfaceComposerClient>*), (override)); - MOCK_METHOD(binder::Status, createDisplay, (const std::string&, bool, float, sp<IBinder>*), - (override)); - MOCK_METHOD(binder::Status, destroyDisplay, (const sp<IBinder>&), (override)); - MOCK_METHOD(binder::Status, getPhysicalDisplayIds, (std::vector<int64_t>*), (override)); - MOCK_METHOD(binder::Status, getPhysicalDisplayToken, (int64_t, sp<IBinder>*), (override)); - MOCK_METHOD(binder::Status, setPowerMode, (const sp<IBinder>&, int), (override)); - MOCK_METHOD(binder::Status, getSupportedFrameTimestamps, (std::vector<FrameEvent>*), - (override)); - MOCK_METHOD(binder::Status, getDisplayStats, (const sp<IBinder>&, gui::DisplayStatInfo*), - (override)); - MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*), - (override)); - MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*), - (override)); - MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*), - (override)); - MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken, - (const sp<IBinder>&, gui::DynamicDisplayInfo*), (override)); - MOCK_METHOD(binder::Status, getDisplayNativePrimaries, - (const sp<IBinder>&, gui::DisplayPrimaries*), (override)); - MOCK_METHOD(binder::Status, setActiveColorMode, (const sp<IBinder>&, int), (override)); - MOCK_METHOD(binder::Status, setBootDisplayMode, (const sp<IBinder>&, int), (override)); - MOCK_METHOD(binder::Status, clearBootDisplayMode, (const sp<IBinder>&), (override)); - MOCK_METHOD(binder::Status, getBootDisplayModeSupport, (bool*), (override)); - MOCK_METHOD(binder::Status, getHdrConversionCapabilities, - (std::vector<gui::HdrConversionCapability>*), (override)); - MOCK_METHOD(binder::Status, setHdrConversionStrategy, - (const gui::HdrConversionStrategy&, int32_t*), (override)); - MOCK_METHOD(binder::Status, getHdrOutputConversionSupport, (bool*), (override)); - MOCK_METHOD(binder::Status, setAutoLowLatencyMode, (const sp<IBinder>&, bool), (override)); - MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override)); - MOCK_METHOD(binder::Status, captureDisplay, - (const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); - MOCK_METHOD(binder::Status, captureDisplayById, - (int64_t, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&), (override)); - MOCK_METHOD(binder::Status, captureLayers, - (const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); - MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override)); - MOCK_METHOD(binder::Status, getAnimationFrameStats, (gui::FrameStats*), (override)); - MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&), - (override)); - MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override)); - MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override)); - MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*), - (override)); - MOCK_METHOD(binder::Status, getDisplayedContentSamplingAttributes, - (const sp<IBinder>&, gui::ContentSamplingAttributes*), (override)); - MOCK_METHOD(binder::Status, setDisplayContentSamplingEnabled, - (const sp<IBinder>&, bool, int8_t, int64_t), (override)); - MOCK_METHOD(binder::Status, getDisplayedContentSample, - (const sp<IBinder>&, int64_t, int64_t, gui::DisplayedFrameStats*), (override)); - MOCK_METHOD(binder::Status, getProtectedContentSupport, (bool*), (override)); - MOCK_METHOD(binder::Status, isWideColorDisplay, (const sp<IBinder>&, bool*), (override)); - MOCK_METHOD(binder::Status, addRegionSamplingListener, - (const gui::ARect&, const sp<IBinder>&, const sp<gui::IRegionSamplingListener>&), - (override)); - MOCK_METHOD(binder::Status, removeRegionSamplingListener, - (const sp<gui::IRegionSamplingListener>&), (override)); - MOCK_METHOD(binder::Status, addFpsListener, (int32_t, const sp<gui::IFpsListener>&), - (override)); - MOCK_METHOD(binder::Status, removeFpsListener, (const sp<gui::IFpsListener>&), (override)); - MOCK_METHOD(binder::Status, addTunnelModeEnabledListener, - (const sp<gui::ITunnelModeEnabledListener>&), (override)); - MOCK_METHOD(binder::Status, removeTunnelModeEnabledListener, - (const sp<gui::ITunnelModeEnabledListener>&), (override)); - MOCK_METHOD(binder::Status, setDesiredDisplayModeSpecs, - (const sp<IBinder>&, const gui::DisplayModeSpecs&), (override)); - MOCK_METHOD(binder::Status, getDesiredDisplayModeSpecs, - (const sp<IBinder>&, gui::DisplayModeSpecs*), (override)); - MOCK_METHOD(binder::Status, getDisplayBrightnessSupport, (const sp<IBinder>&, bool*), - (override)); - MOCK_METHOD(binder::Status, setDisplayBrightness, - (const sp<IBinder>&, const gui::DisplayBrightness&), (override)); - MOCK_METHOD(binder::Status, addHdrLayerInfoListener, - (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override)); - MOCK_METHOD(binder::Status, removeHdrLayerInfoListener, - (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override)); - MOCK_METHOD(binder::Status, notifyPowerBoost, (int), (override)); - MOCK_METHOD(binder::Status, setGlobalShadowSettings, - (const gui::Color&, const gui::Color&, float, float, float), (override)); - MOCK_METHOD(binder::Status, getDisplayDecorationSupport, - (const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override)); - MOCK_METHOD(binder::Status, setGameModeFrameRateOverride, (int32_t, float), (override)); - MOCK_METHOD(binder::Status, setGameDefaultFrameRateOverride, (int32_t, float), (override)); - MOCK_METHOD(binder::Status, enableRefreshRateOverlay, (bool), (override)); - MOCK_METHOD(binder::Status, setDebugFlash, (int), (override)); - MOCK_METHOD(binder::Status, scheduleComposite, (), (override)); - MOCK_METHOD(binder::Status, scheduleCommit, (), (override)); - MOCK_METHOD(binder::Status, forceClientComposition, (bool), (override)); - MOCK_METHOD(binder::Status, updateSmallAreaDetection, - (const std::vector<int32_t>&, const std::vector<float>&), (override)); - MOCK_METHOD(binder::Status, setSmallAreaDetectionThreshold, (int32_t, float), (override)); - MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override)); - MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override)); - MOCK_METHOD(binder::Status, addWindowInfosListener, - (const sp<gui::IWindowInfosListener>&, gui::WindowInfosListenerInfo*), (override)); - MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&), - (override)); - MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override)); - MOCK_METHOD(binder::Status, getStalledTransactionInfo, - (int32_t, std::optional<gui::StalledTransactionInfo>*), (override)); - MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override)); -}; - -class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient { -public: - MOCK_METHOD(binder::Status, createSurface, - (const std::string& name, int32_t flags, const sp<IBinder>& parent, - const gui::LayerMetadata& metadata, gui::CreateSurfaceResult* outResult), - (override)); - - MOCK_METHOD(binder::Status, clearLayerFrameStats, (const sp<IBinder>& handle), (override)); - - MOCK_METHOD(binder::Status, getLayerFrameStats, - (const sp<IBinder>& handle, gui::FrameStats* outStats), (override)); - - MOCK_METHOD(binder::Status, mirrorSurface, - (const sp<IBinder>& mirrorFromHandle, gui::CreateSurfaceResult* outResult), - (override)); - - MOCK_METHOD(binder::Status, mirrorDisplay, - (int64_t displayId, gui::CreateSurfaceResult* outResult), (override)); - - MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override)); -}; - -class FakeDisplayEventDispatcher : public DisplayEventDispatcher { -public: - FakeDisplayEventDispatcher(const sp<Looper>& looper, - gui::ISurfaceComposer::VsyncSource vsyncSource, - gui::ISurfaceComposer::EventRegistration eventRegistration) - : DisplayEventDispatcher(looper, vsyncSource, eventRegistration){}; - - MOCK_METHOD4(dispatchVsync, void(nsecs_t, PhysicalDisplayId, uint32_t, VsyncEventData)); - MOCK_METHOD3(dispatchHotplug, void(nsecs_t, PhysicalDisplayId, bool)); - MOCK_METHOD2(dispatchHotplugConnectionError, void(nsecs_t, int32_t)); - MOCK_METHOD4(dispatchModeChanged, void(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t)); - MOCK_METHOD2(dispatchNullEvent, void(nsecs_t, PhysicalDisplayId)); - MOCK_METHOD3(dispatchFrameRateOverrides, - void(nsecs_t, PhysicalDisplayId, std::vector<FrameRateOverride>)); -}; - -} // namespace android - -namespace android::hardware { - -namespace graphics::bufferqueue::V1_0::utils { - -class FakeGraphicBufferProducerV1 : public HGraphicBufferProducer { -public: - FakeGraphicBufferProducerV1() { - ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return 0; }); - ON_CALL(*this, setAsyncMode).WillByDefault([]() { return 0; }); - ON_CALL(*this, detachBuffer).WillByDefault([]() { return 0; }); - ON_CALL(*this, cancelBuffer).WillByDefault([]() { return 0; }); - ON_CALL(*this, disconnect).WillByDefault([]() { return 0; }); - ON_CALL(*this, setSidebandStream).WillByDefault([]() { return 0; }); - ON_CALL(*this, allowAllocation).WillByDefault([]() { return 0; }); - ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return 0; }); - ON_CALL(*this, setSharedBufferMode).WillByDefault([]() { return 0; }); - ON_CALL(*this, setAutoRefresh).WillByDefault([]() { return 0; }); - ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return 0; }); - ON_CALL(*this, setLegacyBufferDrop).WillByDefault([]() { return 0; }); - }; - MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb)); - MOCK_METHOD1(setMaxDequeuedBufferCount, Return<int32_t>(int32_t)); - MOCK_METHOD1(setAsyncMode, Return<int32_t>(bool)); - MOCK_METHOD6(dequeueBuffer, - Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t, - bool, dequeueBuffer_cb)); - MOCK_METHOD1(detachBuffer, Return<int32_t>(int)); - MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb)); - MOCK_METHOD2(attachBuffer, Return<void>(const media::V1_0::AnwBuffer&, attachBuffer_cb)); - MOCK_METHOD3( - queueBuffer, - Return<void>( - int, - const graphics::bufferqueue::V1_0::IGraphicBufferProducer::QueueBufferInput&, - queueBuffer_cb)); - MOCK_METHOD2(cancelBuffer, Return<int32_t>(int, const hidl_handle&)); - MOCK_METHOD2(query, Return<void>(int32_t, query_cb)); - MOCK_METHOD4(connect, - Return<void>(const sp<graphics::bufferqueue::V1_0::IProducerListener>&, int32_t, - bool, connect_cb)); - MOCK_METHOD2(disconnect, - Return<int32_t>( - int, graphics::bufferqueue::V1_0::IGraphicBufferProducer::DisconnectMode)); - MOCK_METHOD1(setSidebandStream, Return<int32_t>(const hidl_handle&)); - MOCK_METHOD4(allocateBuffers, - Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t)); - MOCK_METHOD1(allowAllocation, Return<int32_t>(bool)); - MOCK_METHOD1(setGenerationNumber, Return<int32_t>(uint32_t)); - MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb)); - MOCK_METHOD1(setSharedBufferMode, Return<int32_t>(bool)); - MOCK_METHOD1(setAutoRefresh, Return<int32_t>(bool)); - MOCK_METHOD1(setDequeueTimeout, Return<int32_t>(nsecs_t)); - MOCK_METHOD1(setLegacyBufferDrop, Return<int32_t>(bool)); - MOCK_METHOD1(getLastQueuedBuffer, Return<void>(getLastQueuedBuffer_cb)); - MOCK_METHOD1(getFrameTimestamps, Return<void>(getFrameTimestamps_cb)); - MOCK_METHOD1(getUniqueId, Return<void>(getUniqueId_cb)); -}; - -}; // namespace graphics::bufferqueue::V1_0::utils - -namespace graphics::bufferqueue::V2_0::utils { - -class FakeGraphicBufferProducerV2 : public HGraphicBufferProducer { -public: - FakeGraphicBufferProducerV2() { - ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, setAsyncMode).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, detachBuffer).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, cancelBuffer).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, disconnect).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, allocateBuffers).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, allowAllocation).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return Status::OK; }); - ON_CALL(*this, getUniqueId).WillByDefault([]() { return 0; }); - }; - MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb)); - MOCK_METHOD1(setMaxDequeuedBufferCount, Return<graphics::bufferqueue::V2_0::Status>(int)); - MOCK_METHOD1(setAsyncMode, Return<graphics::bufferqueue::V2_0::Status>(bool)); - MOCK_METHOD2( - dequeueBuffer, - Return<void>( - const graphics::bufferqueue::V2_0::IGraphicBufferProducer::DequeueBufferInput&, - dequeueBuffer_cb)); - MOCK_METHOD1(detachBuffer, Return<graphics::bufferqueue::V2_0::Status>(int)); - MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb)); - MOCK_METHOD3(attachBuffer, - Return<void>(const graphics::common::V1_2::HardwareBuffer&, uint32_t, - attachBuffer_cb)); - MOCK_METHOD3( - queueBuffer, - Return<void>( - int, - const graphics::bufferqueue::V2_0::IGraphicBufferProducer::QueueBufferInput&, - queueBuffer_cb)); - MOCK_METHOD2(cancelBuffer, - Return<graphics::bufferqueue::V2_0::Status>(int, const hidl_handle&)); - MOCK_METHOD2(query, Return<void>(int32_t, query_cb)); - MOCK_METHOD4(connect, - Return<void>(const sp<graphics::bufferqueue::V2_0::IProducerListener>&, - graphics::bufferqueue::V2_0::ConnectionType, bool, connect_cb)); - MOCK_METHOD1(disconnect, - Return<graphics::bufferqueue::V2_0::Status>( - graphics::bufferqueue::V2_0::ConnectionType)); - MOCK_METHOD4(allocateBuffers, - Return<graphics::bufferqueue::V2_0::Status>(uint32_t, uint32_t, uint32_t, - uint64_t)); - MOCK_METHOD1(allowAllocation, Return<graphics::bufferqueue::V2_0::Status>(bool)); - MOCK_METHOD1(setGenerationNumber, Return<graphics::bufferqueue::V2_0::Status>(uint32_t)); - MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb)); - MOCK_METHOD1(setDequeueTimeout, Return<graphics::bufferqueue::V2_0::Status>(int64_t)); - MOCK_METHOD0(getUniqueId, Return<uint64_t>()); -}; - -}; // namespace graphics::bufferqueue::V2_0::utils -}; // namespace android::hardware diff --git a/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp b/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp deleted file mode 100644 index 9f0f6cac19..0000000000 --- a/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2022 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/BufferQueueConsumer.h> -#include <gui/BufferQueueCore.h> -#include <gui/BufferQueueProducer.h> -#include <gui/LayerMetadata.h> -#include <gui/OccupancyTracker.h> -#include <gui/StreamSplitter.h> -#include <gui/Surface.h> -#include <gui/SurfaceControl.h> -#include <gui/view/Surface.h> -#include <libgui_fuzzer_utils.h> -#include "android/view/LayerMetadataKey.h" - -using namespace android; - -constexpr int32_t kMaxBytes = 256; -constexpr int32_t kMatrixSize = 4; -constexpr int32_t kLayerMetadataKeyCount = 8; - -constexpr uint32_t kMetadataKey[] = { - (uint32_t)view::LayerMetadataKey::METADATA_OWNER_UID, - (uint32_t)view::LayerMetadataKey::METADATA_WINDOW_TYPE, - (uint32_t)view::LayerMetadataKey::METADATA_TASK_ID, - (uint32_t)view::LayerMetadataKey::METADATA_MOUSE_CURSOR, - (uint32_t)view::LayerMetadataKey::METADATA_ACCESSIBILITY_ID, - (uint32_t)view::LayerMetadataKey::METADATA_OWNER_PID, - (uint32_t)view::LayerMetadataKey::METADATA_DEQUEUE_TIME, - (uint32_t)view::LayerMetadataKey::METADATA_GAME_MODE, -}; - -class ParcelableFuzzer { -public: - ParcelableFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - void invokeStreamSplitter(); - void invokeOccupancyTracker(); - void invokeLayerDebugInfo(); - void invokeLayerMetadata(); - void invokeViewSurface(); - - FuzzedDataProvider mFdp; -}; - -void ParcelableFuzzer::invokeViewSurface() { - view::Surface surface; - surface.name = String16((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); - Parcel parcel; - surface.writeToParcel(&parcel); - parcel.setDataPosition(0); - surface.readFromParcel(&parcel); - bool nameAlreadyWritten = mFdp.ConsumeBool(); - surface.writeToParcel(&parcel, nameAlreadyWritten); - parcel.setDataPosition(0); - surface.readFromParcel(&parcel, mFdp.ConsumeBool()); -} - -void ParcelableFuzzer::invokeLayerMetadata() { - std::unordered_map<uint32_t, std::vector<uint8_t>> map; - for (size_t idx = 0; idx < kLayerMetadataKeyCount; ++idx) { - std::vector<uint8_t> data; - for (size_t idx1 = 0; idx1 < mFdp.ConsumeIntegral<uint32_t>(); ++idx1) { - data.push_back(mFdp.ConsumeIntegral<uint8_t>()); - } - map[kMetadataKey[idx]] = data; - } - LayerMetadata metadata(map); - uint32_t key = mFdp.PickValueInArray(kMetadataKey); - metadata.setInt32(key, mFdp.ConsumeIntegral<int32_t>()); - metadata.itemToString(key, (mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str()); - - Parcel parcel; - metadata.writeToParcel(&parcel); - parcel.setDataPosition(0); - metadata.readFromParcel(&parcel); -} - -void ParcelableFuzzer::invokeLayerDebugInfo() { - gui::LayerDebugInfo info; - info.mName = mFdp.ConsumeRandomLengthString(kMaxBytes); - info.mParentName = mFdp.ConsumeRandomLengthString(kMaxBytes); - info.mType = mFdp.ConsumeRandomLengthString(kMaxBytes); - info.mLayerStack = mFdp.ConsumeIntegral<uint32_t>(); - info.mX = mFdp.ConsumeFloatingPoint<float>(); - info.mY = mFdp.ConsumeFloatingPoint<float>(); - info.mZ = mFdp.ConsumeIntegral<uint32_t>(); - info.mWidth = mFdp.ConsumeIntegral<int32_t>(); - info.mHeight = mFdp.ConsumeIntegral<int32_t>(); - info.mActiveBufferWidth = mFdp.ConsumeIntegral<int32_t>(); - info.mActiveBufferHeight = mFdp.ConsumeIntegral<int32_t>(); - info.mActiveBufferStride = mFdp.ConsumeIntegral<int32_t>(); - info.mActiveBufferFormat = mFdp.ConsumeIntegral<int32_t>(); - info.mNumQueuedFrames = mFdp.ConsumeIntegral<int32_t>(); - - info.mFlags = mFdp.ConsumeIntegral<uint32_t>(); - info.mPixelFormat = mFdp.ConsumeIntegral<int32_t>(); - info.mTransparentRegion = Region(getRect(&mFdp)); - info.mVisibleRegion = Region(getRect(&mFdp)); - info.mSurfaceDamageRegion = Region(getRect(&mFdp)); - info.mCrop = getRect(&mFdp); - info.mDataSpace = static_cast<android_dataspace>(mFdp.PickValueInArray(kDataspaces)); - info.mColor = half4(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()); - for (size_t idx = 0; idx < kMatrixSize; ++idx) { - info.mMatrix[idx / 2][idx % 2] = mFdp.ConsumeFloatingPoint<float>(); - } - info.mIsOpaque = mFdp.ConsumeBool(); - info.mContentDirty = mFdp.ConsumeBool(); - info.mStretchEffect.width = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.height = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.vectorX = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.vectorY = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.maxAmountX = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.maxAmountY = mFdp.ConsumeFloatingPoint<float>(); - info.mStretchEffect.mappedChildBounds = - FloatRect(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), - mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()); - - Parcel parcel; - info.writeToParcel(&parcel); - parcel.setDataPosition(0); - info.readFromParcel(&parcel); -} - -void ParcelableFuzzer::invokeOccupancyTracker() { - nsecs_t totalTime = mFdp.ConsumeIntegral<uint32_t>(); - size_t numFrames = mFdp.ConsumeIntegral<size_t>(); - float occupancyAverage = mFdp.ConsumeFloatingPoint<float>(); - OccupancyTracker::Segment segment(totalTime, numFrames, occupancyAverage, - mFdp.ConsumeBool() /*usedThirdBuffer*/); - Parcel parcel; - segment.writeToParcel(&parcel); - parcel.setDataPosition(0); - segment.readFromParcel(&parcel); -} - -void ParcelableFuzzer::invokeStreamSplitter() { - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer); - sp<StreamSplitter> splitter; - StreamSplitter::createSplitter(consumer, &splitter); - splitter->addOutput(producer); - std::string name = mFdp.ConsumeRandomLengthString(kMaxBytes); - splitter->setName(String8(name.c_str())); -} - -void ParcelableFuzzer::process() { - invokeStreamSplitter(); - invokeOccupancyTracker(); - invokeLayerDebugInfo(); - invokeLayerMetadata(); - invokeViewSurface(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - ParcelableFuzzer libGuiFuzzer(data, size); - libGuiFuzzer.process(); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp deleted file mode 100644 index 4daa3be36f..0000000000 --- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2022 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 <aidl/android/hardware/power/Boost.h> -#include <fuzzbinder/libbinder_driver.h> -#include <gui/Surface.h> -#include <gui/SurfaceComposerClient.h> -#include <libgui_fuzzer_utils.h> -#include "android-base/stringprintf.h" - -using namespace android; - -constexpr int32_t kRandomStringMaxBytes = 256; - -constexpr ui::ColorMode kColormodes[] = {ui::ColorMode::NATIVE, - ui::ColorMode::STANDARD_BT601_625, - ui::ColorMode::STANDARD_BT601_625_UNADJUSTED, - ui::ColorMode::STANDARD_BT601_525, - ui::ColorMode::STANDARD_BT601_525_UNADJUSTED, - ui::ColorMode::STANDARD_BT709, - ui::ColorMode::DCI_P3, - ui::ColorMode::SRGB, - ui::ColorMode::ADOBE_RGB, - ui::ColorMode::DISPLAY_P3, - ui::ColorMode::BT2020, - ui::ColorMode::BT2100_PQ, - ui::ColorMode::BT2100_HLG, - ui::ColorMode::DISPLAY_BT2020}; - -constexpr aidl::android::hardware::power::Boost kBoost[] = { - aidl::android::hardware::power::Boost::INTERACTION, - aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT, - aidl::android::hardware::power::Boost::ML_ACC, - aidl::android::hardware::power::Boost::AUDIO_LAUNCH, - aidl::android::hardware::power::Boost::CAMERA_LAUNCH, - aidl::android::hardware::power::Boost::CAMERA_SHOT, -}; - -constexpr gui::TouchOcclusionMode kMode[] = { - gui::TouchOcclusionMode::BLOCK_UNTRUSTED, - gui::TouchOcclusionMode::USE_OPACITY, - gui::TouchOcclusionMode::ALLOW, -}; - -constexpr gui::WindowInfo::Flag kFlags[] = { - gui::WindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON, - gui::WindowInfo::Flag::DIM_BEHIND, - gui::WindowInfo::Flag::BLUR_BEHIND, - gui::WindowInfo::Flag::NOT_FOCUSABLE, - gui::WindowInfo::Flag::NOT_TOUCHABLE, - gui::WindowInfo::Flag::NOT_TOUCH_MODAL, - gui::WindowInfo::Flag::TOUCHABLE_WHEN_WAKING, - gui::WindowInfo::Flag::KEEP_SCREEN_ON, - gui::WindowInfo::Flag::LAYOUT_IN_SCREEN, - gui::WindowInfo::Flag::LAYOUT_NO_LIMITS, - gui::WindowInfo::Flag::FULLSCREEN, - gui::WindowInfo::Flag::FORCE_NOT_FULLSCREEN, - gui::WindowInfo::Flag::DITHER, - gui::WindowInfo::Flag::SECURE, - gui::WindowInfo::Flag::SCALED, - gui::WindowInfo::Flag::IGNORE_CHEEK_PRESSES, - gui::WindowInfo::Flag::LAYOUT_INSET_DECOR, - gui::WindowInfo::Flag::ALT_FOCUSABLE_IM, - gui::WindowInfo::Flag::WATCH_OUTSIDE_TOUCH, - gui::WindowInfo::Flag::SHOW_WHEN_LOCKED, - gui::WindowInfo::Flag::SHOW_WALLPAPER, - gui::WindowInfo::Flag::TURN_SCREEN_ON, - gui::WindowInfo::Flag::DISMISS_KEYGUARD, - gui::WindowInfo::Flag::SPLIT_TOUCH, - gui::WindowInfo::Flag::HARDWARE_ACCELERATED, - gui::WindowInfo::Flag::LAYOUT_IN_OVERSCAN, - gui::WindowInfo::Flag::TRANSLUCENT_STATUS, - gui::WindowInfo::Flag::TRANSLUCENT_NAVIGATION, - gui::WindowInfo::Flag::LOCAL_FOCUS_MODE, - gui::WindowInfo::Flag::SLIPPERY, - gui::WindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR, - gui::WindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS, -}; - -constexpr gui::WindowInfo::Type kType[] = { - gui::WindowInfo::Type::UNKNOWN, - gui::WindowInfo::Type::FIRST_APPLICATION_WINDOW, - gui::WindowInfo::Type::BASE_APPLICATION, - gui::WindowInfo::Type::APPLICATION, - gui::WindowInfo::Type::APPLICATION_STARTING, - gui::WindowInfo::Type::LAST_APPLICATION_WINDOW, - gui::WindowInfo::Type::FIRST_SUB_WINDOW, - gui::WindowInfo::Type::APPLICATION_PANEL, - gui::WindowInfo::Type::APPLICATION_MEDIA, - gui::WindowInfo::Type::APPLICATION_SUB_PANEL, - gui::WindowInfo::Type::APPLICATION_ATTACHED_DIALOG, - gui::WindowInfo::Type::APPLICATION_MEDIA_OVERLAY, -}; - -constexpr gui::WindowInfo::InputConfig kFeatures[] = { - gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL, - gui::WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, - gui::WindowInfo::InputConfig::DROP_INPUT, - gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, - gui::WindowInfo::InputConfig::SPY, - gui::WindowInfo::InputConfig::INTERCEPTS_STYLUS, -}; - -class SurfaceComposerClientFuzzer { -public: - SurfaceComposerClientFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - void invokeSurfaceComposerClient(); - void invokeSurfaceComposerClientBinder(); - void invokeSurfaceComposerTransaction(); - void getWindowInfo(gui::WindowInfo*); - sp<SurfaceControl> makeSurfaceControl(); - BlurRegion getBlurRegion(); - void fuzzOnPullAtom(); - gui::DisplayModeSpecs getDisplayModeSpecs(); - - FuzzedDataProvider mFdp; -}; - -gui::DisplayModeSpecs SurfaceComposerClientFuzzer::getDisplayModeSpecs() { - const auto getRefreshRateRange = [&] { - gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range; - range.min = mFdp.ConsumeFloatingPoint<float>(); - range.max = mFdp.ConsumeFloatingPoint<float>(); - return range; - }; - - const auto getRefreshRateRanges = [&] { - gui::DisplayModeSpecs::RefreshRateRanges ranges; - ranges.physical = getRefreshRateRange(); - ranges.render = getRefreshRateRange(); - return ranges; - }; - - String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); - sp<IBinder> displayToken = - SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/); - gui::DisplayModeSpecs specs; - specs.defaultMode = mFdp.ConsumeIntegral<int32_t>(); - specs.allowGroupSwitching = mFdp.ConsumeBool(); - specs.primaryRanges = getRefreshRateRanges(); - specs.appRequestRanges = getRefreshRateRanges(); - return specs; -} - -BlurRegion SurfaceComposerClientFuzzer::getBlurRegion() { - int32_t left = mFdp.ConsumeIntegral<int32_t>(); - int32_t right = mFdp.ConsumeIntegral<int32_t>(); - int32_t top = mFdp.ConsumeIntegral<int32_t>(); - int32_t bottom = mFdp.ConsumeIntegral<int32_t>(); - uint32_t blurRadius = mFdp.ConsumeIntegral<uint32_t>(); - float alpha = mFdp.ConsumeFloatingPoint<float>(); - float cornerRadiusTL = mFdp.ConsumeFloatingPoint<float>(); - float cornerRadiusTR = mFdp.ConsumeFloatingPoint<float>(); - float cornerRadiusBL = mFdp.ConsumeFloatingPoint<float>(); - float cornerRadiusBR = mFdp.ConsumeFloatingPoint<float>(); - return BlurRegion{blurRadius, cornerRadiusTL, cornerRadiusTR, cornerRadiusBL, - cornerRadiusBR, alpha, left, top, - right, bottom}; -} - -void SurfaceComposerClientFuzzer::getWindowInfo(gui::WindowInfo* windowInfo) { - windowInfo->id = mFdp.ConsumeIntegral<int32_t>(); - windowInfo->name = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes); - windowInfo->layoutParamsFlags = mFdp.PickValueInArray(kFlags); - windowInfo->layoutParamsType = mFdp.PickValueInArray(kType); - windowInfo->frame = Rect(mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<int32_t>(), - mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<int32_t>()); - windowInfo->surfaceInset = mFdp.ConsumeIntegral<int32_t>(); - windowInfo->alpha = mFdp.ConsumeFloatingPointInRange<float>(0, 1); - ui::Transform transform(mFdp.PickValueInArray(kOrientation)); - windowInfo->transform = transform; - windowInfo->touchableRegion = Region(getRect(&mFdp)); - windowInfo->replaceTouchableRegionWithCrop = mFdp.ConsumeBool(); - windowInfo->touchOcclusionMode = mFdp.PickValueInArray(kMode); - windowInfo->ownerPid = gui::Pid{mFdp.ConsumeIntegral<pid_t>()}; - windowInfo->ownerUid = gui::Uid{mFdp.ConsumeIntegral<uid_t>()}; - windowInfo->packageName = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes); - windowInfo->inputConfig = mFdp.PickValueInArray(kFeatures); -} - -sp<SurfaceControl> SurfaceComposerClientFuzzer::makeSurfaceControl() { - sp<IBinder> handle; - const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient()); - sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient); - sp<BnGraphicBufferProducer> producer; - uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t transformHint = mFdp.ConsumeIntegral<uint32_t>(); - uint32_t flags = mFdp.ConsumeIntegral<uint32_t>(); - int32_t format = mFdp.ConsumeIntegral<int32_t>(); - int32_t layerId = mFdp.ConsumeIntegral<int32_t>(); - std::string layerName = base::StringPrintf("#%d", layerId); - return new SurfaceControl(client, handle, layerId, layerName, width, height, format, - transformHint, flags); -} - -void SurfaceComposerClientFuzzer::invokeSurfaceComposerTransaction() { - sp<SurfaceControl> surface = makeSurfaceControl(); - - SurfaceComposerClient::Transaction transaction; - int32_t layer = mFdp.ConsumeIntegral<int32_t>(); - transaction.setLayer(surface, layer); - - sp<SurfaceControl> relativeSurface = makeSurfaceControl(); - transaction.setRelativeLayer(surface, relativeSurface, layer); - - Region transparentRegion(getRect(&mFdp)); - transaction.setTransparentRegionHint(surface, transparentRegion); - transaction.setAlpha(surface, mFdp.ConsumeFloatingPoint<float>()); - - transaction.setCornerRadius(surface, mFdp.ConsumeFloatingPoint<float>()); - transaction.setBackgroundBlurRadius(surface, mFdp.ConsumeFloatingPoint<float>()); - std::vector<BlurRegion> regions; - uint32_t vectorSize = mFdp.ConsumeIntegralInRange<uint32_t>(0, 100); - regions.resize(vectorSize); - for (size_t idx = 0; idx < vectorSize; ++idx) { - regions.push_back(getBlurRegion()); - } - transaction.setBlurRegions(surface, regions); - - transaction.setLayerStack(surface, {mFdp.ConsumeIntegral<uint32_t>()}); - half3 color = {mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(), - mFdp.ConsumeIntegral<uint32_t>()}; - transaction.setColor(surface, color); - transaction.setBackgroundColor(surface, color, mFdp.ConsumeFloatingPoint<float>(), - mFdp.PickValueInArray(kDataspaces)); - - transaction.setApi(surface, mFdp.ConsumeIntegral<int32_t>()); - transaction.setFrameRateSelectionPriority(surface, mFdp.ConsumeIntegral<int32_t>()); - transaction.setColorSpaceAgnostic(surface, mFdp.ConsumeBool() /*agnostic*/); - - gui::WindowInfo windowInfo; - getWindowInfo(&windowInfo); - transaction.setInputWindowInfo(surface, windowInfo); - Parcel windowParcel; - windowInfo.writeToParcel(&windowParcel); - windowParcel.setDataPosition(0); - windowInfo.readFromParcel(&windowParcel); - - windowInfo.addTouchableRegion(getRect(&mFdp)); - int32_t pointX = mFdp.ConsumeIntegral<int32_t>(); - int32_t pointY = mFdp.ConsumeIntegral<int32_t>(); - windowInfo.touchableRegionContainsPoint(pointX, pointY); - windowInfo.frameContainsPoint(pointX, pointY); - - Parcel transactionParcel; - transaction.writeToParcel(&transactionParcel); - transactionParcel.setDataPosition(0); - transaction.readFromParcel(&transactionParcel); - SurfaceComposerClient::Transaction::createFromParcel(&transactionParcel); -} - -void SurfaceComposerClientFuzzer::fuzzOnPullAtom() { - std::string outData; - bool success; - SurfaceComposerClient::onPullAtom(mFdp.ConsumeIntegral<int32_t>(), &outData, &success); -} - -void SurfaceComposerClientFuzzer::invokeSurfaceComposerClient() { - String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); - sp<IBinder> displayToken = - SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/); - SurfaceComposerClient::setDesiredDisplayModeSpecs(displayToken, getDisplayModeSpecs()); - - ui::ColorMode colorMode = mFdp.PickValueInArray(kColormodes); - SurfaceComposerClient::setActiveColorMode(displayToken, colorMode); - SurfaceComposerClient::setAutoLowLatencyMode(displayToken, mFdp.ConsumeBool() /*on*/); - SurfaceComposerClient::setGameContentType(displayToken, mFdp.ConsumeBool() /*on*/); - SurfaceComposerClient::setDisplayPowerMode(displayToken, mFdp.ConsumeIntegral<int32_t>()); - SurfaceComposerClient::doUncacheBufferTransaction(mFdp.ConsumeIntegral<uint64_t>()); - - SurfaceComposerClient::setDisplayBrightness(displayToken, getBrightness(&mFdp)); - aidl::android::hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost); - SurfaceComposerClient::notifyPowerBoost((int32_t)boostId); - - String8 surfaceName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str()); - sp<BBinder> handle(new BBinder()); - sp<BnGraphicBufferProducer> producer; - sp<Surface> surfaceParent( - new Surface(producer, mFdp.ConsumeBool() /*controlledByApp*/, handle)); - - fuzzOnPullAtom(); - SurfaceComposerClient::setDisplayContentSamplingEnabled(displayToken, - mFdp.ConsumeBool() /*enable*/, - mFdp.ConsumeIntegral<uint8_t>(), - mFdp.ConsumeIntegral<uint64_t>()); - - sp<IBinder> stopLayerHandle; - sp<gui::IRegionSamplingListener> listener = sp<gui::IRegionSamplingListenerDefault>::make(); - sp<gui::IRegionSamplingListenerDelegator> sampleListener = - new gui::IRegionSamplingListenerDelegator(listener); - SurfaceComposerClient::addRegionSamplingListener(getRect(&mFdp), stopLayerHandle, - sampleListener); - sp<gui::IFpsListenerDefault> fpsListener; - SurfaceComposerClient::addFpsListener(mFdp.ConsumeIntegral<int32_t>(), fpsListener); -} - -void SurfaceComposerClientFuzzer::invokeSurfaceComposerClientBinder() { - sp<FakeBnSurfaceComposerClient> client(new FakeBnSurfaceComposerClient()); - fuzzService(client.get(), std::move(mFdp)); -} - -void SurfaceComposerClientFuzzer::process() { - invokeSurfaceComposerClient(); - invokeSurfaceComposerTransaction(); - invokeSurfaceComposerClientBinder(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - SurfaceComposerClientFuzzer surfaceComposerClientFuzzer(data, size); - surfaceComposerClientFuzzer.process(); - return 0; -} diff --git a/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp deleted file mode 100644 index 6d5427bc9e..0000000000 --- a/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <fuzzbinder/libbinder_driver.h> -#include <fuzzer/FuzzedDataProvider.h> -#include <libgui_fuzzer_utils.h> - -using namespace android; - -class SurfaceComposerFuzzer { -public: - SurfaceComposerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; - void process(); - -private: - FuzzedDataProvider mFdp; -}; - -void SurfaceComposerFuzzer::process() { - sp<FakeBnSurfaceComposer> composer(new FakeBnSurfaceComposer()); - fuzzService(composer.get(), std::move(mFdp)); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - SurfaceComposerFuzzer surfaceComposerFuzzer(data, size); - surfaceComposerFuzzer.process(); - return 0; -} diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index 9fef512b64..fc79b03c23 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -28,12 +28,18 @@ namespace android { using gui::VsyncEventData; +enum CallbackType : int8_t { + CALLBACK_INPUT, + CALLBACK_ANIMATION, +}; + struct FrameCallback { AChoreographer_frameCallback callback; AChoreographer_frameCallback64 callback64; AChoreographer_vsyncCallback vsyncCallback; void* data; nsecs_t dueTime; + CallbackType callbackType; inline bool operator<(const FrameCallback& rhs) const { // Note that this is intentionally flipped because we want callbacks due sooner to be at @@ -78,7 +84,7 @@ public: void postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, - nsecs_t delay); + nsecs_t delay, CallbackType callbackType); void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) EXCLUDES(gChoreographers.lock); void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data); @@ -109,6 +115,8 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) override; + void dispatchCallbacks(const std::vector<FrameCallback>&, VsyncEventData vsyncEventData, + nsecs_t timestamp); void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) override; void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, @@ -116,6 +124,8 @@ private: void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) override; + void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) override; void scheduleCallbacks(); diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index fe2dd206ed..82cd50c7bd 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -65,6 +65,9 @@ private: virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) = 0; + virtual void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) = 0; + bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, VsyncEventData* outVsyncEventData); diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 79582ce685..8c1103bfc2 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -58,7 +58,6 @@ static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) { // ---------------------------------------------------------------------------- class DisplayEventReceiver { public: - enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), @@ -66,6 +65,7 @@ public: DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'), DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'), DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'), + DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'), }; struct Event { @@ -101,12 +101,22 @@ public: float frameRateHz __attribute__((aligned(8))); }; + /* + * The values are defined in aidl: + * hardware/interfaces/drm/aidl/android/hardware/drm/HdcpLevel.aidl + */ + struct HdcpLevelsChange { + int32_t connectedLevel; + int32_t maxLevel; + }; + Header header; union { VSync vsync; Hotplug hotplug; ModeChange modeChange; FrameRateOverride frameRateOverride; + HdcpLevelsChange hdcpLevelsChange; }; }; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index e1dc7911a1..0fedea7b9e 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -161,6 +161,9 @@ struct layer_state_t { // See SurfaceView scaling behavior for more details. eIgnoreDestinationFrame = 0x400, eLayerIsRefreshRateIndicator = 0x800, // REFRESH_RATE_INDICATOR + // Sets a property on this layer indicating that its visible region should be considered + // when computing TrustedPresentation Thresholds. + eCanOccludePresentation = 0x1000, }; enum { @@ -206,7 +209,7 @@ struct layer_state_t { eBackgroundBlurRadiusChanged = 0x80'00000000, eProducerDisconnect = 0x100'00000000, eFixedTransformHintChanged = 0x200'00000000, - /* unused 0x400'00000000, */ + eDesiredHdrHeadroomChanged = 0x400'00000000, eBlurRegionsChanged = 0x800'00000000, eAutoRefreshChanged = 0x1000'00000000, eStretchChanged = 0x2000'00000000, @@ -245,7 +248,8 @@ struct layer_state_t { layer_state_t::eSidebandStreamChanged | layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eTransparentRegionChanged | - layer_state_t::eExtendedRangeBrightnessChanged; + layer_state_t::eExtendedRangeBrightnessChanged | + layer_state_t::eDesiredHdrHeadroomChanged; // Content updates. static constexpr uint64_t CONTENT_CHANGES = layer_state_t::BUFFER_CHANGES | @@ -416,26 +420,36 @@ public: }; struct DisplayState { - enum { + enum : uint32_t { eSurfaceChanged = 0x01, eLayerStackChanged = 0x02, eDisplayProjectionChanged = 0x04, eDisplaySizeChanged = 0x08, - eFlagsChanged = 0x10 + eFlagsChanged = 0x10, + + eAllChanged = ~0u }; + // Not for direct use. Prefer constructor below for new displays. DisplayState(); + + DisplayState(sp<IBinder> token, ui::LayerStack layerStack) + : what(eAllChanged), + token(std::move(token)), + layerStack(layerStack), + layerStackSpaceRect(Rect::INVALID_RECT), + orientedDisplaySpaceRect(Rect::INVALID_RECT) {} + void merge(const DisplayState& other); void sanitize(int32_t permissions); uint32_t what = 0; uint32_t flags = 0; sp<IBinder> token; - sp<IGraphicBufferProducer> surface; ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK; - // These states define how layers are projected onto the physical display. + // These states define how layers are projected onto the physical or virtual display. // // Layers are first clipped to `layerStackSpaceRect'. They are then translated and // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated @@ -446,10 +460,17 @@ struct DisplayState { // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W, // 0). + // + // Rect::INVALID_RECT sizes the space to the active resolution of the physical display, or the + // default dimensions of the virtual display surface. + // ui::Rotation orientation = ui::ROTATION_0; Rect layerStackSpaceRect = Rect::EMPTY_RECT; Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT; + // Exclusive to virtual displays: The sink surface into which the virtual display is rendered, + // and an optional resolution that overrides its default dimensions. + sp<IGraphicBufferProducer> surface; uint32_t width = 0; uint32_t height = 0; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 14e3dd583e..288882695a 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -594,6 +594,7 @@ public: Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc, float currentBufferRatio, float desiredRatio); + Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio); Transaction& setCachingHint(const sp<SurfaceControl>& sc, gui::CachingHint cachingHint); Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc, @@ -849,7 +850,8 @@ public: static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); static status_t captureDisplay(DisplayId, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&); - static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); + static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&, + bool sync); [[deprecated]] static status_t captureDisplay(DisplayId id, const sp<IScreenCaptureListener>& listener) { diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 4d4c5e4394..32d60be612 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -176,6 +176,8 @@ struct WindowInfo : public Parcelable { static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS), CLONE = static_cast<uint32_t>(os::InputConfig::CLONE), + GLOBAL_STYLUS_BLOCKS_TOUCH = + static_cast<uint32_t>(os::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH), // clang-format on }; @@ -244,6 +246,10 @@ struct WindowInfo : public Parcelable { // any other window. sp<IBinder> focusTransferTarget; + // Sets a property on this window indicating that its visible region should be considered when + // computing TrustedPresentation Thresholds. + bool canOccludePresentation = false; + void setInputConfig(ftl::Flags<InputConfig> config, bool value); void addTouchableRegion(const Rect& region); @@ -267,6 +273,8 @@ struct WindowInfo : public Parcelable { status_t readFromParcel(const android::Parcel* parcel) override; }; +std::ostream& operator<<(std::ostream& out, const WindowInfo& window); + /* * Handle for a window that can receive input. * diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index b081030c9f..38646992da 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.graphics.libgui.flags" +container: "system" flag { name: "bq_setframerate" diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp index cc33e4c27d..386767ba9e 100644 --- a/libs/gui/sysprop/Android.bp +++ b/libs/gui/sysprop/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } sysprop_library { diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index e606b9941e..0f16f714dc 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -30,6 +30,7 @@ cc_test { "BLASTBufferQueue_test.cpp", "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", + "Choreographer_test.cpp", "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", @@ -61,6 +62,7 @@ cc_test { "libSurfaceFlingerProp", "libGLESv1_CM", "libinput", + "libnativedisplay", ], static_libs: [ diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp new file mode 100644 index 0000000000..2ac2550f07 --- /dev/null +++ b/libs/gui/tests/Choreographer_test.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 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 "Choreographer_test" + +#include <android-base/stringprintf.h> +#include <android/choreographer.h> +#include <gtest/gtest.h> +#include <gui/Choreographer.h> +#include <utils/Looper.h> +#include <chrono> +#include <future> +#include <string> + +namespace android { +class ChoreographerTest : public ::testing::Test {}; + +struct VsyncCallback { + std::atomic<bool> completePromise{false}; + std::chrono::nanoseconds frameTime{0LL}; + std::chrono::nanoseconds receivedCallbackTime{0LL}; + + void onVsyncCallback(const AChoreographerFrameCallbackData* callbackData) { + frameTime = std::chrono::nanoseconds{ + AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData)}; + receivedCallbackTime = std::chrono::nanoseconds{systemTime(SYSTEM_TIME_MONOTONIC)}; + completePromise.store(true); + } + + bool callbackReceived() { return completePromise.load(); } +}; + +static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) { + VsyncCallback* cb = static_cast<VsyncCallback*>(data); + cb->onVsyncCallback(callbackData); +} + +TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { + sp<Looper> looper = Looper::prepare(0); + Choreographer* choreographer = Choreographer::getForThread(); + VsyncCallback animationCb; + VsyncCallback inputCb; + + choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0, + CALLBACK_ANIMATION); + choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0, + CALLBACK_INPUT); + + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t currTime; + int pollResult; + do { + pollResult = looper->pollOnce(16); + currTime = systemTime(SYSTEM_TIME_MONOTONIC); + } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) && + (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) && + (currTime - startTime < 3000)); + + ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback"; + ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback"; + + ASSERT_EQ(inputCb.frameTime, animationCb.frameTime) + << android::base::StringPrintf("input and animation callback frame times don't match. " + "inputFrameTime=%lld animationFrameTime=%lld", + inputCb.frameTime.count(), + animationCb.frameTime.count()); + + ASSERT_LT(inputCb.receivedCallbackTime, animationCb.receivedCallbackTime) + << android::base::StringPrintf("input callback was not called first. " + "inputCallbackTime=%lld animationCallbackTime=%lld", + inputCb.frameTime.count(), + animationCb.frameTime.count()); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index d4b8dbeeb9..a9d6e8d3bf 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -63,8 +63,7 @@ namespace android::test { using Transaction = SurfaceComposerClient::Transaction; sp<IInputFlinger> getInputFlinger() { - sp<IBinder> input(defaultServiceManager()->getService( - String16("inputflinger"))); + sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); if (input == nullptr) { ALOGE("Failed to link to input service"); } else { ALOGE("Linked to input"); } @@ -104,8 +103,13 @@ public: if (noInputChannel) { mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); } else { - mClientChannel = std::make_shared<InputChannel>(); - mInputFlinger->createInputChannel("testchannels", mClientChannel.get()); + android::os::InputChannelCore tempChannel; + android::binder::Status result = + mInputFlinger->createInputChannel("testchannels", &tempChannel); + if (!result.isOk()) { + ADD_FAILURE() << "binder call to createInputChannel failed"; + } + mClientChannel = InputChannel::create(std::move(tempChannel)); mInputInfo.token = mClientChannel->getConnectionToken(); mInputConsumer = new InputConsumer(mClientChannel); } @@ -168,8 +172,8 @@ public: return std::make_unique<InputSurface>(surfaceControl, width, height); } - InputEvent *consumeEvent(int timeoutMs = 3000) { - waitForEventAvailable(timeoutMs); + InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) { + mClientChannel->waitForMessage(timeout); InputEvent *ev; uint32_t seqId; @@ -302,15 +306,6 @@ public: t.apply(true); } -private: - void waitForEventAvailable(int timeoutMs) { - struct pollfd fd; - - fd.fd = mClientChannel->getFd(); - fd.events = POLLIN; - poll(&fd, 1, timeoutMs); - } - public: sp<SurfaceControl> mSurfaceControl; std::shared_ptr<InputChannel> mClientChannel; @@ -615,7 +610,7 @@ TEST_F(InputSurfacesTest, touchable_region) { // A tap within the surface but outside the touchable region should not be sent to the surface. injectTap(20, 30); - EXPECT_EQ(surface->consumeEvent(200 /*timeoutMs*/), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr); injectTap(31, 52); surface->expectTap(20, 30); @@ -981,12 +976,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; obscuringSurface->showAt(100, 100); injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { @@ -1002,12 +997,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { @@ -1024,12 +1019,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) { injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { @@ -1046,12 +1041,12 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) { injectTap(111, 111); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) { @@ -1076,12 +1071,12 @@ TEST_F(InputSurfacesTest, drop_input_policy) { injectTap(101, 101); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(); surface->assertFocusChange(true); injectKey(AKEYCODE_V); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) { @@ -1116,7 +1111,7 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ // Does not receive events outside its crop injectTap(26, 26); - EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); + EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); } /** @@ -1141,7 +1136,7 @@ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_nul // Does not receive events outside parent bounds injectTap(31, 31); - EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); + EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); } /** @@ -1167,7 +1162,7 @@ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { // Does not receive events outside crop layer bounds injectTap(21, 21); injectTap(71, 71); - EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); + EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) { @@ -1184,7 +1179,7 @@ TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) { [&](auto &t, auto &sc) { t.reparent(sc, parent->mSurfaceControl); }); injectTap(101, 101); - EXPECT_EQ(parent->consumeEvent(100), nullptr); + EXPECT_EQ(parent->consumeEvent(/*timeout=*/100ms), nullptr); } class MultiDisplayTests : public InputSurfacesTest { @@ -1233,7 +1228,7 @@ TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) { // Touches should be dropped if the layer is on an invalid display. injectTapOnDisplay(101, 101, layerStack.id); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); // However, we still let the window be focused and receive keys. surface->requestFocus(layerStack.id); @@ -1271,12 +1266,12 @@ TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) { injectTapOnDisplay(101, 101, layerStack.id); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); surface->requestFocus(layerStack.id); surface->assertFocusChange(true); injectKeyOnDisplay(AKEYCODE_V, layerStack.id); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr); } TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) { diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp index 5fe22b05f9..04bfb28f65 100644 --- a/libs/gui/tests/FrameRateUtilsTest.cpp +++ b/libs/gui/tests/FrameRateUtilsTest.cpp @@ -34,6 +34,8 @@ TEST(FrameRateUtilsTest, ValidateFrameRate) { ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, "")); EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_GTE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); // Privileged APIs. EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index c6ea317949..577d2394c6 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -791,6 +791,10 @@ public: return binder::Status::ok(); } + binder::Status captureLayersSync(const LayerCaptureArgs&, ScreenCaptureResults*) override { + return binder::Status::ok(); + } + binder::Status captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override { return binder::Status::ok(); diff --git a/libs/input/AccelerationCurve.cpp b/libs/input/AccelerationCurve.cpp new file mode 100644 index 0000000000..0a92a71596 --- /dev/null +++ b/libs/input/AccelerationCurve.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2024 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 <input/AccelerationCurve.h> + +#include <array> +#include <limits> + +#include <log/log_main.h> + +#define LOG_TAG "AccelerationCurve" + +namespace android { + +namespace { + +// The last segment must have an infinite maximum speed, so that all speeds are covered. +constexpr std::array<AccelerationCurveSegment, 4> kSegments = {{ + {32.002, 3.19, 0}, + {52.83, 4.79, -51.254}, + {119.124, 7.28, -182.737}, + {std::numeric_limits<double>::infinity(), 15.04, -1107.556}, +}}; + +static_assert(kSegments.back().maxPointerSpeedMmPerS == std::numeric_limits<double>::infinity()); + +constexpr std::array<double, 15> kSensitivityFactors = {1, 2, 4, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 16, 18, 20}; + +} // namespace + +std::vector<AccelerationCurveSegment> createAccelerationCurveForPointerSensitivity( + int32_t sensitivity) { + LOG_ALWAYS_FATAL_IF(sensitivity < -7 || sensitivity > 7, "Invalid pointer sensitivity value"); + std::vector<AccelerationCurveSegment> output; + output.reserve(kSegments.size()); + + // The curves we want to produce for different sensitivity values are actually the same curve, + // just scaled in the Y (gain) axis by a sensitivity factor and a couple of constants. + double commonFactor = 0.64 * kSensitivityFactors[sensitivity + 7] / 10; + for (AccelerationCurveSegment seg : kSegments) { + output.push_back(AccelerationCurveSegment{seg.maxPointerSpeedMmPerS, + commonFactor * seg.baseGain, + commonFactor * seg.reciprocal}); + } + + return output; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 252040d1b0..6b8bc01c24 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -39,6 +39,7 @@ filegroup { aconfig_declarations { name: "com.android.input.flags-aconfig", package: "com.android.input.flags", + container: "system", srcs: ["input_flags.aconfig"], } @@ -134,6 +135,29 @@ rust_bindgen { ], } +cc_library_static { + name: "iinputflinger_aidl_lib_static", + host_supported: true, + srcs: [ + "android/os/IInputFlinger.aidl", + "android/os/InputChannelCore.aidl", + ], + shared_libs: [ + "libbinder", + ], + whole_static_libs: [ + "libgui_window_info_static", + ], + aidl: { + export_aidl_headers: true, + local_include_dirs: ["."], + include_dirs: [ + "frameworks/native/libs/gui", + "frameworks/native/libs/input", + ], + }, +} + // Contains methods to help access C++ code from rust cc_library_static { name: "libinput_from_rust_to_cpp", @@ -178,7 +202,7 @@ cc_library { "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", ], srcs: [ - "android/os/IInputFlinger.aidl", + "AccelerationCurve.cpp", "Input.cpp", "InputDevice.cpp", "InputEventLabels.cpp", @@ -240,7 +264,6 @@ cc_library { static_libs: [ "inputconstants-cpp", - "libgui_window_info_static", "libui-types", "libtflite_static", "libkernelconfigs", @@ -249,10 +272,10 @@ cc_library { whole_static_libs: [ "com.android.input.flags-aconfig-cc", "libinput_rust_ffi", + "iinputflinger_aidl_lib_static", ], export_static_lib_headers: [ - "libgui_window_info_static", "libui-types", ], @@ -286,14 +309,6 @@ cc_library { ], }, }, - - aidl: { - local_include_dirs: ["."], - export_aidl_headers: true, - include_dirs: [ - "frameworks/native/libs/gui", - ], - }, } // Use bootstrap version of stats logging library. diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index bd5b67b1d0..61a964ece9 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -60,6 +60,45 @@ bool shouldDisregardOffset(uint32_t source) { return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER); } +int32_t resolveActionForSplitMotionEvent( + int32_t action, int32_t flags, const std::vector<PointerProperties>& pointerProperties, + const std::vector<PointerProperties>& splitPointerProperties) { + LOG_ALWAYS_FATAL_IF(splitPointerProperties.empty()); + const auto maskedAction = MotionEvent::getActionMasked(action); + if (maskedAction != AMOTION_EVENT_ACTION_POINTER_DOWN && + maskedAction != AMOTION_EVENT_ACTION_POINTER_UP) { + // The action is unaffected by splitting this motion event. + return action; + } + const auto actionIndex = MotionEvent::getActionIndex(action); + if (CC_UNLIKELY(actionIndex >= pointerProperties.size())) { + LOG(FATAL) << "Action index is out of bounds, index: " << actionIndex; + } + + const auto affectedPointerId = pointerProperties[actionIndex].id; + std::optional<uint32_t> splitActionIndex; + for (uint32_t i = 0; i < splitPointerProperties.size(); i++) { + if (affectedPointerId == splitPointerProperties[i].id) { + splitActionIndex = i; + break; + } + } + if (!splitActionIndex.has_value()) { + // The affected pointer is not part of the split motion event. + return AMOTION_EVENT_ACTION_MOVE; + } + + if (splitPointerProperties.size() > 1) { + return maskedAction | (*splitActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + + if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + return ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) ? AMOTION_EVENT_ACTION_CANCEL + : AMOTION_EVENT_ACTION_UP; + } + return AMOTION_EVENT_ACTION_DOWN; +} + } // namespace const char* motionClassificationToString(MotionClassification classification) { @@ -374,11 +413,16 @@ std::ostream& operator<<(std::ostream& out, const KeyEvent& event) { out << ", deviceId=" << event.getDeviceId(); out << ", source=" << inputEventSourceToString(event.getSource()); out << ", displayId=" << event.getDisplayId(); - out << ", eventId=" << event.getId(); + out << ", eventId=0x" << std::hex << event.getId() << std::dec; out << "}"; return out; } +std::ostream& operator<<(std::ostream& out, const PointerProperties& properties) { + out << "Pointer(id=" << properties.id << ", " << ftl::enum_string(properties.toolType) << ")"; + return out; +} + // --- PointerCoords --- float PointerCoords::getAxisValue(int32_t axis) const { @@ -579,6 +623,28 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { } } +void MotionEvent::splitFrom(const android::MotionEvent& other, + std::bitset<MAX_POINTER_ID + 1> splitPointerIds, int32_t newEventId) { + // TODO(b/327503168): The down time should be a parameter to the split function, because only + // the caller can know when the first event went down on the target. + const nsecs_t splitDownTime = other.mDownTime; + + auto [action, pointerProperties, pointerCoords] = + split(other.getAction(), other.getFlags(), other.getHistorySize(), + other.mPointerProperties, other.mSamplePointerCoords, splitPointerIds); + + // Initialize the event with zero pointers, and manually set the split pointers. + initialize(newEventId, other.mDeviceId, other.mSource, other.mDisplayId, /*hmac=*/{}, action, + other.mActionButton, other.mFlags, other.mEdgeFlags, other.mMetaState, + other.mButtonState, other.mClassification, other.mTransform, other.mXPrecision, + other.mYPrecision, other.mRawXCursorPosition, other.mRawYCursorPosition, + other.mRawTransform, splitDownTime, other.getEventTime(), /*pointerCount=*/0, + pointerProperties.data(), pointerCoords.data()); + mPointerProperties = std::move(pointerProperties); + mSamplePointerCoords = std::move(pointerCoords); + mSampleEventTimes = other.mSampleEventTimes; +} + void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { @@ -685,6 +751,18 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { mTransform.set(currXOffset + xOffset, currYOffset + yOffset); } +float MotionEvent::getRawXOffset() const { + // This is equivalent to the x-coordinate of the point that the origin of the raw coordinate + // space maps to. + return (mTransform * mRawTransform.inverse()).tx(); +} + +float MotionEvent::getRawYOffset() const { + // This is equivalent to the y-coordinate of the point that the origin of the raw coordinate + // space maps to. + return (mTransform * mRawTransform.inverse()).ty(); +} + void MotionEvent::scale(float globalScaleFactor) { mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor); mRawTransform.set(mRawTransform.tx() * globalScaleFactor, @@ -929,6 +1007,46 @@ std::string MotionEvent::actionToString(int32_t action) { return android::base::StringPrintf("%" PRId32, action); } +std::tuple<int32_t, std::vector<PointerProperties>, std::vector<PointerCoords>> MotionEvent::split( + int32_t action, int32_t flags, int32_t historySize, + const std::vector<PointerProperties>& pointerProperties, + const std::vector<PointerCoords>& pointerCoords, + std::bitset<MAX_POINTER_ID + 1> splitPointerIds) { + LOG_ALWAYS_FATAL_IF(!splitPointerIds.any()); + const auto pointerCount = pointerProperties.size(); + LOG_ALWAYS_FATAL_IF(pointerCoords.size() != (pointerCount * (historySize + 1))); + const auto splitCount = splitPointerIds.count(); + + std::vector<PointerProperties> splitPointerProperties; + std::vector<PointerCoords> splitPointerCoords; + + for (uint32_t i = 0; i < pointerCount; i++) { + if (splitPointerIds.test(pointerProperties[i].id)) { + splitPointerProperties.emplace_back(pointerProperties[i]); + } + } + for (uint32_t i = 0; i < pointerCoords.size(); i++) { + if (splitPointerIds.test(pointerProperties[i % pointerCount].id)) { + splitPointerCoords.emplace_back(pointerCoords[i]); + } + } + LOG_ALWAYS_FATAL_IF(splitPointerCoords.size() != + (splitPointerProperties.size() * (historySize + 1))); + + if (CC_UNLIKELY(splitPointerProperties.size() != splitCount)) { + // TODO(b/329107108): Promote this to a fatal check once bugs in the caller are resolved. + LOG(ERROR) << "Cannot split MotionEvent: Requested splitting " << splitCount + << " pointers from the original event, but the original event only contained " + << splitPointerProperties.size() << " of those pointers."; + } + + // TODO(b/327503168): Verify the splitDownTime here once it is used correctly. + + const auto splitAction = resolveActionForSplitMotionEvent(action, flags, pointerProperties, + splitPointerProperties); + return {splitAction, splitPointerProperties, splitPointerCoords}; +} + // Apply the given transformation to the point without checking whether the entire transform // should be disregarded altogether for the provided source. static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform, @@ -1002,6 +1120,33 @@ PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, return out; } +bool MotionEvent::operator==(const android::MotionEvent& o) const { + // We use NaN values to represent invalid cursor positions. Since NaN values are not equal + // to themselves according to IEEE 754, we cannot use the default equality operator to compare + // MotionEvents. Therefore we define a custom equality operator with special handling for NaNs. + // clang-format off + return InputEvent::operator==(static_cast<const InputEvent&>(o)) && + mAction == o.mAction && + mActionButton == o.mActionButton && + mFlags == o.mFlags && + mEdgeFlags == o.mEdgeFlags && + mMetaState == o.mMetaState && + mButtonState == o.mButtonState && + mClassification == o.mClassification && + mTransform == o.mTransform && + mXPrecision == o.mXPrecision && + mYPrecision == o.mYPrecision && + ((std::isnan(mRawXCursorPosition) && std::isnan(o.mRawXCursorPosition)) || + mRawXCursorPosition == o.mRawXCursorPosition) && + ((std::isnan(mRawYCursorPosition) && std::isnan(o.mRawYCursorPosition)) || + mRawYCursorPosition == o.mRawYCursorPosition) && + mRawTransform == o.mRawTransform && mDownTime == o.mDownTime && + mPointerProperties == o.mPointerProperties && + mSampleEventTimes == o.mSampleEventTimes && + mSamplePointerCoords == o.mSamplePointerCoords; + // clang-format on +} + std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction()); if (event.getActionButton() != 0) { @@ -1032,6 +1177,9 @@ std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { if (event.getMetaState() != 0) { out << ", metaState=" << event.getMetaState(); } + if (event.getFlags() != 0) { + out << ", flags=0x" << std::hex << event.getFlags() << std::dec; + } if (event.getEdgeFlags() != 0) { out << ", edgeFlags=" << event.getEdgeFlags(); } @@ -1046,7 +1194,7 @@ std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << ", deviceId=" << event.getDeviceId(); out << ", source=" << inputEventSourceToString(event.getSource()); out << ", displayId=" << event.getDisplayId(); - out << ", eventId=" << event.getId(); + out << ", eventId=0x" << std::hex << event.getId() << std::dec; out << "}"; return out; } diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index ccc83231fa..c348833747 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -193,14 +193,16 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mHasSensor(other.mHasSensor), mMotionRanges(other.mMotionRanges), mSensors(other.mSensors), - mLights(other.mLights) {} + mLights(other.mLights), + mViewBehavior(other.mViewBehavior) {} InputDeviceInfo::~InputDeviceInfo() { } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, int32_t associatedDisplayId) { + bool isExternal, bool hasMic, int32_t associatedDisplayId, + InputDeviceViewBehavior viewBehavior) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; @@ -215,6 +217,7 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mHasBattery = false; mHasButtonUnderPad = false; mHasSensor = false; + mViewBehavior = viewBehavior; mUsiVersion.reset(); mMotionRanges.clear(); mSensors.clear(); diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index 0e627e56fd..8db0ca588b 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -348,7 +348,9 @@ namespace android { DEFINE_KEYCODE(MACRO_1), \ DEFINE_KEYCODE(MACRO_2), \ DEFINE_KEYCODE(MACRO_3), \ - DEFINE_KEYCODE(MACRO_4) + DEFINE_KEYCODE(MACRO_4), \ + DEFINE_KEYCODE(EMOJI_PICKER), \ + DEFINE_KEYCODE(SCREENSHOT) // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 09e98d0515..b3a36ebf5a 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -10,6 +10,7 @@ #include <fcntl.h> #include <inttypes.h> #include <math.h> +#include <poll.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> @@ -25,10 +26,13 @@ #include <com_android_input_flags.h> #include <input/InputTransport.h> +#include <input/PrintTools.h> #include <input/TraceTools.h> namespace input_flags = com::android::input::flags; +namespace android { + namespace { /** @@ -94,21 +98,91 @@ bool debugResampling() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); } -} // namespace +android::base::unique_fd dupChannelFd(int fd) { + android::base::unique_fd newFd(::dup(fd)); + if (!newFd.ok()) { + ALOGE("Could not duplicate fd %i : %s", fd, strerror(errno)); + const bool hitFdLimit = errno == EMFILE || errno == ENFILE; + // If this process is out of file descriptors, then throwing that might end up exploding + // on the other side of a binder call, which isn't really helpful. + // Better to just crash here and hope that the FD leak is slow. + // Other failures could be client errors, so we still propagate those back to the caller. + LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel"); + return {}; + } + return newFd; +} -using android::base::Result; -using android::base::StringPrintf; +void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) { + event.initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source, + msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action, + msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode, + msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime, + msg.body.key.eventTime); +} -namespace android { +void initializeFocusEvent(FocusEvent& event, const InputMessage& msg) { + event.initialize(msg.body.focus.eventId, msg.body.focus.hasFocus); +} + +void initializeCaptureEvent(CaptureEvent& event, const InputMessage& msg) { + event.initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled); +} + +void initializeDragEvent(DragEvent& event, const InputMessage& msg) { + event.initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y, + msg.body.drag.isExiting); +} + +void initializeMotionEvent(MotionEvent& event, const InputMessage& msg) { + uint32_t pointerCount = msg.body.motion.pointerCount; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties[i] = msg.body.motion.pointers[i].properties; + pointerCoords[i] = msg.body.motion.pointers[i].coords; + } + + ui::Transform transform; + transform.set({msg.body.motion.dsdx, msg.body.motion.dtdx, msg.body.motion.tx, + msg.body.motion.dtdy, msg.body.motion.dsdy, msg.body.motion.ty, 0, 0, 1}); + ui::Transform displayTransform; + displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw, + msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw, + 0, 0, 1}); + event.initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source, + msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action, + msg.body.motion.actionButton, msg.body.motion.flags, msg.body.motion.edgeFlags, + msg.body.motion.metaState, msg.body.motion.buttonState, + msg.body.motion.classification, transform, msg.body.motion.xPrecision, + msg.body.motion.yPrecision, msg.body.motion.xCursorPosition, + msg.body.motion.yCursorPosition, displayTransform, msg.body.motion.downTime, + msg.body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); +} + +void addSample(MotionEvent& event, const InputMessage& msg) { + uint32_t pointerCount = msg.body.motion.pointerCount; + PointerCoords pointerCoords[pointerCount]; + for (uint32_t i = 0; i < pointerCount; i++) { + pointerCoords[i] = msg.body.motion.pointers[i].coords; + } + + event.setMetaState(event.getMetaState() | msg.body.motion.metaState); + event.addSample(msg.body.motion.eventTime, pointerCoords); +} + +void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) { + event.initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode); +} // Socket buffer size. The default is typically about 128KB, which is much larger than // we really need. So we make it smaller. It just needs to be big enough to hold // a few dozen large multi-finger motion events in the case where an application gets // behind processing touches. -static const size_t SOCKET_BUFFER_SIZE = 32 * 1024; +static constexpr size_t SOCKET_BUFFER_SIZE = 32 * 1024; // Nanoseconds per milliseconds. -static const nsecs_t NANOS_PER_MS = 1000000; +static constexpr nsecs_t NANOS_PER_MS = 1000000; // Latency added during resampling. A few milliseconds doesn't hurt much but // reduces the impact of mispredicted touch positions. @@ -141,32 +215,28 @@ static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; * Crash if the events that are getting sent to the InputPublisher are inconsistent. * Enable this via "adb shell setprop log.tag.InputTransportVerifyEvents DEBUG" */ -static bool verifyEvents() { +bool verifyEvents() { return input_flags::enable_outbound_event_verification() || __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VerifyEvents", ANDROID_LOG_INFO); } -template<typename T> -inline static T min(const T& a, const T& b) { - return a < b ? a : b; -} - -inline static float lerp(float a, float b, float alpha) { +inline float lerp(float a, float b, float alpha) { return a + alpha * (b - a); } -inline static bool isPointerEvent(int32_t source) { +inline bool isPointerEvent(int32_t source) { return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; } -inline static const char* toString(bool value) { - return value ? "true" : "false"; -} - -static bool shouldResampleTool(ToolType toolType) { +bool shouldResampleTool(ToolType toolType) { return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN; } +} // namespace + +using android::base::Result; +using android::base::StringPrintf; + // --- InputMessage --- bool InputMessage::isValid(size_t actualSize) const { @@ -394,15 +464,23 @@ std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token)); } -InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) - : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) { +std::unique_ptr<InputChannel> InputChannel::create( + android::os::InputChannelCore&& parceledChannel) { + return InputChannel::create(parceledChannel.name, parceledChannel.fd.release(), + parceledChannel.token); +} + +InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) { + this->name = std::move(name); + this->fd.reset(std::move(fd)); + this->token = std::move(token); ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel constructed: name='%s', fd=%d", - getName().c_str(), getFd().get()); + getName().c_str(), getFd()); } InputChannel::~InputChannel() { ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel destroyed: name='%s', fd=%d", - getName().c_str(), getFd().get()); + getName().c_str(), getFd()); } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -440,19 +518,19 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { ATRACE_NAME_IF(ATRACE_ENABLED(), StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")", - mName.c_str(), msg->header.seq, msg->header.type)); + name.c_str(), msg->header.seq, msg->header.type)); const size_t msgLength = msg->size(); InputMessage cleanMsg; msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(getFd().get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { int error = errno; ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ error sending message of type %s, %s", - mName.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); + name.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -464,12 +542,12 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { if (size_t(nWrite) != msgLength) { ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ error sending message type %s, send was incomplete", mName.c_str(), + "channel '%s' ~ error sending message type %s, send was incomplete", name.c_str(), ftl::enum_string(msg->header.type).c_str()); return DEAD_OBJECT; } - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(), + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", name.c_str(), ftl::enum_string(msg->header.type).c_str()); return OK; @@ -478,13 +556,13 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::recv(getFd().get(), msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { int error = errno; ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d", - mName.c_str(), errno); + name.c_str(), errno); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -496,81 +574,85 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { if (nRead == 0) { // check for EOF ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ receive message failed because peer was closed", mName.c_str()); + "channel '%s' ~ receive message failed because peer was closed", name.c_str()); return DEAD_OBJECT; } if (!msg->isValid(nRead)) { - ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead); + ALOGE("channel '%s' ~ received invalid message of size %zd", name.c_str(), nRead); return BAD_VALUE; } - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(), + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", name.c_str(), ftl::enum_string(msg->header.type).c_str()); if (ATRACE_ENABLED()) { // Add an additional trace point to include data about the received message. std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")", - mName.c_str(), msg->header.seq, msg->header.type); + name.c_str(), msg->header.seq, msg->header.type); ATRACE_NAME(message.c_str()); } return OK; } -std::unique_ptr<InputChannel> InputChannel::dup() const { - base::unique_fd newFd(dupFd()); - return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); +bool InputChannel::probablyHasInput() const { + struct pollfd pfds = {.fd = fd.get(), .events = POLLIN}; + if (::poll(&pfds, /*nfds=*/1, /*timeout=*/0) <= 0) { + // This can be a false negative because EINTR and ENOMEM are not handled. The latter should + // be extremely rare. The EINTR is also unlikely because it happens only when the signal + // arrives while the syscall is executed, and the syscall is quick. Hitting EINTR too often + // would be a sign of having too many signals, which is a bigger performance problem. A + // common tradition is to repeat the syscall on each EINTR, but it is not necessary here. + // In other words, the missing one liner is replaced by a multiline explanation. + return false; + } + // From poll(2): The bits returned in |revents| can include any of those specified in |events|, + // or one of the values POLLERR, POLLHUP, or POLLNVAL. + return (pfds.revents & POLLIN) != 0; } -void InputChannel::copyTo(InputChannel& outChannel) const { - outChannel.mName = getName(); - outChannel.mFd = dupFd(); - outChannel.mToken = getConnectionToken(); +void InputChannel::waitForMessage(std::chrono::milliseconds timeout) const { + if (timeout < 0ms) { + LOG(FATAL) << "Timeout cannot be negative, received " << timeout.count(); + } + struct pollfd pfds = {.fd = fd.get(), .events = POLLIN}; + int ret; + std::chrono::time_point<std::chrono::steady_clock> stopTime = + std::chrono::steady_clock::now() + timeout; + std::chrono::milliseconds remaining = timeout; + do { + ret = ::poll(&pfds, /*nfds=*/1, /*timeout=*/remaining.count()); + remaining = std::chrono::duration_cast<std::chrono::milliseconds>( + stopTime - std::chrono::steady_clock::now()); + } while (ret == -1 && errno == EINTR && remaining > 0ms); } -status_t InputChannel::writeToParcel(android::Parcel* parcel) const { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - return parcel->writeStrongBinder(mToken) - ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd); +std::unique_ptr<InputChannel> InputChannel::dup() const { + base::unique_fd newFd(dupChannelFd(fd.get())); + return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); } -status_t InputChannel::readFromParcel(const android::Parcel* parcel) { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - mToken = parcel->readStrongBinder(); - return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd); +void InputChannel::copyTo(android::os::InputChannelCore& outChannel) const { + outChannel.name = getName(); + outChannel.fd.reset(dupChannelFd(fd.get())); + outChannel.token = getConnectionToken(); } -sp<IBinder> InputChannel::getConnectionToken() const { - return mToken; +void InputChannel::moveChannel(std::unique_ptr<InputChannel> from, + android::os::InputChannelCore& outChannel) { + outChannel.name = from->getName(); + outChannel.fd = android::os::ParcelFileDescriptor(std::move(from->fd)); + outChannel.token = from->getConnectionToken(); } -base::unique_fd InputChannel::dupFd() const { - base::unique_fd newFd(::dup(getFd().get())); - if (!newFd.ok()) { - ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(), - strerror(errno)); - const bool hitFdLimit = errno == EMFILE || errno == ENFILE; - // If this process is out of file descriptors, then throwing that might end up exploding - // on the other side of a binder call, which isn't really helpful. - // Better to just crash here and hope that the FD leak is slow. - // Other failures could be client errors, so we still propagate those back to the caller. - LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", - getName().c_str()); - return {}; - } - return newFd; +sp<IBinder> InputChannel::getConnectionToken() const { + return token; } // --- InputPublisher --- InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) - : mChannel(channel), mInputVerifier(channel->getName()) {} + : mChannel(channel), mInputVerifier(mChannel->getName()) {} InputPublisher::~InputPublisher() { } @@ -646,7 +728,7 @@ status_t InputPublisher::publishMotionEvent( "action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, classification=%s," "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " - "pointerCount=%" PRIu32 " \n%s", + "pointerCount=%" PRIu32 "\n%s", mChannel->getName().c_str(), __func__, seq, eventId, deviceId, inputEventSourceToString(source).c_str(), displayId, MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags, @@ -850,6 +932,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32, mMsg.header.seq); + + // Trace the event processing timeline - event was just read from the socket + ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/mMsg.header.seq); } if (result) { // Consume the next batched event unless batches are being held for later. @@ -871,7 +956,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum KeyEvent* keyEvent = factory->createKeyEvent(); if (!keyEvent) return NO_MEMORY; - initializeKeyEvent(keyEvent, &mMsg); + initializeKeyEvent(*keyEvent, mMsg); *outSeq = mMsg.header.seq; *outEvent = keyEvent; ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, @@ -934,7 +1019,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum if (!motionEvent) return NO_MEMORY; updateTouchState(mMsg); - initializeMotionEvent(motionEvent, &mMsg); + initializeMotionEvent(*motionEvent, mMsg); *outSeq = mMsg.header.seq; *outEvent = motionEvent; @@ -956,7 +1041,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum FocusEvent* focusEvent = factory->createFocusEvent(); if (!focusEvent) return NO_MEMORY; - initializeFocusEvent(focusEvent, &mMsg); + initializeFocusEvent(*focusEvent, mMsg); *outSeq = mMsg.header.seq; *outEvent = focusEvent; break; @@ -966,7 +1051,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum CaptureEvent* captureEvent = factory->createCaptureEvent(); if (!captureEvent) return NO_MEMORY; - initializeCaptureEvent(captureEvent, &mMsg); + initializeCaptureEvent(*captureEvent, mMsg); *outSeq = mMsg.header.seq; *outEvent = captureEvent; break; @@ -976,7 +1061,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum DragEvent* dragEvent = factory->createDragEvent(); if (!dragEvent) return NO_MEMORY; - initializeDragEvent(dragEvent, &mMsg); + initializeDragEvent(*dragEvent, mMsg); *outSeq = mMsg.header.seq; *outEvent = dragEvent; break; @@ -986,7 +1071,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum TouchModeEvent* touchModeEvent = factory->createTouchModeEvent(); if (!touchModeEvent) return NO_MEMORY; - initializeTouchModeEvent(touchModeEvent, &mMsg); + initializeTouchModeEvent(*touchModeEvent, mMsg); *outSeq = mMsg.header.seq; *outEvent = touchModeEvent; break; @@ -1048,9 +1133,9 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, seqChain.seq = msg.header.seq; seqChain.chain = chain; mSeqChains.push_back(seqChain); - addSample(motionEvent, &msg); + addSample(*motionEvent, msg); } else { - initializeMotionEvent(motionEvent, &msg); + initializeMotionEvent(*motionEvent, msg); } chain = msg.header.seq; } @@ -1231,7 +1316,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, delta); return; } - nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION); + nsecs_t maxPredict = current->eventTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION); if (sampleTime > maxPredict) { ALOGD_IF(debugResampling(), "Sample time is too far in the future, adjusting prediction " @@ -1388,6 +1473,9 @@ status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) // message anymore. If the socket write did not succeed, we will try again and will still // need consume time. popConsumeTime(seq); + + // Trace the event processing timeline - event was just finished + ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/seq); } return result; } @@ -1406,6 +1494,10 @@ int32_t InputConsumer::getPendingBatchSource() const { return head.body.motion.source; } +bool InputConsumer::probablyHasInput() const { + return hasPendingBatch() || mChannel->probablyHasInput(); +} + ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mBatches.size(); i++) { const Batch& batch = mBatches[i]; @@ -1427,69 +1519,6 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { return -1; } -void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize(msg->body.key.eventId, msg->body.key.deviceId, msg->body.key.source, - msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action, - msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode, - msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime, - msg->body.key.eventTime); -} - -void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus); -} - -void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) { - event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled); -} - -void InputConsumer::initializeDragEvent(DragEvent* event, const InputMessage* msg) { - event->initialize(msg->body.drag.eventId, msg->body.drag.x, msg->body.drag.y, - msg->body.drag.isExiting); -} - -void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { - uint32_t pointerCount = msg->body.motion.pointerCount; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i] = msg->body.motion.pointers[i].properties; - pointerCoords[i] = msg->body.motion.pointers[i].coords; - } - - ui::Transform transform; - transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx, - msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1}); - ui::Transform displayTransform; - displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw, - msg->body.motion.txRaw, msg->body.motion.dtdyRaw, - msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1}); - event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source, - msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, - msg->body.motion.actionButton, msg->body.motion.flags, - msg->body.motion.edgeFlags, msg->body.motion.metaState, - msg->body.motion.buttonState, msg->body.motion.classification, transform, - msg->body.motion.xPrecision, msg->body.motion.yPrecision, - msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime, - pointerCount, pointerProperties, pointerCoords); -} - -void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) { - event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode); -} - -void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { - uint32_t pointerCount = msg->body.motion.pointerCount; - PointerCoords pointerCoords[pointerCount]; - for (uint32_t i = 0; i < pointerCount; i++) { - pointerCoords[i] = msg->body.motion.pointers[i].coords; - } - - event->setMetaState(event->getMetaState() | msg->body.motion.metaState); - event->addSample(msg->body.motion.eventTime, pointerCoords); -} - bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { const InputMessage& head = batch.samples[0]; uint32_t pointerCount = msg->body.motion.pointerCount; diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index ab8c341b15..508818852e 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -97,6 +97,10 @@ static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_ bool kernelConfigsArePresent(const std::set<std::string>& configs) { #if defined(__ANDROID__) + if (configs.empty()) { + return true; + } + std::map<std::string, std::string> kernelConfigs; const status_t result = android::kernelconfigs::LoadKernelConfigs(&kernelConfigs); LOG_ALWAYS_FATAL_IF(result != OK, "Kernel configs could not be fetched"); diff --git a/libs/input/MotionPredictorMetricsManager.cpp b/libs/input/MotionPredictorMetricsManager.cpp index 0412d08181..6872af2aa5 100644 --- a/libs/input/MotionPredictorMetricsManager.cpp +++ b/libs/input/MotionPredictorMetricsManager.cpp @@ -113,7 +113,12 @@ void MotionPredictorMetricsManager::onRecord(const MotionEvent& inputEvent) { // Adds new predictions to mRecentPredictions and maintains the invariant that elements are // sorted in ascending order of targetTimestamp. void MotionPredictorMetricsManager::onPredict(const MotionEvent& predictionEvent) { - for (size_t i = 0; i < predictionEvent.getHistorySize() + 1; ++i) { + const size_t numPredictions = predictionEvent.getHistorySize() + 1; + if (numPredictions > mMaxNumPredictions) { + LOG(WARNING) << "numPredictions (" << numPredictions << ") > mMaxNumPredictions (" + << mMaxNumPredictions << "). Ignoring extra predictions in metrics."; + } + for (size_t i = 0; (i < numPredictions) && (i < mMaxNumPredictions); ++i) { // Convert MotionEvent to PredictionPoint. const PointerCoords* coords = predictionEvent.getHistoricalRawPointerCoords(/*pointerIndex=*/0, i); @@ -325,42 +330,44 @@ void MotionPredictorMetricsManager::computeAtomFields() { mAtomFields[i].highVelocityOffTrajectoryRmse = static_cast<int>(offTrajectoryRmse * 1000); } + } - // Scale-invariant errors: reported only for the last time bucket, where the values - // represent an average across all time buckets. - if (i + 1 == mMaxNumPredictions) { - // Compute error averages. - float alongTrajectoryRmseSum = 0; - float offTrajectoryRmseSum = 0; - for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) { - // If we have general errors (checked above), we should always also have - // scale-invariant errors. - LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantErrorsCount == 0, - "mAggregatedMetrics[%zu].scaleInvariantErrorsCount is 0", j); - - LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0, - "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f " - "should not be negative", - j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse); - alongTrajectoryRmseSum += - std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse / - mAggregatedMetrics[j].scaleInvariantErrorsCount); - - LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0, - "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f " - "should not be negative", - j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse); - offTrajectoryRmseSum += - std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse / - mAggregatedMetrics[j].scaleInvariantErrorsCount); + // Scale-invariant errors: the average scale-invariant error across all time buckets + // is reported in the last time bucket. + { + // Compute error averages. + float alongTrajectoryRmseSum = 0; + float offTrajectoryRmseSum = 0; + int bucket_count = 0; + for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) { + if (mAggregatedMetrics[j].scaleInvariantErrorsCount == 0) { + continue; } - const float averageAlongTrajectoryRmse = - alongTrajectoryRmseSum / mAggregatedMetrics.size(); + LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0, + "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f " + "should not be negative", + j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse); + alongTrajectoryRmseSum += + std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse / + mAggregatedMetrics[j].scaleInvariantErrorsCount); + + LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0, + "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f " + "should not be negative", + j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse); + offTrajectoryRmseSum += std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse / + mAggregatedMetrics[j].scaleInvariantErrorsCount); + + ++bucket_count; + } + + if (bucket_count > 0) { + const float averageAlongTrajectoryRmse = alongTrajectoryRmseSum / bucket_count; mAtomFields.back().scaleInvariantAlongTrajectoryRmse = static_cast<int>(averageAlongTrajectoryRmse * 1000); - const float averageOffTrajectoryRmse = offTrajectoryRmseSum / mAggregatedMetrics.size(); + const float averageOffTrajectoryRmse = offTrajectoryRmseSum / bucket_count; mAtomFields.back().scaleInvariantOffTrajectoryRmse = static_cast<int>(averageOffTrajectoryRmse * 1000); } diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index c835a081a5..edd31e91d3 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -15,7 +15,6 @@ */ #define LOG_TAG "VelocityControl" -//#define LOG_NDEBUG 0 // Log debug messages about acceleration. static constexpr bool DEBUG_ACCELERATION = false; @@ -23,6 +22,7 @@ static constexpr bool DEBUG_ACCELERATION = false; #include <math.h> #include <limits.h> +#include <android-base/logging.h> #include <input/VelocityControl.h> #include <utils/BitSet.h> #include <utils/Timers.h> @@ -37,15 +37,6 @@ VelocityControl::VelocityControl() { reset(); } -const VelocityControlParameters& VelocityControl::getParameters() const{ - return mParameters; -} - -void VelocityControl::setParameters(const VelocityControlParameters& parameters) { - mParameters = parameters; - reset(); -} - void VelocityControl::reset() { mLastMovementTime = LLONG_MIN; mRawPositionX = 0; @@ -54,65 +45,156 @@ void VelocityControl::reset() { } void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { - if ((deltaX && *deltaX) || (deltaY && *deltaY)) { - if (eventTime >= mLastMovementTime + STOP_TIME) { - if (DEBUG_ACCELERATION && mLastMovementTime != LLONG_MIN) { - ALOGD("VelocityControl: stopped, last movement was %0.3fms ago", - (eventTime - mLastMovementTime) * 0.000001f); - } - reset(); - } + if ((deltaX == nullptr || *deltaX == 0) && (deltaY == nullptr || *deltaY == 0)) { + return; + } + if (eventTime >= mLastMovementTime + STOP_TIME) { + ALOGD_IF(DEBUG_ACCELERATION && mLastMovementTime != LLONG_MIN, + "VelocityControl: stopped, last movement was %0.3fms ago", + (eventTime - mLastMovementTime) * 0.000001f); + reset(); + } - mLastMovementTime = eventTime; - if (deltaX) { - mRawPositionX += *deltaX; - } - if (deltaY) { - mRawPositionY += *deltaY; - } - mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X, - mRawPositionX); - mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y, - mRawPositionY); - - std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); - std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); - float scale = mParameters.scale; - if (vx && vy) { - float speed = hypotf(*vx, *vy) * scale; - if (speed >= mParameters.highThreshold) { - // Apply full acceleration above the high speed threshold. - scale *= mParameters.acceleration; - } else if (speed > mParameters.lowThreshold) { - // Linearly interpolate the acceleration to apply between the low and high - // speed thresholds. - scale *= 1 + (speed - mParameters.lowThreshold) - / (mParameters.highThreshold - mParameters.lowThreshold) - * (mParameters.acceleration - 1); - } - - if (DEBUG_ACCELERATION) { - ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " - "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale); - } - - } else { - if (DEBUG_ACCELERATION) { - ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration); - } - } + mLastMovementTime = eventTime; + if (deltaX) { + mRawPositionX += *deltaX; + } + if (deltaY) { + mRawPositionY += *deltaY; + } + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X, mRawPositionX); + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y, mRawPositionY); + scaleDeltas(deltaX, deltaY); +} + +// --- SimpleVelocityControl --- + +const VelocityControlParameters& SimpleVelocityControl::getParameters() const { + return mParameters; +} - if (deltaX) { - *deltaX *= scale; +void SimpleVelocityControl::setParameters(const VelocityControlParameters& parameters) { + mParameters = parameters; + reset(); +} + +void SimpleVelocityControl::scaleDeltas(float* deltaX, float* deltaY) { + std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); + std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); + float scale = mParameters.scale; + if (vx.has_value() && vy.has_value()) { + float speed = hypotf(*vx, *vy) * scale; + if (speed >= mParameters.highThreshold) { + // Apply full acceleration above the high speed threshold. + scale *= mParameters.acceleration; + } else if (speed > mParameters.lowThreshold) { + // Linearly interpolate the acceleration to apply between the low and high + // speed thresholds. + scale *= 1 + + (speed - mParameters.lowThreshold) / + (mParameters.highThreshold - mParameters.lowThreshold) * + (mParameters.acceleration - 1); } - if (deltaY) { - *deltaY *= scale; + + ALOGD_IF(DEBUG_ACCELERATION, + "SimpleVelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " + "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale); + + } else { + ALOGD_IF(DEBUG_ACCELERATION, + "SimpleVelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration); + } + + if (deltaX != nullptr) { + *deltaX *= scale; + } + if (deltaY != nullptr) { + *deltaY *= scale; + } +} + +// --- CurvedVelocityControl --- + +namespace { + +/** + * The resolution that we assume a mouse to have, in counts per inch. + * + * Mouse resolutions vary wildly, but 800 CPI is probably the most common. There should be enough + * range in the available sensitivity settings to accommodate users of mice with other resolutions. + */ +constexpr int32_t MOUSE_CPI = 800; + +float countsToMm(float counts) { + return counts / MOUSE_CPI * 25.4; +} + +} // namespace + +CurvedVelocityControl::CurvedVelocityControl() + : mCurveSegments(createAccelerationCurveForPointerSensitivity(0)) {} + +void CurvedVelocityControl::setCurve(const std::vector<AccelerationCurveSegment>& curve) { + mCurveSegments = curve; +} + +void CurvedVelocityControl::setAccelerationEnabled(bool enabled) { + mAccelerationEnabled = enabled; +} + +void CurvedVelocityControl::scaleDeltas(float* deltaX, float* deltaY) { + if (!mAccelerationEnabled) { + ALOGD_IF(DEBUG_ACCELERATION, "CurvedVelocityControl: acceleration disabled"); + return; + } + + std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); + std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); + + float ratio; + if (vx.has_value() && vy.has_value()) { + float vxMmPerS = countsToMm(*vx); + float vyMmPerS = countsToMm(*vy); + float speedMmPerS = sqrtf(vxMmPerS * vxMmPerS + vyMmPerS * vyMmPerS); + + const AccelerationCurveSegment& seg = segmentForSpeed(speedMmPerS); + ratio = seg.baseGain + seg.reciprocal / speedMmPerS; + ALOGD_IF(DEBUG_ACCELERATION, + "CurvedVelocityControl: velocities (%0.3f, %0.3f) → speed %0.3f → ratio %0.3f", + vxMmPerS, vyMmPerS, speedMmPerS, ratio); + } else { + // We don't have enough data to compute a velocity yet. This happens early in the movement, + // when the speed is presumably low, so use the base gain of the first segment of the curve. + // (This would behave oddly for curves with a reciprocal term on the first segment, but we + // don't have any of those, and they'd be very strange at velocities close to zero anyway.) + ratio = mCurveSegments[0].baseGain; + ALOGD_IF(DEBUG_ACCELERATION, + "CurvedVelocityControl: unknown velocity, using base gain of first segment (%.3f)", + ratio); + } + + if (deltaX != nullptr) { + *deltaX *= ratio; + } + if (deltaY != nullptr) { + *deltaY *= ratio; + } +} + +const AccelerationCurveSegment& CurvedVelocityControl::segmentForSpeed(float speedMmPerS) { + for (const AccelerationCurveSegment& seg : mCurveSegments) { + if (speedMmPerS <= seg.maxPointerSpeedMmPerS) { + return seg; } } + ALOGE("CurvedVelocityControl: No segment found for speed %.3f; last segment should always have " + "a max speed of infinity.", + speedMmPerS); + return mCurveSegments.back(); } } // namespace android diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp index db7031ab03..eea06f1720 100644 --- a/libs/input/VirtualInputDevice.cpp +++ b/libs/input/VirtualInputDevice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,9 @@ static bool isDebug() { } namespace android { + VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {} + VirtualInputDevice::~VirtualInputDevice() { ioctl(mFd, UI_DEV_DESTROY); } @@ -56,7 +58,7 @@ bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t v return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev); } -/** Utility method to write keyboard key events or mouse button events. */ +/** Utility method to write keyboard key events or mouse/stylus button events. */ bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAction, const std::map<int, int>& evKeyCodeMapping, const std::map<int, UinputAction>& actionMapping, @@ -68,13 +70,17 @@ bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAct } auto actionIterator = actionMapping.find(androidAction); if (actionIterator == actionMapping.end()) { + ALOGE("Unsupported native action for android action %d", androidAction); return false; } - if (!writeInputEvent(EV_KEY, static_cast<uint16_t>(evKeyCodeIterator->second), - static_cast<int32_t>(actionIterator->second), eventTime)) { + int32_t action = static_cast<int32_t>(actionIterator->second); + uint16_t evKeyCode = static_cast<uint16_t>(evKeyCodeIterator->second); + if (!writeInputEvent(EV_KEY, evKeyCode, action, eventTime)) { + ALOGE("Failed to write native action %d and EV keycode %u.", action, evKeyCode); return false; } if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) { + ALOGE("Failed to write SYN_REPORT for EV_KEY event."); return false; } return true; @@ -85,6 +91,7 @@ const std::map<int, UinputAction> VirtualKeyboard::KEY_ACTION_MAPPING = { {AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS}, {AKEY_EVENT_ACTION_UP, UinputAction::RELEASE}, }; + // Keycode mapping from https://source.android.com/devices/input/keyboard-devices const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = { {AKEYCODE_0, KEY_0}, @@ -195,7 +202,9 @@ const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = { {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA}, {AKEYCODE_LANGUAGE_SWITCH, KEY_LANGUAGE}, }; + VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {} + VirtualKeyboard::~VirtualKeyboard() {} bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction, @@ -275,6 +284,7 @@ const std::map<int, UinputAction> VirtualTouchscreen::TOUCH_ACTION_MAPPING = { {AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE}, {AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL}, }; + // Tool type mapping from https://source.android.com/devices/input/touch-devices const std::map<int, int> VirtualTouchscreen::TOOL_TYPE_MAPPING = { {AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER}, @@ -393,4 +403,110 @@ bool VirtualTouchscreen::handleTouchDown(int32_t pointerId, std::chrono::nanosec return true; } +// --- VirtualStylus --- +const std::map<int, int> VirtualStylus::TOOL_TYPE_MAPPING = { + {AMOTION_EVENT_TOOL_TYPE_STYLUS, BTN_TOOL_PEN}, + {AMOTION_EVENT_TOOL_TYPE_ERASER, BTN_TOOL_RUBBER}, +}; + +// Button code mapping from https://source.android.com/devices/input/touch-devices +const std::map<int, int> VirtualStylus::BUTTON_CODE_MAPPING = { + {AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, BTN_STYLUS}, + {AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, BTN_STYLUS2}, +}; + +VirtualStylus::VirtualStylus(unique_fd fd) + : VirtualInputDevice(std::move(fd)), mIsStylusDown(false) {} + +VirtualStylus::~VirtualStylus() {} + +bool VirtualStylus::writeMotionEvent(int32_t toolType, int32_t action, int32_t locationX, + int32_t locationY, int32_t pressure, int32_t tiltX, + int32_t tiltY, std::chrono::nanoseconds eventTime) { + auto actionIterator = VirtualTouchscreen::TOUCH_ACTION_MAPPING.find(action); + if (actionIterator == VirtualTouchscreen::TOUCH_ACTION_MAPPING.end()) { + ALOGE("Unsupported action passed for stylus: %d.", action); + return false; + } + UinputAction uinputAction = actionIterator->second; + auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType); + if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) { + ALOGE("Unsupported tool type passed for stylus: %d.", toolType); + return false; + } + uint16_t tool = static_cast<uint16_t>(toolTypeIterator->second); + if (uinputAction == UinputAction::PRESS && !handleStylusDown(tool, eventTime)) { + return false; + } + if (!mIsStylusDown) { + ALOGE("Action UP or MOVE received with no prior action DOWN for stylus %d.", mFd.get()); + return false; + } + if (uinputAction == UinputAction::RELEASE && !handleStylusUp(tool, eventTime)) { + return false; + } + if (!writeInputEvent(EV_ABS, ABS_X, locationX, eventTime)) { + ALOGE("Unsupported x-axis location passed for stylus: %d.", locationX); + return false; + } + if (!writeInputEvent(EV_ABS, ABS_Y, locationY, eventTime)) { + ALOGE("Unsupported y-axis location passed for stylus: %d.", locationY); + return false; + } + if (!writeInputEvent(EV_ABS, ABS_TILT_X, tiltX, eventTime)) { + ALOGE("Unsupported x-axis tilt passed for stylus: %d.", tiltX); + return false; + } + if (!writeInputEvent(EV_ABS, ABS_TILT_Y, tiltY, eventTime)) { + ALOGE("Unsupported y-axis tilt passed for stylus: %d.", tiltY); + return false; + } + if (!writeInputEvent(EV_ABS, ABS_PRESSURE, pressure, eventTime)) { + ALOGE("Unsupported pressure passed for stylus: %d.", pressure); + return false; + } + if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) { + ALOGE("Failed to write SYN_REPORT for stylus motion event."); + return false; + } + return true; +} + +bool VirtualStylus::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction, + std::chrono::nanoseconds eventTime) { + return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING, + VirtualMouse::BUTTON_ACTION_MAPPING, eventTime); +} + +bool VirtualStylus::handleStylusDown(uint16_t tool, std::chrono::nanoseconds eventTime) { + if (mIsStylusDown) { + ALOGE("Repetitive action DOWN event received for a stylus that is already down."); + return false; + } + if (!writeInputEvent(EV_KEY, tool, static_cast<int32_t>(UinputAction::PRESS), eventTime)) { + ALOGE("Failed to write EV_KEY for stylus tool type: %u.", tool); + return false; + } + if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS), eventTime)) { + ALOGE("Failed to write BTN_TOUCH for stylus press."); + return false; + } + mIsStylusDown = true; + return true; +} + +bool VirtualStylus::handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime) { + if (!writeInputEvent(EV_KEY, tool, static_cast<int32_t>(UinputAction::RELEASE), eventTime)) { + ALOGE("Failed to write EV_KEY for stylus tool type: %u.", tool); + return false; + } + if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE), + eventTime)) { + ALOGE("Failed to write BTN_TOUCH for stylus release."); + return false; + } + mIsStylusDown = false; + return true; +} + } // namespace android diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl index 00ebd4d34f..c1aacfbc53 100644 --- a/libs/input/android/os/IInputFlinger.aidl +++ b/libs/input/android/os/IInputFlinger.aidl @@ -16,14 +16,13 @@ package android.os; -import android.InputChannel; +import android.os.InputChannelCore; import android.gui.FocusRequest; -import android.gui.WindowInfo; /** @hide */ interface IInputFlinger { - InputChannel createInputChannel(in @utf8InCpp String name); + InputChannelCore createInputChannel(in @utf8InCpp String name); void removeInputChannel(in IBinder connectionToken); /** * Sets focus to the window identified by the token. This must be called diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/os/InputChannelCore.aidl index c2d1112dd3..888a553268 100644 --- a/libs/input/android/InputChannel.aidl +++ b/libs/input/android/os/InputChannelCore.aidl @@ -15,6 +15,16 @@ ** limitations under the License. */ -package android; +package android.os; -parcelable InputChannel cpp_header "input/InputTransport.h"; +import android.os.ParcelFileDescriptor; + +/** + * Input channel struct for sending InputChannel between processes. + * @hide + */ +parcelable InputChannelCore { + @utf8InCpp String name; + ParcelFileDescriptor fd; + IBinder token; +} diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl index 4e644fff06..5d391551c2 100644 --- a/libs/input/android/os/InputConfig.aidl +++ b/libs/input/android/os/InputConfig.aidl @@ -150,4 +150,11 @@ enum InputConfig { * likely a duplicate window with the same client token, but different bounds. */ CLONE = 1 << 16, + + /** + * If the stylus is currently down *anywhere* on the screen, new touches will not be delivered + * to the window with this flag. This helps prevent unexpected clicks on some system windows, + * like StatusBar and TaskBar. + */ + GLOBAL_STYLUS_BLOCKS_TOUCH = 1 << 17, } diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 54eeb39935..bdec5c33cd 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.input.flags" +container: "system" flag { name: "enable_outbound_event_verification" @@ -26,6 +27,13 @@ flag { namespace: "input" description: "Set to true to enable timer support for the touchpad Gestures library" bug: "297192727" + } + + flag { + name: "enable_input_event_tracing" + namespace: "input" + description: "Set to true to enable input event tracing, including always-on tracing on non-user builds" + bug: "210460522" } flag { @@ -52,15 +60,15 @@ flag { flag { name: "enable_touchpad_typing_palm_rejection" namespace: "input" - description: "Enable additional palm rejection on touchpad while typing" + description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard" bug: "301055381" } flag { - name: "remove_app_switch_drops" + name: "enable_v2_touchpad_typing_palm_rejection" namespace: "input" - description: "Remove the logic of dropping events due to pending app switch" - bug: "284808102" + description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click." + bug: "301055381" } flag { @@ -90,3 +98,31 @@ flag { description: "Remove pointer event tracking in WM after the Pointer Icon Refactor" bug: "315321016" } + +flag { + name: "enable_new_mouse_pointer_ballistics" + namespace: "input" + description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones" + bug: "315313622" +} + +flag { + name: "rate_limit_user_activity_poke_in_dispatcher" + namespace: "input" + description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher." + bug: "320499729" +} + +flag { + name: "input_device_view_behavior_api" + namespace: "input" + description: "Controls the API to provide InputDevice view behavior." + bug: "246946631" +} + +flag { + name: "enable_touchpad_fling_stop" + namespace: "input" + description: "Enable fling scrolling to be stopped by putting a finger on the touchpad again" + bug: "281106755" +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 13cfb491b5..93af4c2066 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -13,6 +13,7 @@ cc_test { cpp_std: "c++20", host_supported: true, srcs: [ + "BlockingQueue_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", @@ -25,6 +26,7 @@ cc_test { "TfLiteMotionPredictor_test.cpp", "TouchResampling_test.cpp", "TouchVideoFrame_test.cpp", + "VelocityControl_test.cpp", "VelocityTracker_test.cpp", "VerifiedInputEvent_test.cpp", ], diff --git a/libs/input/tests/BlockingQueue_test.cpp b/libs/input/tests/BlockingQueue_test.cpp new file mode 100644 index 0000000000..924b937080 --- /dev/null +++ b/libs/input/tests/BlockingQueue_test.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/BlockingQueue.h> + +#include <gtest/gtest.h> +#include <thread> + +namespace android { + +using std::chrono_literals::operator""ns; + +// --- BlockingQueueTest --- + +/** + * Validate basic pop and push operation. + */ +TEST(BlockingQueueTest, Queue_AddAndRemove) { + constexpr size_t capacity = 10; + BlockingQueue<int> queue(capacity); + + ASSERT_TRUE(queue.push(1)); + ASSERT_EQ(queue.pop(), 1); + + ASSERT_TRUE(queue.emplace(2)); + ASSERT_EQ(queue.popWithTimeout(0ns), 2); + + ASSERT_TRUE(queue.push(3)); + ASSERT_EQ(queue.popWithTimeout(100ns), 3); + + ASSERT_EQ(std::nullopt, queue.popWithTimeout(0ns)); +} + +/** + * Make sure the queue has strict capacity limits. + */ +TEST(BlockingQueueTest, Queue_ReachesCapacity) { + constexpr size_t capacity = 3; + BlockingQueue<int> queue(capacity); + + // First 3 elements should be added successfully + ASSERT_TRUE(queue.push(1)); + ASSERT_TRUE(queue.push(2)); + ASSERT_TRUE(queue.push(3)); + ASSERT_FALSE(queue.push(4)) << "Queue should reach capacity at size " << capacity; +} + +/** + * Make sure the queue maintains FIFO order. + * Add elements and remove them, and check the order. + */ +TEST(BlockingQueueTest, Queue_isFIFO) { + constexpr size_t capacity = 10; + BlockingQueue<int> queue(capacity); + + for (size_t i = 0; i < capacity; i++) { + ASSERT_TRUE(queue.push(static_cast<int>(i))); + } + for (size_t i = 0; i < capacity; i++) { + ASSERT_EQ(queue.pop(), static_cast<int>(i)); + } +} + +TEST(BlockingQueueTest, Queue_Clears) { + constexpr size_t capacity = 2; + BlockingQueue<int> queue(capacity); + + queue.push(1); + queue.push(2); + queue.clear(); + queue.push(3); + // Should no longer receive elements 1 and 2 + ASSERT_EQ(3, queue.pop()); +} + +TEST(BlockingQueueTest, Queue_Erases) { + constexpr size_t capacity = 4; + BlockingQueue<int> queue(capacity); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + // Erase elements 2 and 4 + queue.erase_if([](int element) { return element == 2 || element == 4; }); + // Should no longer receive elements 2 and 4 + ASSERT_EQ(1, queue.pop()); + ASSERT_EQ(3, queue.pop()); +} + +// --- BlockingQueueTest - Multiple threads --- + +TEST(BlockingQueueTest, Queue_AllowsMultipleThreads) { + constexpr size_t capacity = 100; // large capacity to increase likelihood that threads overlap + BlockingQueue<int> queue(capacity); + + // Fill queue from a different thread + std::thread fillQueue([&queue]() { + for (size_t i = 0; i < capacity; i++) { + ASSERT_TRUE(queue.push(static_cast<int>(i))); + } + }); + + // Make sure all elements are received in correct order + for (size_t i = 0; i < capacity; i++) { + ASSERT_EQ(queue.pop(), static_cast<int>(i)); + } + + fillQueue.join(); +} + +/** + * When the queue has no elements, and pop is called, it should block + * the current thread until an element is added to the queue (from another thread). + * Here we create a separate thread and call pop on an empty queue. Next, + * we check that the thread is blocked. + */ +TEST(BlockingQueueTest, Queue_BlocksWhileWaitingForElements) { + constexpr size_t capacity = 1; + BlockingQueue<int> queue(capacity); + + std::atomic_bool hasReceivedElement = false; + + // fill queue from a different thread + std::thread waitUntilHasElements([&queue, &hasReceivedElement]() { + queue.pop(); // This should block until an element has been added + hasReceivedElement = true; + }); + + ASSERT_FALSE(hasReceivedElement); + queue.push(1); + waitUntilHasElements.join(); + ASSERT_TRUE(hasReceivedElement); +} + +TEST(BlockingQueueTest, Queue_TimesOut) { + BlockingQueue<int> queue; + ASSERT_EQ(std::nullopt, queue.popWithTimeout(1ns)); +} + +} // namespace android diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 0661261003..02d4c07bfa 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -16,8 +16,6 @@ #include <array> -#include "TestHelpers.h" - #include <unistd.h> #include <time.h> #include <errno.h> @@ -32,37 +30,31 @@ namespace android { +namespace { +bool operator==(const InputChannel& left, const InputChannel& right) { + struct stat lhs, rhs; + if (fstat(left.getFd(), &lhs) != 0) { + return false; + } + if (fstat(right.getFd(), &rhs) != 0) { + return false; + } + // If file descriptors are pointing to same inode they are duplicated fds. + return left.getName() == right.getName() && + left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino; +} +} // namespace + class InputChannelTest : public testing::Test { }; +TEST_F(InputChannelTest, ClientAndServerTokensMatch) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; -TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { - // Our purpose here is to verify that the input channel destructor closes the - // file descriptor provided to it. One easy way is to provide it with one end - // of a pipe and to check for EPIPE on the other end after the channel is destroyed. - Pipe pipe; - - android::base::unique_fd sendFd(pipe.sendFd); - - std::unique_ptr<InputChannel> inputChannel = - InputChannel::create("channel name", std::move(sendFd), new BBinder()); - - EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; - EXPECT_STREQ("channel name", inputChannel->getName().c_str()) - << "channel should have provided name"; - EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd"; - - // InputChannel should be the owner of the file descriptor now - ASSERT_FALSE(sendFd.ok()); -} - -TEST_F(InputChannelTest, SetAndGetToken) { - Pipe pipe; - sp<IBinder> token = new BBinder(); - std::unique_ptr<InputChannel> channel = - InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token); - - EXPECT_EQ(token, channel->getConnectionToken()); + status_t result = + InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + EXPECT_EQ(serverChannel->getConnectionToken(), clientChannel->getConnectionToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { @@ -71,8 +63,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); - ASSERT_EQ(OK, result) - << "should have successfully opened a channel pair"; + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; // Name EXPECT_STREQ("channel name (server)", serverChannel->getName().c_str()) @@ -81,8 +72,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "client channel should have suffixed name"; // Server->Client communication - InputMessage serverMsg; - memset(&serverMsg, 0, sizeof(InputMessage)); + InputMessage serverMsg = {}; serverMsg.header.type = InputMessage::Type::KEY; serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) @@ -97,8 +87,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "client channel should receive the correct message from server channel"; // Client->Server communication - InputMessage clientReply; - memset(&clientReply, 0, sizeof(InputMessage)); + InputMessage clientReply = {}; clientReply.header.type = InputMessage::Type::FINISHED; clientReply.header.seq = 0x11223344; clientReply.body.finished.handled = true; @@ -116,6 +105,48 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "server channel should receive the correct message from client channel"; } +TEST_F(InputChannelTest, ProbablyHasInput) { + std::unique_ptr<InputChannel> senderChannel, receiverChannel; + + // Open a pair of channels. + status_t result = + InputChannel::openInputChannelPair("channel name", senderChannel, receiverChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + ASSERT_FALSE(receiverChannel->probablyHasInput()); + + // Send one message. + InputMessage serverMsg = {}; + serverMsg.header.type = InputMessage::Type::KEY; + serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; + EXPECT_EQ(OK, senderChannel->sendMessage(&serverMsg)) + << "server channel should be able to send message to client channel"; + + // Verify input is available. + bool hasInput = false; + do { + // The probablyHasInput() can return false positive under rare circumstances uncontrollable + // by the tests. Re-request the availability in this case. Returning |false| for a long + // time is not intended, and would cause a test timeout. + hasInput = receiverChannel->probablyHasInput(); + } while (!hasInput); + EXPECT_TRUE(hasInput) + << "client channel should observe that message is available before receiving it"; + + // Receive (consume) the message. + InputMessage clientMsg; + EXPECT_EQ(OK, receiverChannel->receiveMessage(&clientMsg)) + << "client channel should be able to receive message from server channel"; + EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) + << "client channel should receive the correct message from server channel"; + EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) + << "client channel should receive the correct message from server channel"; + + // Verify input is not available. + EXPECT_FALSE(receiverChannel->probablyHasInput()) + << "client should not observe any more messages after receiving the single one"; +} + TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { std::unique_ptr<InputChannel> serverChannel, clientChannel; @@ -195,25 +226,6 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { } } -TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) { - std::unique_ptr<InputChannel> serverChannel, clientChannel; - - status_t result = - InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel); - - ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - - InputChannel chan; - Parcel parcel; - ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel)); - parcel.setDataPosition(0); - chan.readFromParcel(&parcel); - - EXPECT_EQ(chan == *serverChannel, true) - << "inputchannel should be equal after parceling and unparceling.\n" - << "name " << chan.getName() << " name " << serverChannel->getName(); -} - TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) { std::unique_ptr<InputChannel> serverChannel, clientChannel; diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index a9655730fc..0df06b790e 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -23,6 +23,7 @@ #include <gtest/gtest.h> #include <gui/constants.h> #include <input/Input.h> +#include <input/InputEventBuilders.h> namespace android { @@ -31,6 +32,18 @@ static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; +static constexpr auto POINTER_0_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + +static constexpr auto POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + +static constexpr auto POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + +static constexpr auto POINTER_1_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + class BaseTest : public testing::Test { protected: static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, @@ -358,8 +371,10 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); ASSERT_EQ(MotionClassification::NONE, event->getClassification()); EXPECT_EQ(mTransform, event->getTransform()); - ASSERT_EQ(X_OFFSET, event->getXOffset()); - ASSERT_EQ(Y_OFFSET, event->getYOffset()); + ASSERT_NEAR((-RAW_X_OFFSET / RAW_X_SCALE) * X_SCALE + X_OFFSET, event->getRawXOffset(), + EPSILON); + ASSERT_NEAR((-RAW_Y_OFFSET / RAW_Y_SCALE) * Y_SCALE + Y_OFFSET, event->getRawYOffset(), + EPSILON); ASSERT_EQ(2.0f, event->getXPrecision()); ASSERT_EQ(2.1f, event->getYPrecision()); ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime()); @@ -554,25 +569,168 @@ TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) { ASSERT_EQ(event.getX(0), copy.getX(0)); } +TEST_F(MotionEventTest, SplitPointerDown) { + MotionEvent event = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6)) + .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8)) + .build(); + + MotionEvent splitDown; + std::bitset<MAX_POINTER_ID + 1> splitDownIds{}; + splitDownIds.set(6, true); + splitDown.splitFrom(event, splitDownIds, /*eventId=*/42); + ASSERT_EQ(splitDown.getAction(), AMOTION_EVENT_ACTION_DOWN); + ASSERT_EQ(splitDown.getPointerCount(), 1u); + ASSERT_EQ(splitDown.getPointerId(0), 6); + ASSERT_EQ(splitDown.getX(0), 6); + ASSERT_EQ(splitDown.getY(0), 6); + + MotionEvent splitPointerDown; + std::bitset<MAX_POINTER_ID + 1> splitPointerDownIds{}; + splitPointerDownIds.set(6, true); + splitPointerDownIds.set(8, true); + splitPointerDown.splitFrom(event, splitPointerDownIds, /*eventId=*/42); + ASSERT_EQ(splitPointerDown.getAction(), POINTER_0_DOWN); + ASSERT_EQ(splitPointerDown.getPointerCount(), 2u); + ASSERT_EQ(splitPointerDown.getPointerId(0), 6); + ASSERT_EQ(splitPointerDown.getX(0), 6); + ASSERT_EQ(splitPointerDown.getY(0), 6); + ASSERT_EQ(splitPointerDown.getPointerId(1), 8); + ASSERT_EQ(splitPointerDown.getX(1), 8); + ASSERT_EQ(splitPointerDown.getY(1), 8); + + MotionEvent splitMove; + std::bitset<MAX_POINTER_ID + 1> splitMoveIds{}; + splitMoveIds.set(4, true); + splitMove.splitFrom(event, splitMoveIds, /*eventId=*/43); + ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE); + ASSERT_EQ(splitMove.getPointerCount(), 1u); + ASSERT_EQ(splitMove.getPointerId(0), 4); + ASSERT_EQ(splitMove.getX(0), 4); + ASSERT_EQ(splitMove.getY(0), 4); +} + +TEST_F(MotionEventTest, SplitPointerUp) { + MotionEvent event = MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6)) + .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8)) + .build(); + + MotionEvent splitUp; + std::bitset<MAX_POINTER_ID + 1> splitUpIds{}; + splitUpIds.set(4, true); + splitUp.splitFrom(event, splitUpIds, /*eventId=*/42); + ASSERT_EQ(splitUp.getAction(), AMOTION_EVENT_ACTION_UP); + ASSERT_EQ(splitUp.getPointerCount(), 1u); + ASSERT_EQ(splitUp.getPointerId(0), 4); + ASSERT_EQ(splitUp.getX(0), 4); + ASSERT_EQ(splitUp.getY(0), 4); + + MotionEvent splitPointerUp; + std::bitset<MAX_POINTER_ID + 1> splitPointerUpIds{}; + splitPointerUpIds.set(4, true); + splitPointerUpIds.set(8, true); + splitPointerUp.splitFrom(event, splitPointerUpIds, /*eventId=*/42); + ASSERT_EQ(splitPointerUp.getAction(), POINTER_0_UP); + ASSERT_EQ(splitPointerUp.getPointerCount(), 2u); + ASSERT_EQ(splitPointerUp.getPointerId(0), 4); + ASSERT_EQ(splitPointerUp.getX(0), 4); + ASSERT_EQ(splitPointerUp.getY(0), 4); + ASSERT_EQ(splitPointerUp.getPointerId(1), 8); + ASSERT_EQ(splitPointerUp.getX(1), 8); + ASSERT_EQ(splitPointerUp.getY(1), 8); + + MotionEvent splitMove; + std::bitset<MAX_POINTER_ID + 1> splitMoveIds{}; + splitMoveIds.set(6, true); + splitMoveIds.set(8, true); + splitMove.splitFrom(event, splitMoveIds, /*eventId=*/43); + ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE); + ASSERT_EQ(splitMove.getPointerCount(), 2u); + ASSERT_EQ(splitMove.getPointerId(0), 6); + ASSERT_EQ(splitMove.getX(0), 6); + ASSERT_EQ(splitMove.getY(0), 6); + ASSERT_EQ(splitMove.getPointerId(1), 8); + ASSERT_EQ(splitMove.getX(1), 8); + ASSERT_EQ(splitMove.getY(1), 8); +} + +TEST_F(MotionEventTest, SplitPointerUpCancel) { + MotionEvent event = MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6)) + .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8)) + .addFlag(AMOTION_EVENT_FLAG_CANCELED) + .build(); + + MotionEvent splitUp; + std::bitset<MAX_POINTER_ID + 1> splitUpIds{}; + splitUpIds.set(6, true); + splitUp.splitFrom(event, splitUpIds, /*eventId=*/42); + ASSERT_EQ(splitUp.getAction(), AMOTION_EVENT_ACTION_CANCEL); + ASSERT_EQ(splitUp.getPointerCount(), 1u); + ASSERT_EQ(splitUp.getPointerId(0), 6); + ASSERT_EQ(splitUp.getX(0), 6); + ASSERT_EQ(splitUp.getY(0), 6); +} + +TEST_F(MotionEventTest, SplitPointerMove) { + MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6)) + .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8)) + .transform(ui::Transform(ui::Transform::ROT_90, 100, 100)) + .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50)) + .build(); + + MotionEvent splitMove; + std::bitset<MAX_POINTER_ID + 1> splitMoveIds{}; + splitMoveIds.set(4, true); + splitMoveIds.set(8, true); + splitMove.splitFrom(event, splitMoveIds, /*eventId=*/42); + ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE); + ASSERT_EQ(splitMove.getPointerCount(), 2u); + ASSERT_EQ(splitMove.getPointerId(0), 4); + ASSERT_EQ(splitMove.getX(0), event.getX(0)); + ASSERT_EQ(splitMove.getY(0), event.getY(0)); + ASSERT_EQ(splitMove.getRawX(0), event.getRawX(0)); + ASSERT_EQ(splitMove.getRawY(0), event.getRawY(0)); + ASSERT_EQ(splitMove.getPointerId(1), 8); + ASSERT_EQ(splitMove.getX(1), event.getX(2)); + ASSERT_EQ(splitMove.getY(1), event.getY(2)); + ASSERT_EQ(splitMove.getRawX(1), event.getRawX(2)); + ASSERT_EQ(splitMove.getRawY(1), event.getRawY(2)); +} + TEST_F(MotionEventTest, OffsetLocation) { MotionEvent event; initializeEventWithHistory(&event); + const float xOffset = event.getRawXOffset(); + const float yOffset = event.getRawYOffset(); event.offsetLocation(5.0f, -2.0f); - ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset()); - ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset()); + ASSERT_EQ(xOffset + 5.0f, event.getRawXOffset()); + ASSERT_EQ(yOffset - 2.0f, event.getRawYOffset()); } TEST_F(MotionEventTest, Scale) { MotionEvent event; initializeEventWithHistory(&event); const float unscaledOrientation = event.getOrientation(0); + const float unscaledXOffset = event.getRawXOffset(); + const float unscaledYOffset = event.getRawYOffset(); event.scale(2.0f); - ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); - ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); + ASSERT_EQ(unscaledXOffset * 2, event.getRawXOffset()); + ASSERT_EQ(unscaledYOffset * 2, event.getRawYOffset()); ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON); ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 06b841be0d..b5fab496e2 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "TestHelpers.h" - #include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> #include <gui/constants.h> @@ -40,11 +38,188 @@ struct Pointer { bool isResampled = false; }; +// A collection of arguments to be sent as publishMotionEvent(). The saved members of this struct +// allow to check the expectations against the event acquired from the InputReceiver. To help +// simplify expectation checking it carries members not present in MotionEvent, like |rawXScale|. +struct PublishMotionArgs { + const int32_t action; + const nsecs_t downTime; + const uint32_t seq; + const int32_t eventId; + const int32_t deviceId = 1; + const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; + const int32_t displayId = ADISPLAY_ID_DEFAULT; + const int32_t actionButton = 0; + const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; + const MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; + const float xScale = 2; + const float yScale = 3; + const float xOffset = -10; + const float yOffset = -20; + const float rawXScale = 4; + const float rawYScale = -5; + const float rawXOffset = -11; + const float rawYOffset = 42; + const float xPrecision = 0.25; + const float yPrecision = 0.5; + const float xCursorPosition = 1.3; + const float yCursorPosition = 50.6; + std::array<uint8_t, 32> hmac; + int32_t flags; + ui::Transform transform; + ui::Transform rawTransform; + const nsecs_t eventTime; + size_t pointerCount; + std::vector<PointerProperties> pointerProperties; + std::vector<PointerCoords> pointerCoords; + + PublishMotionArgs(int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers, + const uint32_t seq); +}; + +PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime, + const std::vector<Pointer>& pointers, const uint32_t inSeq) + : action(inAction), + downTime(inDownTime), + seq(inSeq), + eventId(InputEvent::nextId()), + eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) { + hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + if (action == AMOTION_EVENT_ACTION_CANCEL) { + flags |= AMOTION_EVENT_FLAG_CANCELED; + } + pointerCount = pointers.size(); + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties.push_back({}); + pointerProperties[i].clear(); + pointerProperties[i].id = pointers[i].id; + pointerProperties[i].toolType = ToolType::FINGER; + + pointerCoords.push_back({}); + pointerCoords[i].clear(); + pointerCoords[i].isResampled = pointers[i].isResampled; + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); + } + transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); + rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1}); +} + +// Checks expectations against |motionEvent| acquired from an InputConsumer. Floating point +// comparisons limit precision to EPSILON. +void verifyArgsEqualToEvent(const PublishMotionArgs& args, const MotionEvent& motionEvent) { + EXPECT_EQ(args.eventId, motionEvent.getId()); + EXPECT_EQ(args.deviceId, motionEvent.getDeviceId()); + EXPECT_EQ(args.source, motionEvent.getSource()); + EXPECT_EQ(args.displayId, motionEvent.getDisplayId()); + EXPECT_EQ(args.hmac, motionEvent.getHmac()); + EXPECT_EQ(args.action, motionEvent.getAction()); + EXPECT_EQ(args.downTime, motionEvent.getDownTime()); + EXPECT_EQ(args.flags, motionEvent.getFlags()); + EXPECT_EQ(args.edgeFlags, motionEvent.getEdgeFlags()); + EXPECT_EQ(args.metaState, motionEvent.getMetaState()); + EXPECT_EQ(args.buttonState, motionEvent.getButtonState()); + EXPECT_EQ(args.classification, motionEvent.getClassification()); + EXPECT_EQ(args.transform, motionEvent.getTransform()); + EXPECT_NEAR((-args.rawXOffset / args.rawXScale) * args.xScale + args.xOffset, + motionEvent.getRawXOffset(), EPSILON); + EXPECT_NEAR((-args.rawYOffset / args.rawYScale) * args.yScale + args.yOffset, + motionEvent.getRawYOffset(), EPSILON); + EXPECT_EQ(args.xPrecision, motionEvent.getXPrecision()); + EXPECT_EQ(args.yPrecision, motionEvent.getYPrecision()); + EXPECT_NEAR(args.xCursorPosition, motionEvent.getRawXCursorPosition(), EPSILON); + EXPECT_NEAR(args.yCursorPosition, motionEvent.getRawYCursorPosition(), EPSILON); + EXPECT_NEAR(args.xCursorPosition * args.xScale + args.xOffset, motionEvent.getXCursorPosition(), + EPSILON); + EXPECT_NEAR(args.yCursorPosition * args.yScale + args.yOffset, motionEvent.getYCursorPosition(), + EPSILON); + EXPECT_EQ(args.rawTransform, motionEvent.getRawTransform()); + EXPECT_EQ(args.eventTime, motionEvent.getEventTime()); + EXPECT_EQ(args.pointerCount, motionEvent.getPointerCount()); + EXPECT_EQ(0U, motionEvent.getHistorySize()); + + for (size_t i = 0; i < args.pointerCount; i++) { + SCOPED_TRACE(i); + EXPECT_EQ(args.pointerProperties[i].id, motionEvent.getPointerId(i)); + EXPECT_EQ(args.pointerProperties[i].toolType, motionEvent.getToolType(i)); + + const auto& pc = args.pointerCoords[i]; + EXPECT_EQ(pc, motionEvent.getSamplePointerCoords()[i]); + + EXPECT_NEAR(pc.getX() * args.rawXScale + args.rawXOffset, motionEvent.getRawX(i), EPSILON); + EXPECT_NEAR(pc.getY() * args.rawYScale + args.rawYOffset, motionEvent.getRawY(i), EPSILON); + EXPECT_NEAR(pc.getX() * args.xScale + args.xOffset, motionEvent.getX(i), EPSILON); + EXPECT_NEAR(pc.getY() * args.yScale + args.yOffset, motionEvent.getY(i), EPSILON); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent.getPressure(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent.getSize(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent.getTouchMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent.getTouchMinor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent.getToolMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent.getToolMinor(i)); + + // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is + // "up", and the positive y direction is "down". + const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + const float x = sinf(unscaledOrientation) * args.xScale; + const float y = -cosf(unscaledOrientation) * args.yScale; + EXPECT_EQ(atan2f(x, -y), motionEvent.getOrientation(i)); + } +} + +void publishMotionEvent(InputPublisher& publisher, const PublishMotionArgs& a) { + status_t status = + publisher.publishMotionEvent(a.seq, a.eventId, a.deviceId, a.source, a.displayId, + a.hmac, a.action, a.actionButton, a.flags, a.edgeFlags, + a.metaState, a.buttonState, a.classification, a.transform, + a.xPrecision, a.yPrecision, a.xCursorPosition, + a.yCursorPosition, a.rawTransform, a.downTime, a.eventTime, + a.pointerCount, a.pointerProperties.data(), + a.pointerCoords.data()); + ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; +} + +void sendAndVerifyFinishedSignal(InputConsumer& consumer, InputPublisher& publisher, uint32_t seq, + nsecs_t publishTime) { + status_t status = consumer.sendFinishedSignal(seq, false); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + Result<InputPublisher::ConsumerResponse> result = publisher.receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_FALSE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; +} + +void waitUntilInputAvailable(const InputConsumer& inputConsumer) { + bool hasInput; + do { + // The probablyHasInput() can return false positive under rare circumstances uncontrollable + // by the tests. Re-request the availability in this case. Returning |false| for a long + // time is not intended, and would cause a test timeout. + hasInput = inputConsumer.probablyHasInput(); + } while (!hasInput); +} + } // namespace class InputPublisherAndConsumerTest : public testing::Test { protected: - std::shared_ptr<InputChannel> mServerChannel, mClientChannel; std::unique_ptr<InputPublisher> mPublisher; std::unique_ptr<InputConsumer> mConsumer; PreallocatedInputEventFactory mEventFactory; @@ -54,15 +229,15 @@ protected: status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result); - mServerChannel = std::move(serverChannel); - mClientChannel = std::move(clientChannel); - mPublisher = std::make_unique<InputPublisher>(mServerChannel); - mConsumer = std::make_unique<InputConsumer>(mClientChannel); + mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel)); + mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel)); } void publishAndConsumeKeyEvent(); void publishAndConsumeMotionStream(); + void publishAndConsumeMotionDown(nsecs_t downTime); + void publishAndConsumeBatchedMotionMove(nsecs_t downTime); void publishAndConsumeFocusEvent(); void publishAndConsumeCaptureEvent(); void publishAndConsumeDragEvent(); @@ -73,24 +248,10 @@ protected: private: // The sequence number to use when publishing the next event uint32_t mSeq = 1; - - void publishAndConsumeMotionEvent( - int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, - int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - float xScale, float yScale, float xOffset, float yOffset, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, float rawXScale, - float rawYScale, float rawXOffset, float rawYOffset, nsecs_t downTime, - nsecs_t eventTime, const std::vector<PointerProperties>& pointerProperties, - const std::vector<PointerCoords>& pointerCoords); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { - ASSERT_NE(nullptr, mPublisher->getChannel()); - ASSERT_NE(nullptr, mConsumer->getChannel()); - EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get()); - EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get()); - ASSERT_EQ(mPublisher->getChannel()->getConnectionToken(), + ASSERT_EQ(mPublisher->getChannel().getConnectionToken(), mConsumer->getChannel()->getConnectionToken()); } @@ -121,11 +282,14 @@ void InputPublisherAndConsumerTest::publishAndConsumeKeyEvent() { ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + waitUntilInputAvailable(*mConsumer); uint32_t consumeSeq; InputEvent* event; status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; + EXPECT_FALSE(mConsumer->probablyHasInput()) + << "no events should be waiting after being consumed"; ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; @@ -185,176 +349,51 @@ void InputPublisherAndConsumerTest::publishAndConsumeMotionStream() { Pointer{.id = 2, .x = 300, .y = 400}}); } -void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent( - int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) { - constexpr int32_t deviceId = 1; - constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; - constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; - constexpr std::array<uint8_t, 32> hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; - constexpr int32_t actionButton = 0; - int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - - if (action == AMOTION_EVENT_ACTION_CANCEL) { - flags |= AMOTION_EVENT_FLAG_CANCELED; - } - const size_t pointerCount = pointers.size(); - constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; - constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; - constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; - constexpr float xScale = 2; - constexpr float yScale = 3; - constexpr float xOffset = -10; - constexpr float yOffset = -20; - constexpr float rawXScale = 4; - constexpr float rawYScale = -5; - constexpr float rawXOffset = -11; - constexpr float rawYOffset = 42; - constexpr float xPrecision = 0.25; - constexpr float yPrecision = 0.5; - constexpr float xCursorPosition = 1.3; - constexpr float yCursorPosition = 50.6; - - const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC); - std::vector<PointerProperties> pointerProperties; - std::vector<PointerCoords> pointerCoords; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties.push_back({}); - pointerProperties[i].clear(); - pointerProperties[i].id = pointers[i].id; - pointerProperties[i].toolType = ToolType::FINGER; +void InputPublisherAndConsumerTest::publishAndConsumeMotionDown(nsecs_t downTime) { + publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}}); +} - pointerCoords.push_back({}); - pointerCoords[i].clear(); - pointerCoords[i].isResampled = pointers[i].isResampled; - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); - } +void InputPublisherAndConsumerTest::publishAndConsumeBatchedMotionMove(nsecs_t downTime) { + uint32_t seq = mSeq++; + const std::vector<Pointer> pointers = {Pointer{.id = 0, .x = 20, .y = 30}}; + PublishMotionArgs args(AMOTION_EVENT_ACTION_MOVE, downTime, pointers, seq); + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + publishMotionEvent(*mPublisher, args); - publishAndConsumeMotionEvent(deviceId, source, displayId, hmac, action, actionButton, flags, - edgeFlags, metaState, buttonState, classification, xScale, yScale, - xOffset, yOffset, xPrecision, yPrecision, xCursorPosition, - yCursorPosition, rawXScale, rawYScale, rawXOffset, rawYOffset, - downTime, eventTime, pointerProperties, pointerCoords); + // Consume leaving a batch behind. + uint32_t consumeSeq; + InputEvent* event; + status_t status = mConsumer->consume(&mEventFactory, + /*consumeBatches=*/false, -1, &consumeSeq, &event); + ASSERT_EQ(WOULD_BLOCK, status) + << "consumer consume should return WOULD_BLOCK when a new batch is started"; + ASSERT_TRUE(mConsumer->hasPendingBatch()) << "consume should have created a batch"; + EXPECT_TRUE(mConsumer->probablyHasInput()) + << "should deterministically have input because there is a batch"; + sendAndVerifyFinishedSignal(*mConsumer, *mPublisher, seq, publishTime); } void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent( - int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, - int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xScale, float yScale, - float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, float rawXScale, float rawYScale, float rawXOffset, float rawYOffset, - nsecs_t downTime, nsecs_t eventTime, - const std::vector<PointerProperties>& pointerProperties, - const std::vector<PointerCoords>& pointerCoords) { - const uint32_t seq = mSeq++; - const int32_t eventId = InputEvent::nextId(); - ui::Transform transform; - transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); - ui::Transform rawTransform; - rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1}); - - status_t status; - ASSERT_EQ(pointerProperties.size(), pointerCoords.size()); - const size_t pointerCount = pointerProperties.size(); - const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); - status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, - actionButton, flags, edgeFlags, metaState, buttonState, - classification, transform, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, rawTransform, - downTime, eventTime, pointerCount, - pointerProperties.data(), pointerCoords.data()); - ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; + int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) { + uint32_t seq = mSeq++; + PublishMotionArgs args(action, downTime, pointers, seq); + nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + publishMotionEvent(*mPublisher, args); uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); - ASSERT_EQ(OK, status) - << "consumer consume should return OK"; - + status_t status = + mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(InputEventType::MOTION, event->getType()) << "consumer should have returned a motion event"; - - MotionEvent* motionEvent = static_cast<MotionEvent*>(event); EXPECT_EQ(seq, consumeSeq); - EXPECT_EQ(eventId, motionEvent->getId()); - EXPECT_EQ(deviceId, motionEvent->getDeviceId()); - EXPECT_EQ(source, motionEvent->getSource()); - EXPECT_EQ(displayId, motionEvent->getDisplayId()); - EXPECT_EQ(hmac, motionEvent->getHmac()); - EXPECT_EQ(action, motionEvent->getAction()); - EXPECT_EQ(flags, motionEvent->getFlags()); - EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); - EXPECT_EQ(metaState, motionEvent->getMetaState()); - EXPECT_EQ(buttonState, motionEvent->getButtonState()); - EXPECT_EQ(classification, motionEvent->getClassification()); - EXPECT_EQ(transform, motionEvent->getTransform()); - EXPECT_EQ(xOffset, motionEvent->getXOffset()); - EXPECT_EQ(yOffset, motionEvent->getYOffset()); - EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); - EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); - EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON); - EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON); - EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON); - EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON); - EXPECT_EQ(rawTransform, motionEvent->getRawTransform()); - EXPECT_EQ(downTime, motionEvent->getDownTime()); - EXPECT_EQ(eventTime, motionEvent->getEventTime()); - EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); - EXPECT_EQ(0U, motionEvent->getHistorySize()); - - for (size_t i = 0; i < pointerCount; i++) { - SCOPED_TRACE(i); - EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); - EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); - - const auto& pc = pointerCoords[i]; - EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]); - - EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON); - EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON); - EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON); - EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i)); - - // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is - // "up", and the positive y direction is "down". - const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - const float x = sinf(unscaledOrientation) * xScale; - const float y = -cosf(unscaledOrientation) * yScale; - EXPECT_EQ(atan2f(x, -y), motionEvent->getOrientation(i)); - } - status = mConsumer->sendFinishedSignal(seq, false); - ASSERT_EQ(OK, status) - << "consumer sendFinishedSignal should return OK"; - - Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); - ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; - ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); - const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); - ASSERT_EQ(seq, finish.seq) - << "receiveConsumerResponse should have returned the original sequence number"; - ASSERT_FALSE(finish.handled) - << "receiveConsumerResponse should have set handled to consumer's reply"; - ASSERT_GE(finish.consumeTime, publishTime) - << "finished signal's consume time should be greater than publish time"; + verifyArgsEqualToEvent(args, static_cast<const MotionEvent&>(*event)); + sendAndVerifyFinishedSignal(*mConsumer, *mPublisher, seq, publishTime); } void InputPublisherAndConsumerTest::publishAndConsumeFocusEvent() { @@ -546,6 +585,15 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionStream()); } +TEST_F(InputPublisherAndConsumerTest, PublishMotionMoveEvent_EndToEnd) { + // Publish a DOWN event before MOVE to pass the InputVerifier checks. + const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionDown(downTime)); + + // Publish the MOVE event and check expectations. + ASSERT_NO_FATAL_FAILURE(publishAndConsumeBatchedMotionMove(downTime)); +} + TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent()); } diff --git a/libs/input/tests/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp index 31cc1459fc..cc41eeb5e7 100644 --- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp +++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp @@ -238,14 +238,17 @@ TEST(MakeMotionEventTest, MakeLiftMotionEvent) { // --- Ground-truth-generation helper functions. --- +// Generates numPoints ground truth points with values equal to those of the given +// GroundTruthPoint, and with consecutive timestamps separated by the given inputInterval. std::vector<GroundTruthPoint> generateConstantGroundTruthPoints( - const GroundTruthPoint& groundTruthPoint, size_t numPoints) { + const GroundTruthPoint& groundTruthPoint, size_t numPoints, + nsecs_t inputInterval = TEST_PREDICTION_INTERVAL_NANOS) { std::vector<GroundTruthPoint> groundTruthPoints; nsecs_t timestamp = groundTruthPoint.timestamp; for (size_t i = 0; i < numPoints; ++i) { groundTruthPoints.emplace_back(groundTruthPoint); groundTruthPoints.back().timestamp = timestamp; - timestamp += TEST_PREDICTION_INTERVAL_NANOS; + timestamp += inputInterval; } return groundTruthPoints; } @@ -280,7 +283,8 @@ TEST(GenerateConstantGroundTruthPointsTest, BasicTest) { const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f}, .timestamp = TEST_INITIAL_TIMESTAMP}; const std::vector<GroundTruthPoint> groundTruthPoints = - generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3); + generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3, + /*inputInterval=*/10); ASSERT_EQ(3u, groundTruthPoints.size()); // First point. @@ -290,11 +294,11 @@ TEST(GenerateConstantGroundTruthPointsTest, BasicTest) { // Second point. EXPECT_EQ(groundTruthPoints[1].position, groundTruthPoint.position); EXPECT_EQ(groundTruthPoints[1].pressure, groundTruthPoint.pressure); - EXPECT_GT(groundTruthPoints[1].timestamp, groundTruthPoints[0].timestamp); + EXPECT_EQ(groundTruthPoints[1].timestamp, groundTruthPoint.timestamp + 10); // Third point. EXPECT_EQ(groundTruthPoints[2].position, groundTruthPoint.position); EXPECT_EQ(groundTruthPoints[2].pressure, groundTruthPoint.pressure); - EXPECT_GT(groundTruthPoints[2].timestamp, groundTruthPoints[1].timestamp); + EXPECT_EQ(groundTruthPoints[2].timestamp, groundTruthPoint.timestamp + 20); } TEST(GenerateCircularArcGroundTruthTest, StraightLineUpwards) { @@ -333,16 +337,19 @@ TEST(GenerateCircularArcGroundTruthTest, CounterclockwiseSquare) { // --- Prediction-generation helper functions. --- -// Creates a sequence of predictions with values equal to those of the given GroundTruthPoint. -std::vector<PredictionPoint> generateConstantPredictions(const GroundTruthPoint& groundTruthPoint) { +// Generates TEST_MAX_NUM_PREDICTIONS predictions with values equal to those of the given +// GroundTruthPoint, and with consecutive timestamps separated by the given predictionInterval. +std::vector<PredictionPoint> generateConstantPredictions( + const GroundTruthPoint& groundTruthPoint, + nsecs_t predictionInterval = TEST_PREDICTION_INTERVAL_NANOS) { std::vector<PredictionPoint> predictions; - nsecs_t predictionTimestamp = groundTruthPoint.timestamp + TEST_PREDICTION_INTERVAL_NANOS; + nsecs_t predictionTimestamp = groundTruthPoint.timestamp + predictionInterval; for (size_t j = 0; j < TEST_MAX_NUM_PREDICTIONS; ++j) { predictions.push_back(PredictionPoint{{.position = groundTruthPoint.position, .pressure = groundTruthPoint.pressure}, .originTimestamp = groundTruthPoint.timestamp, .targetTimestamp = predictionTimestamp}); - predictionTimestamp += TEST_PREDICTION_INTERVAL_NANOS; + predictionTimestamp += predictionInterval; } return predictions; } @@ -375,8 +382,9 @@ std::vector<PredictionPoint> generatePredictionsByLinearExtrapolation( TEST(GeneratePredictionsTest, GenerateConstantPredictions) { const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f}, .timestamp = TEST_INITIAL_TIMESTAMP}; + const nsecs_t predictionInterval = 10; const std::vector<PredictionPoint> predictionPoints = - generateConstantPredictions(groundTruthPoint); + generateConstantPredictions(groundTruthPoint, predictionInterval); ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, predictionPoints.size()); for (size_t i = 0; i < predictionPoints.size(); ++i) { @@ -385,8 +393,7 @@ TEST(GeneratePredictionsTest, GenerateConstantPredictions) { EXPECT_THAT(predictionPoints[i].pressure, FloatNear(groundTruthPoint.pressure, 1e-6)); EXPECT_EQ(predictionPoints[i].originTimestamp, groundTruthPoint.timestamp); EXPECT_EQ(predictionPoints[i].targetTimestamp, - groundTruthPoint.timestamp + - static_cast<nsecs_t>(i + 1) * TEST_PREDICTION_INTERVAL_NANOS); + TEST_INITIAL_TIMESTAMP + static_cast<nsecs_t>(i + 1) * predictionInterval); } } @@ -678,12 +685,9 @@ ReportAtomFunction createMockReportAtomFunction(std::vector<AtomFields>& reporte // • groundTruthPoints: chronologically-ordered ground truth points, with at least 2 elements. // • predictionPoints: the first index points to a vector of predictions corresponding to the // source ground truth point with the same index. -// - The first element should be empty, because there are not expected to be predictions until -// we have received 2 ground truth points. -// - The last element may be empty, because there will be no future ground truth points to -// associate with those predictions (if not empty, it will be ignored). +// - For empty prediction vectors, MetricsManager::onPredict will not be called. // - To test all prediction buckets, there should be at least TEST_MAX_NUM_PREDICTIONS non-empty -// prediction sets (that is, excluding the first and last). Thus, groundTruthPoints and +// prediction vectors (that is, excluding the first and last). Thus, groundTruthPoints and // predictionPoints should have size at least TEST_MAX_NUM_PREDICTIONS + 2. // // When the function returns, outReportedAtomFields will contain the reported AtomFields. @@ -697,19 +701,12 @@ void runMetricsManager(const std::vector<GroundTruthPoint>& groundTruthPoints, createMockReportAtomFunction( outReportedAtomFields)); - // Validate structure of groundTruthPoints and predictionPoints. - ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size()); ASSERT_GE(groundTruthPoints.size(), 2u); - ASSERT_EQ(predictionPoints[0].size(), 0u); - for (size_t i = 1; i + 1 < predictionPoints.size(); ++i) { - SCOPED_TRACE(testing::Message() << "i = " << i); - ASSERT_EQ(predictionPoints[i].size(), TEST_MAX_NUM_PREDICTIONS); - } + ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size()); - // Pass ground truth points and predictions (for all except first and last ground truth). for (size_t i = 0; i < groundTruthPoints.size(); ++i) { metricsManager.onRecord(makeMotionEvent(groundTruthPoints[i])); - if ((i > 0) && (i + 1 < predictionPoints.size())) { + if (!predictionPoints[i].empty()) { metricsManager.onPredict(makeMotionEvent(predictionPoints[i])); } } @@ -738,7 +735,7 @@ TEST(MotionPredictorMetricsManagerTest, NoPredictions) { // Perfect predictions test: // • Input: constant input events, perfect predictions matching the input events. // • Expectation: all error metrics should be zero, or NO_DATA_SENTINEL for "unreported" metrics. -// (For example, scale-invariant errors are only reported for the final time bucket.) +// (For example, scale-invariant errors are only reported for the last time bucket.) TEST(MotionPredictorMetricsManagerTest, ConstantGroundTruthPerfectPredictions) { GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10.0f, 20.0f), .pressure = 0.6f}, .timestamp = TEST_INITIAL_TIMESTAMP}; @@ -977,5 +974,35 @@ TEST(MotionPredictorMetricsManagerTest, CounterclockwiseOctagonGroundTruthLinear } } +// Robustness test: +// • Input: input events separated by a significantly greater time interval than the interval +// between predictions. +// • Expectation: the MetricsManager should not crash in this case. (No assertions are made about +// the resulting metrics.) +// +// In practice, this scenario could arise either if the input and prediction intervals are +// mismatched, or if input events are missing (dropped or skipped for some reason). +TEST(MotionPredictorMetricsManagerTest, MismatchedInputAndPredictionInterval) { + // Create two ground truth points separated by MAX_NUM_PREDICTIONS * PREDICTION_INTERVAL, + // so that the second ground truth point corresponds to the last prediction bucket. This + // ensures that the scale-invariant error codepath will be run, giving full code coverage. + GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(0.0f, 0.0f), .pressure = 0.5f}, + .timestamp = TEST_INITIAL_TIMESTAMP}; + const nsecs_t inputInterval = TEST_MAX_NUM_PREDICTIONS * TEST_PREDICTION_INTERVAL_NANOS; + const std::vector<GroundTruthPoint> groundTruthPoints = + generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/2, inputInterval); + + // Create predictions separated by the prediction interval. + std::vector<std::vector<PredictionPoint>> predictionPoints; + for (size_t i = 0; i < groundTruthPoints.size(); ++i) { + predictionPoints.push_back( + generateConstantPredictions(groundTruthPoints[i], TEST_PREDICTION_INTERVAL_NANOS)); + } + + // Test that we can run the MetricsManager without crashing. + std::vector<AtomFields> reportedAtomFields; + runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields); +} + } // namespace } // namespace android diff --git a/libs/input/tests/TestHelpers.h b/libs/input/tests/TestHelpers.h deleted file mode 100644 index 343d81f917..0000000000 --- a/libs/input/tests/TestHelpers.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 TESTHELPERS_H -#define TESTHELPERS_H - -#include <unistd.h> - -#include <utils/threads.h> - -namespace android { - -class Pipe { -public: - int sendFd; - int receiveFd; - - Pipe() { - int fds[2]; - ::pipe(fds); - - receiveFd = fds[0]; - sendFd = fds[1]; - } - - ~Pipe() { - if (sendFd != -1) { - ::close(sendFd); - } - - if (receiveFd != -1) { - ::close(receiveFd); - } - } - - status_t writeSignal() { - ssize_t nWritten = ::write(sendFd, "*", 1); - return nWritten == 1 ? 0 : -errno; - } - - status_t readSignal() { - char buf[1]; - ssize_t nRead = ::read(receiveFd, buf, 1); - return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno; - } -}; - -class DelayedTask : public Thread { - int mDelayMillis; - -public: - explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { } - -protected: - virtual ~DelayedTask() { } - - virtual void doTask() = 0; - - virtual bool threadLoop() { - usleep(mDelayMillis * 1000); - doTask(); - return false; - } -}; - -} // namespace android - -#endif // TESTHELPERS_H diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp index 1cb7f7ba8c..0b0bb63a81 100644 --- a/libs/input/tests/TouchResampling_test.cpp +++ b/libs/input/tests/TouchResampling_test.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "TestHelpers.h" - #include <chrono> #include <vector> diff --git a/libs/input/tests/VelocityControl_test.cpp b/libs/input/tests/VelocityControl_test.cpp new file mode 100644 index 0000000000..63d64c6048 --- /dev/null +++ b/libs/input/tests/VelocityControl_test.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 2024 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 <input/VelocityControl.h> + +#include <limits> + +#include <gtest/gtest.h> +#include <input/AccelerationCurve.h> +#include <utils/Timers.h> + +namespace android { + +namespace { + +constexpr float EPSILON = 0.001; +constexpr float COUNTS_PER_MM = 800 / 25.4; + +} // namespace + +class CurvedVelocityControlTest : public testing::Test { +protected: + CurvedVelocityControl mCtrl; + + void moveWithoutCheckingResult(nsecs_t eventTime, float deltaX, float deltaY) { + mCtrl.move(eventTime, &deltaX, &deltaY); + } + + void moveAndCheckRatio(nsecs_t eventTime, const float deltaX, const float deltaY, + float expectedRatio) { + float newDeltaX = deltaX, newDeltaY = deltaY; + mCtrl.move(eventTime, &newDeltaX, &newDeltaY); + ASSERT_NEAR(expectedRatio * deltaX, newDeltaX, EPSILON) + << "Expected ratio of " << expectedRatio << " in X, but actual ratio was " + << newDeltaX / deltaX; + ASSERT_NEAR(expectedRatio * deltaY, newDeltaY, EPSILON) + << "Expected ratio of " << expectedRatio << " in Y, but actual ratio was " + << newDeltaY / deltaY; + } +}; + +TEST_F(CurvedVelocityControlTest, SegmentSelection) { + // To make the maths simple, use a "curve" that's actually just a sequence of steps. + mCtrl.setCurve({ + {10, 2, 0}, + {20, 3, 0}, + {30, 4, 0}, + {std::numeric_limits<double>::infinity(), 5, 0}, + }); + + // Establish a velocity of 16 mm/s. + moveWithoutCheckingResult(0, 0, 0); + moveWithoutCheckingResult(10'000'000, 0.16 * COUNTS_PER_MM, 0); + moveWithoutCheckingResult(20'000'000, 0.16 * COUNTS_PER_MM, 0); + moveWithoutCheckingResult(30'000'000, 0.16 * COUNTS_PER_MM, 0); + ASSERT_NO_FATAL_FAILURE( + moveAndCheckRatio(40'000'000, 0.16 * COUNTS_PER_MM, 0, /*expectedRatio=*/3)); + + // Establish a velocity of 50 mm/s. + mCtrl.reset(); + moveWithoutCheckingResult(100'000'000, 0, 0); + moveWithoutCheckingResult(110'000'000, 0.50 * COUNTS_PER_MM, 0); + moveWithoutCheckingResult(120'000'000, 0.50 * COUNTS_PER_MM, 0); + moveWithoutCheckingResult(130'000'000, 0.50 * COUNTS_PER_MM, 0); + ASSERT_NO_FATAL_FAILURE( + moveAndCheckRatio(140'000'000, 0.50 * COUNTS_PER_MM, 0, /*expectedRatio=*/5)); +} + +TEST_F(CurvedVelocityControlTest, RatioDefaultsToFirstSegmentWhenVelocityIsUnknown) { + mCtrl.setCurve({ + {10, 3, 0}, + {20, 2, 0}, + {std::numeric_limits<double>::infinity(), 4, 0}, + }); + + // Only send two moves, which won't be enough for VelocityTracker to calculate a velocity from. + moveWithoutCheckingResult(0, 0, 0); + ASSERT_NO_FATAL_FAILURE( + moveAndCheckRatio(10'000'000, 0.25 * COUNTS_PER_MM, 0, /*expectedRatio=*/3)); +} + +TEST_F(CurvedVelocityControlTest, VelocityCalculatedUsingBothAxes) { + mCtrl.setCurve({ + {8.0, 3, 0}, + {8.1, 2, 0}, + {std::numeric_limits<double>::infinity(), 4, 0}, + }); + + // Establish a velocity of 8.06 (= √65 = √(7²+4²)) mm/s between the two axes. + moveWithoutCheckingResult(0, 0, 0); + moveWithoutCheckingResult(10'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM); + moveWithoutCheckingResult(20'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM); + moveWithoutCheckingResult(30'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM); + ASSERT_NO_FATAL_FAILURE(moveAndCheckRatio(40'000'000, 0.07 * COUNTS_PER_MM, + 0.04 * COUNTS_PER_MM, + /*expectedRatio=*/2)); +} + +TEST_F(CurvedVelocityControlTest, ReciprocalTerm) { + mCtrl.setCurve({ + {10, 2, 0}, + {20, 3, -10}, + {std::numeric_limits<double>::infinity(), 3, 0}, + }); + + // Establish a velocity of 15 mm/s. + moveWithoutCheckingResult(0, 0, 0); + moveWithoutCheckingResult(10'000'000, 0, 0.15 * COUNTS_PER_MM); + moveWithoutCheckingResult(20'000'000, 0, 0.15 * COUNTS_PER_MM); + moveWithoutCheckingResult(30'000'000, 0, 0.15 * COUNTS_PER_MM); + // Expected ratio is 3 - 10 / 15 = 2.33333... + ASSERT_NO_FATAL_FAILURE( + moveAndCheckRatio(40'000'000, 0, 0.15 * COUNTS_PER_MM, /*expectedRatio=*/2.33333)); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 8f005a56f8..bed31e27a8 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -148,29 +148,31 @@ AChoreographer* AChoreographer_getInstance() { void AChoreographer_postFrameCallback(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data) { AChoreographer_to_Choreographer(choreographer) - ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0); + ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data, long delayMillis) { AChoreographer_to_Choreographer(choreographer) - ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis)); + ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis), + CALLBACK_ANIMATION); } void AChoreographer_postVsyncCallback(AChoreographer* choreographer, AChoreographer_vsyncCallback callback, void* data) { AChoreographer_to_Choreographer(choreographer) - ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0); + ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallback64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data) { AChoreographer_to_Choreographer(choreographer) - ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0); + ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0, CALLBACK_ANIMATION); } void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) { AChoreographer_to_Choreographer(choreographer) - ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis)); + ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis), + CALLBACK_ANIMATION); } void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, AChoreographer_refreshRateCallback callback, diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index 342f5de337..03f4f399d0 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -16,6 +16,7 @@ package { default_applicable_licenses: [ "frameworks_native_libs_nativedisplay_license", ], + default_team: "trendy_team_android_core_graphics_stack", } // Added automatically by a large-scale-change @@ -33,7 +34,13 @@ license { cc_library_headers { name: "libnativedisplay_headers", + host_supported: true, export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + }, + }, } cc_library_shared { diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index e7b2195056..52612870ab 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -113,6 +113,22 @@ static_assert( AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM, "HAL and AHardwareBuffer pixel format don't match"); +static enum AHardwareBufferStatus filterStatus(status_t status) { + switch (status) { + case STATUS_OK: + return AHARDWAREBUFFER_STATUS_OK; + case STATUS_NO_MEMORY: + return AHARDWAREBUFFER_STATUS_NO_MEMORY; + case STATUS_BAD_VALUE: + return AHARDWAREBUFFER_STATUS_BAD_VALUE; + case STATUS_UNKNOWN_TRANSACTION: + case STATUS_INVALID_OPERATION: + return AHARDWAREBUFFER_STATUS_UNSUPPORTED; + default: + return AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR; + } +} + // ---------------------------------------------------------------------------- // Public functions // ---------------------------------------------------------------------------- @@ -511,6 +527,24 @@ binder_status_t AHardwareBuffer_writeToParcel(const AHardwareBuffer* _Nonnull bu return AParcel_viewPlatformParcel(parcel)->write(*gb); } +ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) { + const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + if (!gb) return ADATASPACE_UNKNOWN; + ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; + status_t status = gb->getDataspace(&dataspace); + if (status != OK) { + return ADATASPACE_UNKNOWN; + } + return static_cast<ADataSpace>(dataspace); +} + +enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* buffer, + ADataSpace dataspace) { + GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); + auto& mapper = GraphicBufferMapper::get(); + return filterStatus(mapper.setDataspace(gb->handle, static_cast<ui::Dataspace>(dataspace))); +} + // ---------------------------------------------------------------------------- // VNDK functions // ---------------------------------------------------------------------------- @@ -552,6 +586,56 @@ int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, return NO_ERROR; } +enum AHardwareBufferStatus AHardwareBuffer_allocateWithOptions( + const AHardwareBuffer_Desc* desc, const AHardwareBufferLongOptions* additionalOptions, + size_t additionalOptionsSize, AHardwareBuffer** outBuffer) { + (void)additionalOptions; + (void)additionalOptionsSize; + if (!outBuffer || !desc) return AHARDWAREBUFFER_STATUS_BAD_VALUE; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) { + return AHARDWAREBUFFER_STATUS_BAD_VALUE; + } + + int format = AHardwareBuffer_convertToPixelFormat(desc->format); + uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); + + std::vector<GraphicBufferAllocator::AdditionalOptions> extras; + extras.reserve(additionalOptionsSize); + for (size_t i = 0; i < additionalOptionsSize; i++) { + extras.push_back(GraphicBufferAllocator::AdditionalOptions{additionalOptions[i].name, + additionalOptions[i].value}); + } + + const auto extrasCount = extras.size(); + auto gbuffer = sp<GraphicBuffer>::make(GraphicBufferAllocator::AllocationRequest{ + .importBuffer = true, + .width = desc->width, + .height = desc->height, + .format = format, + .layerCount = desc->layers, + .usage = usage, + .requestorName = std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]", + .extras = std::move(extras), + }); + + status_t err = gbuffer->initCheck(); + if (err != 0 || gbuffer->handle == nullptr) { + if (err == NO_MEMORY) { + GraphicBuffer::dumpAllocationsToSystemLog(); + } + ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u, extrasCount=%zd) failed (%s), handle=%p", + desc->width, desc->height, desc->layers, extrasCount, strerror(-err), + gbuffer->handle); + return filterStatus(err == 0 ? UNKNOWN_ERROR : err); + } + + *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); + + // Ensure the buffer doesn't get destroyed when the sp<> goes away. + AHardwareBuffer_acquire(*outBuffer); + return AHARDWAREBUFFER_STATUS_OK; +} + // ---------------------------------------------------------------------------- // Helpers implementation // ---------------------------------------------------------------------------- @@ -652,12 +736,9 @@ uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) { return ahardwarebuffer_format; } +// TODO: Remove, this is just to make an overly aggressive ABI checker happy int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) { - GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer); - auto& mapper = GraphicBufferMapper::get(); - ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; - mapper.getDataspace(gb->handle, &dataspace); - return static_cast<int32_t>(dataspace); + return ::AHardwareBuffer_getDataSpace(buffer); } uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index dd5958de28..f97eed5db3 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -152,31 +152,56 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) { static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN)); - static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); - static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); - static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); - static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); - static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); - static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); - static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); - static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); - static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); - static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); - static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); - static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); - static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); - static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); - static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); - static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); - static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); - static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); - static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK)); - static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); - static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL)); - static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); - static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_MASK) == + static_cast<int>(HAL_DATASPACE_STANDARD_MASK)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT709) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT709)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625_UNADJUSTED) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525_UNADJUSTED) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_BT470M) == + static_cast<int>(HAL_DATASPACE_STANDARD_BT470M)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_FILM) == + static_cast<int>(HAL_DATASPACE_STANDARD_FILM)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_DCI_P3) == + static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3)); + static_assert(static_cast<int>(ADATASPACE_STANDARD_ADOBE_RGB) == + static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_MASK) == + static_cast<int>(HAL_DATASPACE_TRANSFER_MASK)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_LINEAR) == + static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_SMPTE_170M) == + static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_2) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_6) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_8) == + static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_ST2084) == + static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084)); + static_assert(static_cast<int>(ADATASPACE_TRANSFER_HLG) == + static_cast<int>(HAL_DATASPACE_TRANSFER_HLG)); + static_assert(static_cast<int>(ADATASPACE_RANGE_MASK) == + static_cast<int>(HAL_DATASPACE_RANGE_MASK)); + static_assert(static_cast<int>(ADATASPACE_RANGE_UNSPECIFIED) == + static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED)); + static_assert(static_cast<int>(ADATASPACE_RANGE_FULL) == + static_cast<int>(HAL_DATASPACE_RANGE_FULL)); + static_assert(static_cast<int>(ADATASPACE_RANGE_LIMITED) == + static_cast<int>(HAL_DATASPACE_RANGE_LIMITED)); + static_assert(static_cast<int>(ADATASPACE_RANGE_EXTENDED) == + static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED)); static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB)); static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB)); static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index bc0bfc52d5..855807472e 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -16,6 +16,7 @@ package { default_applicable_licenses: [ "frameworks_native_libs_nativewindow_license", ], + default_team: "trendy_team_android_core_graphics_stack", } // Added automatically by a large-scale-change @@ -53,6 +54,11 @@ cc_library_headers { "test_com.android.media.swcodec", ], host_supported: true, + target: { + windows: { + enabled: true, + }, + }, } ndk_library { diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h index 880c694934..f145a2f7c2 100644 --- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h +++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h @@ -27,8 +27,8 @@ #include <stdint.h> -struct AHardwareBuffer; -struct AHardwareBuffer_Desc; +#include <vndk/hardware_buffer.h> + struct ANativeWindowBuffer; namespace android { @@ -46,11 +46,6 @@ 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); -// retrieves a dataspace from the AHardwareBuffer metadata, if the device -// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not -// supported. -int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer); - // convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op) uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage); diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 9f8ae863db..8056d9ac4f 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -64,7 +64,7 @@ enum ADataSpace : int32_t { * Defines the chromaticity coordinates of the source primaries in terms of * the CIE 1931 definition of x and y specified in ISO 11664-1. */ - STANDARD_MASK = 63 << 16, + ADATASPACE_STANDARD_MASK = 63 << 16, /** * Chromacity coordinates are unknown or are determined by the application. @@ -79,7 +79,7 @@ enum ADataSpace : int32_t { * For all other formats standard is undefined, and implementations should use * an appropriate standard for the data represented. */ - STANDARD_UNSPECIFIED = 0 << 16, + ADATASPACE_STANDARD_UNSPECIFIED = 0 << 16, /** * <pre> @@ -92,7 +92,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation * for RGB conversion. */ - STANDARD_BT709 = 1 << 16, + ADATASPACE_STANDARD_BT709 = 1 << 16, /** * <pre> @@ -107,7 +107,7 @@ enum ADataSpace : int32_t { * to minimize the color shift into RGB space that uses BT.709 * primaries. */ - STANDARD_BT601_625 = 2 << 16, + ADATASPACE_STANDARD_BT601_625 = 2 << 16, /** * <pre> @@ -120,7 +120,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation * for RGB conversion. */ - STANDARD_BT601_625_UNADJUSTED = 3 << 16, + ADATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << 16, /** * <pre> @@ -135,7 +135,7 @@ enum ADataSpace : int32_t { * to minimize the color shift into RGB space that uses BT.709 * primaries. */ - STANDARD_BT601_525 = 4 << 16, + ADATASPACE_STANDARD_BT601_525 = 4 << 16, /** * <pre> @@ -148,7 +148,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation * for RGB conversion (as in SMPTE 240M). */ - STANDARD_BT601_525_UNADJUSTED = 5 << 16, + ADATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << 16, /** * <pre> @@ -161,7 +161,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation * for RGB conversion. */ - STANDARD_BT2020 = 6 << 16, + ADATASPACE_STANDARD_BT2020 = 6 << 16, /** * <pre> @@ -174,7 +174,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation * for RGB conversion using the linear domain. */ - STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, + ADATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16, /** * <pre> @@ -187,7 +187,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation * for RGB conversion. */ - STANDARD_BT470M = 8 << 16, + ADATASPACE_STANDARD_BT470M = 8 << 16, /** * <pre> @@ -200,7 +200,7 @@ enum ADataSpace : int32_t { * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation * for RGB conversion. */ - STANDARD_FILM = 9 << 16, + ADATASPACE_STANDARD_FILM = 9 << 16, /** * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3) @@ -211,7 +211,7 @@ enum ADataSpace : int32_t { * red 0.680 0.320 * white (D65) 0.3127 0.3290</pre> */ - STANDARD_DCI_P3 = 10 << 16, + ADATASPACE_STANDARD_DCI_P3 = 10 << 16, /** * Adobe RGB @@ -222,7 +222,7 @@ enum ADataSpace : int32_t { * red 0.640 0.330 * white (D65) 0.3127 0.3290</pre> */ - STANDARD_ADOBE_RGB = 11 << 16, + ADATASPACE_STANDARD_ADOBE_RGB = 11 << 16, /** * Transfer aspect @@ -237,7 +237,7 @@ enum ADataSpace : int32_t { * component. Implementation may apply the transfer function in RGB space * for all pixel formats if desired. */ - TRANSFER_MASK = 31 << 22, + ADATASPACE_TRANSFER_MASK = 31 << 22, /** * Transfer characteristics are unknown or are determined by the @@ -245,13 +245,13 @@ enum ADataSpace : int32_t { * * Implementations should use the following transfer functions: * - * For YCbCr formats: use TRANSFER_SMPTE_170M - * For RGB formats: use TRANSFER_SRGB + * For YCbCr formats: use ADATASPACE_TRANSFER_SMPTE_170M + * For RGB formats: use ADATASPACE_TRANSFER_SRGB * * For all other formats transfer function is undefined, and implementations * should use an appropriate standard for the data represented. */ - TRANSFER_UNSPECIFIED = 0 << 22, + ADATASPACE_TRANSFER_UNSPECIFIED = 0 << 22, /** * Linear transfer. @@ -261,7 +261,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_LINEAR = 1 << 22, + ADATASPACE_TRANSFER_LINEAR = 1 << 22, /** * sRGB transfer. @@ -272,7 +272,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_SRGB = 2 << 22, + ADATASPACE_TRANSFER_SRGB = 2 << 22, /** * SMPTE 170M transfer. @@ -283,7 +283,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_SMPTE_170M = 3 << 22, + ADATASPACE_TRANSFER_SMPTE_170M = 3 << 22, /** * Display gamma 2.2. @@ -293,7 +293,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_2 = 4 << 22, + ADATASPACE_TRANSFER_GAMMA2_2 = 4 << 22, /** * Display gamma 2.6. @@ -303,7 +303,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_6 = 5 << 22, + ADATASPACE_TRANSFER_GAMMA2_6 = 5 << 22, /** * Display gamma 2.8. @@ -313,7 +313,7 @@ enum ADataSpace : int32_t { * L - luminance of image 0 <= L <= 1 for conventional colorimetry * E - corresponding electrical signal</pre> */ - TRANSFER_GAMMA2_8 = 6 << 22, + ADATASPACE_TRANSFER_GAMMA2_8 = 6 << 22, /** * SMPTE ST 2084 (Dolby Perceptual Quantizer). @@ -329,7 +329,7 @@ enum ADataSpace : int32_t { * L = 1 corresponds to 10000 cd/m2 * E - corresponding electrical signal</pre> */ - TRANSFER_ST2084 = 7 << 22, + ADATASPACE_TRANSFER_ST2084 = 7 << 22, /** * ARIB STD-B67 Hybrid Log Gamma. @@ -345,7 +345,7 @@ enum ADataSpace : int32_t { * to reference white level of 100 cd/m2 * E - corresponding electrical signal</pre> */ - TRANSFER_HLG = 8 << 22, + ADATASPACE_TRANSFER_HLG = 8 << 22, /** * Range aspect @@ -353,7 +353,7 @@ enum ADataSpace : int32_t { * Defines the range of values corresponding to the unit range of 0-1. * This is defined for YCbCr only, but can be expanded to RGB space. */ - RANGE_MASK = 7 << 27, + ADATASPACE_RANGE_MASK = 7 << 27, /** * Range is unknown or are determined by the application. Implementations @@ -366,13 +366,13 @@ enum ADataSpace : int32_t { * For all other formats range is undefined, and implementations should use * an appropriate range for the data represented. */ - RANGE_UNSPECIFIED = 0 << 27, + ADATASPACE_RANGE_UNSPECIFIED = 0 << 27, /** * Full range uses all values for Y, Cb and Cr from * 0 to 2^b-1, where b is the bit depth of the color format. */ - RANGE_FULL = 1 << 27, + ADATASPACE_RANGE_FULL = 1 << 27, /** * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and @@ -387,7 +387,7 @@ enum ADataSpace : int32_t { * Luma (Y) samples should range from 64 to 940, inclusive * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive */ - RANGE_LIMITED = 2 << 27, + ADATASPACE_RANGE_LIMITED = 2 << 27, /** * Extended range is used for scRGB. Intended for use with @@ -396,7 +396,7 @@ enum ADataSpace : int32_t { * color outside the sRGB gamut. * Used to blend / merge multiple dataspaces on a single display. */ - RANGE_EXTENDED = 3 << 27, + ADATASPACE_RANGE_EXTENDED = 3 << 27, /** * scRGB linear encoding @@ -411,7 +411,8 @@ enum ADataSpace : int32_t { * * Uses extended range, linear transfer and BT.709 standard. */ - ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED + ADATASPACE_SCRGB_LINEAR = 406913024, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR | + // ADATASPACE_RANGE_EXTENDED /** * sRGB gamma encoding @@ -426,7 +427,8 @@ enum ADataSpace : int32_t { * * Uses full range, sRGB transfer BT.709 standard. */ - ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL + ADATASPACE_SRGB = 142671872, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_FULL /** * scRGB @@ -441,14 +443,16 @@ enum ADataSpace : int32_t { * * Uses extended range, sRGB transfer and BT.709 standard. */ - ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED + ADATASPACE_SCRGB = 411107328, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_EXTENDED /** * Display P3 * * Uses full range, sRGB transfer and D65 DCI-P3 standard. */ - ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL + ADATASPACE_DISPLAY_P3 = 143261696, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_SRGB | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) @@ -457,7 +461,8 @@ enum ADataSpace : int32_t { * * Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard. */ - ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL + ADATASPACE_BT2020_PQ = 163971072, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) @@ -466,7 +471,8 @@ enum ADataSpace : int32_t { * * Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard. */ - ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED + ADATASPACE_BT2020_ITU_PQ = 298188800, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 + // | ADATASPACE_RANGE_LIMITED /** * Adobe RGB @@ -476,7 +482,8 @@ enum ADataSpace : int32_t { * Note: Application is responsible for gamma encoding the data as * a 2.2 gamma encoding is not supported in HW. */ - ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL + ADATASPACE_ADOBE_RGB = 151715840, // ADATASPACE_STANDARD_ADOBE_RGB | + // ADATASPACE_TRANSFER_GAMMA2_2 | ADATASPACE_RANGE_FULL /** * JPEG File Interchange Format (JFIF) @@ -485,7 +492,8 @@ enum ADataSpace : int32_t { * * Uses full range, SMPTE 170M transfer and BT.601_625 standard. */ - ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL + ADATASPACE_JFIF = 146931712, // ADATASPACE_STANDARD_BT601_625 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 601 (BT.601) - 625-line @@ -494,7 +502,8 @@ enum ADataSpace : int32_t { * * Uses limited range, SMPTE 170M transfer and BT.601_625 standard. */ - ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT601_625 = 281149440, // ADATASPACE_STANDARD_BT601_625 | + // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED /** * ITU-R Recommendation 601 (BT.601) - 525-line @@ -503,7 +512,8 @@ enum ADataSpace : int32_t { * * Uses limited range, SMPTE 170M transfer and BT.601_525 standard. */ - ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT601_525 = 281280512, // ADATASPACE_STANDARD_BT601_525 | + // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED /** * ITU-R Recommendation 2020 (BT.2020) @@ -512,7 +522,8 @@ enum ADataSpace : int32_t { * * Uses full range, SMPTE 170M transfer and BT2020 standard. */ - ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL + ADATASPACE_BT2020 = 147193856, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_FULL /** * ITU-R Recommendation 709 (BT.709) @@ -521,7 +532,8 @@ enum ADataSpace : int32_t { * * Uses limited range, SMPTE 170M transfer and BT.709 standard. */ - ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED + ADATASPACE_BT709 = 281083904, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SMPTE_170M | + // ADATASPACE_RANGE_LIMITED /** * SMPTE EG 432-1 and SMPTE RP 431-2 @@ -533,7 +545,8 @@ enum ADataSpace : int32_t { * Note: Application is responsible for gamma encoding the data as * a 2.6 gamma encoding is not supported in HW. */ - ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL + ADATASPACE_DCI_P3 = 155844608, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_GAMMA2_6 | + // ADATASPACE_RANGE_FULL /** * sRGB linear encoding @@ -547,21 +560,24 @@ enum ADataSpace : int32_t { * * Uses full range, linear transfer and BT.709 standard. */ - ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL + ADATASPACE_SRGB_LINEAR = 138477568, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR | + // ADATASPACE_RANGE_FULL /** * Hybrid Log Gamma encoding * * Uses full range, hybrid log gamma transfer and BT2020 standard. */ - ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL + ADATASPACE_BT2020_HLG = 168165376, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | + // ADATASPACE_RANGE_FULL /** * ITU Hybrid Log Gamma encoding * * Uses limited range, hybrid log gamma transfer and BT2020 standard. */ - ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED + ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | + // ADATASPACE_RANGE_LIMITED /** * Depth @@ -575,7 +591,37 @@ enum ADataSpace : int32_t { * * Embedded depth metadata following the dynamic depth specification. */ - ADATASPACE_DYNAMIC_DEPTH = 4098 + ADATASPACE_DYNAMIC_DEPTH = 4098, + +#ifndef ADATASPACE_SKIP_LEGACY_DEFINES + STANDARD_MASK = ADATASPACE_STANDARD_MASK, + STANDARD_UNSPECIFIED = ADATASPACE_STANDARD_UNSPECIFIED, + STANDARD_BT709 = ADATASPACE_STANDARD_BT709, + STANDARD_BT601_625 = ADATASPACE_STANDARD_BT601_625, + STANDARD_BT601_625_UNADJUSTED = ADATASPACE_STANDARD_BT601_625_UNADJUSTED, + STANDARD_BT601_525 = ADATASPACE_STANDARD_BT601_525, + STANDARD_BT601_525_UNADJUSTED = ADATASPACE_STANDARD_BT601_525_UNADJUSTED, + STANDARD_BT470M = ADATASPACE_STANDARD_BT470M, + STANDARD_BT2020 = ADATASPACE_STANDARD_BT2020, + STANDARD_FILM = ADATASPACE_STANDARD_FILM, + STANDARD_DCI_P3 = ADATASPACE_STANDARD_DCI_P3, + STANDARD_ADOBE_RGB = ADATASPACE_STANDARD_ADOBE_RGB, + TRANSFER_MASK = ADATASPACE_TRANSFER_MASK, + TRANSFER_UNSPECIFIED = ADATASPACE_TRANSFER_UNSPECIFIED, + TRANSFER_LINEAR = ADATASPACE_TRANSFER_LINEAR, + TRANSFER_SMPTE_170M = ADATASPACE_TRANSFER_SMPTE_170M, + TRANSFER_GAMMA2_2 = ADATASPACE_TRANSFER_GAMMA2_2, + TRANSFER_GAMMA2_6 = ADATASPACE_TRANSFER_GAMMA2_6, + TRANSFER_GAMMA2_8 = ADATASPACE_TRANSFER_GAMMA2_8, + TRANSFER_SRGB = ADATASPACE_TRANSFER_SRGB, + TRANSFER_ST2084 = ADATASPACE_TRANSFER_ST2084, + TRANSFER_HLG = ADATASPACE_TRANSFER_HLG, + RANGE_MASK = ADATASPACE_RANGE_MASK, + RANGE_UNSPECIFIED = ADATASPACE_RANGE_UNSPECIFIED, + RANGE_FULL = ADATASPACE_RANGE_FULL, + RANGE_LIMITED = ADATASPACE_RANGE_LIMITED, + RANGE_EXTENDED = ADATASPACE_RANGE_EXTENDED, +#endif }; __END_DECLS diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index 21798d0e29..6fcb3a4842 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -46,9 +46,16 @@ #define ANDROID_HARDWARE_BUFFER_H #include <android/rect.h> +#define ADATASPACE_SKIP_LEGACY_DEFINES +#include <android/data_space.h> +#undef ADATASPACE_SKIP_LEGACY_DEFINES #include <inttypes.h> #include <sys/cdefs.h> +#if !defined(__INTRODUCED_IN) +#define __INTRODUCED_IN(__api_level) /* nothing */ +#endif + __BEGIN_DECLS // clang-format off diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index a98ea86073..969a5cff05 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1095,10 +1095,19 @@ enum { ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL = 3, /** + * Indicates that, as a result of a user interaction, an animation is likely to start. + * This category is a signal that a user interaction heuristic determined the need of a + * high refresh rate, and is not an explicit request from the app. + * As opposed to FRAME_RATE_CATEGORY_HIGH, this vote may be ignored in favor of + * more explicit votes. + */ + ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH_HINT = 4, + + /** * Indicates a frame rate suitable for animations that require a high frame rate, which may * increase smoothness but may also increase power usage. */ - ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 4 + ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 5 }; /* diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h index 21931bb553..48fb02f69d 100644 --- a/libs/nativewindow/include/vndk/hardware_buffer.h +++ b/libs/nativewindow/include/vndk/hardware_buffer.h @@ -21,6 +21,7 @@ #include <android/hardware_buffer.h> #include <cutils/native_handle.h> +#include <errno.h> __BEGIN_DECLS @@ -105,6 +106,76 @@ enum { AHARDWAREBUFFER_USAGE_CAMERA_MASK = 6UL << 16, }; +/** + * Additional options for AHardwareBuffer_allocateWithOptions. These correspond to + * android.hardware.graphics.common.ExtendableType + */ +typedef struct { + const char* _Nonnull name; + int64_t value; +} AHardwareBufferLongOptions; + +enum AHardwareBufferStatus : int32_t { + /* Success, no error */ + AHARDWAREBUFFER_STATUS_OK = 0, + /* There's insufficient memory to satisfy the request */ + AHARDWAREBUFFER_STATUS_NO_MEMORY = -ENOMEM, + /* The given argument is invalid */ + AHARDWAREBUFFER_STATUS_BAD_VALUE = -EINVAL, + /* The requested operation is not supported by the device */ + AHARDWAREBUFFER_STATUS_UNSUPPORTED = -ENOSYS, + /* An unknown error occurred */ + AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR = (-2147483647 - 1), +}; + +/** + * Allocates a buffer that matches the passed AHardwareBuffer_Desc with additional options + * + * If allocation succeeds, the buffer can be used according to the + * usage flags specified in its description. If a buffer is used in ways + * not compatible with its usage flags, the results are undefined and + * may include program termination. + * + * @param desc The AHardwareBuffer_Desc that describes the allocation to request. Note that `stride` + * is ignored. + * @param additionalOptions A pointer to an array of AHardwareBufferLongOptions with additional + * string key + long value options that may be specified. May be null if + * `additionalOptionsSize` is 0 + * @param additionalOptionsSize The number of additional options to pass + * @param outBuffer The resulting buffer allocation + * @return AHARDWAREBUFFER_STATUS_OK on success + * AHARDWAREBUFFER_STATUS_NO_MEMORY if there's insufficient resources for the allocation + * AHARDWAREBUFFER_STATUS_BAD_VALUE if the provided description & options are not supported + * by the device + * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other error + * any reason. The returned buffer has a reference count of 1. + */ +enum AHardwareBufferStatus AHardwareBuffer_allocateWithOptions( + const AHardwareBuffer_Desc* _Nonnull desc, + const AHardwareBufferLongOptions* _Nullable additionalOptions, size_t additionalOptionsSize, + AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Queries the dataspace of the given AHardwareBuffer. + * + * @param buffer The non-null buffer for which to query the Dataspace + * @return The dataspace of the buffer, or ADATASPACE_UNKNOWN if one hasn't been set + */ +enum ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) + __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the dataspace of the given AHardwareBuffer + * @param buffer The non-null buffer for which to set the dataspace + * @param dataSpace The dataspace to set + * @return AHARDWAREBUFFER_STATUS_OK on success, + * AHARDWAREBUFFER_STATUS_UNSUPPORTED if the device doesn't support setting the dataspace, + * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other failure. + */ +enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* _Nonnull buffer, + enum ADataSpace dataSpace) + __INTRODUCED_IN(__ANDROID_API_V__); + __END_DECLS #endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 95fc920da1..e29d5a6bb4 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,6 +2,7 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; + AHardwareBuffer_allocateWithOptions; # llndk systemapi AHardwareBuffer_createFromHandle; # llndk systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 @@ -16,6 +17,8 @@ LIBNATIVEWINDOW { AHardwareBuffer_unlock; AHardwareBuffer_readFromParcel; # introduced=34 AHardwareBuffer_writeToParcel; # introduced=34 + AHardwareBuffer_getDataSpace; # llndk systemapi + AHardwareBuffer_setDataSpace; # llndk systemapi ANativeWindowBuffer_getHardwareBuffer; # llndk ANativeWindow_OemStorageGet; # llndk ANativeWindow_OemStorageSet; # llndk diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp index 90d0a8e400..a3df4820ba 100644 --- a/libs/nativewindow/rust/Android.bp +++ b/libs/nativewindow/rust/Android.bp @@ -16,6 +16,7 @@ package { default_applicable_licenses: [ "frameworks_native_libs_nativewindow_license", ], + default_team: "trendy_team_android_core_graphics_stack", } rust_bindgen { diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp index ef863b6d67..136395aac0 100644 --- a/libs/nativewindow/tests/AHardwareBufferTest.cpp +++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "AHardwareBuffer_test" //#define LOG_NDEBUG 0 +#include <android-base/properties.h> +#include <android/data_space.h> #include <android/hardware/graphics/common/1.0/types.h> #include <gtest/gtest.h> #include <private/android/AHardwareBufferHelpers.h> @@ -26,6 +28,10 @@ using namespace android; using android::hardware::graphics::common::V1_0::BufferUsage; +static bool IsCuttlefish() { + return ::android::base::GetProperty("ro.product.board", "") == "cutf"; +} + static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected, uint64_t actual, const char* type) { std::ostringstream ss; @@ -170,3 +176,83 @@ TEST(AHardwareBufferTest, GetIdTest) { EXPECT_NE(id1, id2); } + +TEST(AHardwareBufferTest, Allocate2NoExtras) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 1, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_BLOB, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + ASSERT_EQ(0, AHardwareBuffer_allocateWithOptions(&desc, nullptr, 0, &buffer)); + uint64_t id = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); + EXPECT_NE(0, id); + AHardwareBuffer_Desc desc2{}; + AHardwareBuffer_describe(buffer, &desc2); + EXPECT_EQ(desc.width, desc2.width); + EXPECT_EQ(desc.height, desc2.height); + EXPECT_GE(desc2.stride, desc2.width); + + AHardwareBuffer_release(buffer); +} + +TEST(AHardwareBufferTest, Allocate2WithExtras) { + if (!IsCuttlefish()) { + GTEST_SKIP() << "Unknown gralloc HAL, cannot test extras"; + } + + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 48, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + std::array<AHardwareBufferLongOptions, 1> extras = {{ + {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3}, + }}; + ASSERT_EQ(0, AHardwareBuffer_allocateWithOptions(&desc, extras.data(), extras.size(), &buffer)); + uint64_t id = 0; + EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); + EXPECT_NE(0, id); + AHardwareBuffer_Desc desc2{}; + AHardwareBuffer_describe(buffer, &desc2); + EXPECT_EQ(desc.width, desc2.width); + EXPECT_EQ(desc.height, desc2.height); + EXPECT_GE(desc2.stride, desc2.width); + + EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer)); + + AHardwareBuffer_release(buffer); +} + +TEST(AHardwareBufferTest, GetSetDataspace) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 48, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 0, + }; + + AHardwareBuffer* buffer = nullptr; + ASSERT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer)); + + EXPECT_EQ(ADATASPACE_UNKNOWN, AHardwareBuffer_getDataSpace(buffer)); + AHardwareBufferStatus status = AHardwareBuffer_setDataSpace(buffer, ADATASPACE_DISPLAY_P3); + if (status != AHARDWAREBUFFER_STATUS_UNSUPPORTED) { + EXPECT_EQ(0, status); + EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer)); + } + + AHardwareBuffer_release(buffer); +}
\ No newline at end of file diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp index 30737c1bf6..a4ebcaca0e 100644 --- a/libs/nativewindow/tests/Android.bp +++ b/libs/nativewindow/tests/Android.bp @@ -23,6 +23,7 @@ package { default_applicable_licenses: [ "frameworks_native_libs_nativewindow_license", ], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { @@ -31,6 +32,7 @@ cc_test { "device-tests", ], shared_libs: [ + "libbase", "libgui", "liblog", "libnativewindow", @@ -44,5 +46,8 @@ cc_test { "ANativeWindowTest.cpp", "c_compatibility.c", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], } diff --git a/libs/nativewindow/tests/benchmark/Android.bp b/libs/nativewindow/tests/benchmark/Android.bp index 6f844cf864..b815d80142 100644 --- a/libs/nativewindow/tests/benchmark/Android.bp +++ b/libs/nativewindow/tests/benchmark/Android.bp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", +} + cc_defaults { name: "nativewindow_benchmark_defaults_cc", shared_libs: ["libnativewindow"], diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index ba2eb7d224..09d7cb5d3f 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { @@ -48,11 +49,19 @@ cc_defaults { static_libs: [ "libshaders", "libtonemap", + "libsurfaceflinger_common", ], local_include_dirs: ["include"], export_include_dirs: ["include"], } +// Needed by FlagManager to access a #define. +cc_library_static { + name: "librenderengine_includes", + local_include_dirs: ["include"], + export_include_dirs: ["include"], +} + filegroup { name: "librenderengine_sources", srcs: [ @@ -78,6 +87,7 @@ filegroup { "skia/SkiaRenderEngine.cpp", "skia/SkiaGLRenderEngine.cpp", "skia/SkiaVkRenderEngine.cpp", + "skia/VulkanInterface.cpp", "skia/debug/CaptureTimer.cpp", "skia/debug/CommonPool.cpp", "skia/debug/SkiaCapture.cpp", diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 3e1ac33d57..233134d2db 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -28,28 +28,28 @@ namespace android { namespace renderengine { std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { - switch (args.renderEngineType) { - case RenderEngineType::SKIA_GL: + if (args.threaded == Threaded::YES) { + switch (args.graphicsApi) { + case GraphicsApi::GL: + ALOGD("Threaded RenderEngine with SkiaGL Backend"); + return renderengine::threaded::RenderEngineThreaded::create([args]() { + return android::renderengine::skia::SkiaGLRenderEngine::create(args); + }); + case GraphicsApi::VK: + ALOGD("Threaded RenderEngine with SkiaVK Backend"); + return renderengine::threaded::RenderEngineThreaded::create([args]() { + return android::renderengine::skia::SkiaVkRenderEngine::create(args); + }); + } + } + + switch (args.graphicsApi) { + case GraphicsApi::GL: ALOGD("RenderEngine with SkiaGL Backend"); return renderengine::skia::SkiaGLRenderEngine::create(args); - case RenderEngineType::SKIA_VK: + case GraphicsApi::VK: ALOGD("RenderEngine with SkiaVK Backend"); return renderengine::skia::SkiaVkRenderEngine::create(args); - case RenderEngineType::SKIA_GL_THREADED: { - ALOGD("Threaded RenderEngine with SkiaGL Backend"); - return renderengine::threaded::RenderEngineThreaded::create( - [args]() { - return android::renderengine::skia::SkiaGLRenderEngine::create(args); - }, - args.renderEngineType); - } - case RenderEngineType::SKIA_VK_THREADED: - ALOGD("Threaded RenderEngine with SkiaVK Backend"); - return renderengine::threaded::RenderEngineThreaded::create( - [args]() { - return android::renderengine::skia::SkiaVkRenderEngine::create(args); - }, - args.renderEngineType); } } diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp index 55c34cd059..e1a6f6af0d 100644 --- a/libs/renderengine/benchmark/Android.bp +++ b/libs/renderengine/benchmark/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_benchmark { @@ -37,6 +38,7 @@ cc_benchmark { static_libs: [ "librenderengine", "libshaders", + "libsurfaceflinger_common", "libtonemap", ], cflags: [ @@ -54,6 +56,7 @@ cc_benchmark { "libsync", "libui", "libutils", + "server_configurable_flags", ], data: ["resources/*"], diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index a7f1df9a9b..101f519e55 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -30,46 +30,6 @@ using namespace android; using namespace android::renderengine; /////////////////////////////////////////////////////////////////////////////// -// Helpers for Benchmark::Apply -/////////////////////////////////////////////////////////////////////////////// - -std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) { - switch (type) { - case RenderEngine::RenderEngineType::SKIA_GL_THREADED: - return "skiaglthreaded"; - case RenderEngine::RenderEngineType::SKIA_GL: - return "skiagl"; - case RenderEngine::RenderEngineType::SKIA_VK: - return "skiavk"; - case RenderEngine::RenderEngineType::SKIA_VK_THREADED: - return "skiavkthreaded"; - } -} - -/** - * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a - * Benchmark which specifies which RenderEngineType it uses. - * - * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make - * it obvious which version is being run. - * - * @param b The benchmark family - * @param type The type of RenderEngine to use. - */ -static void AddRenderEngineType(benchmark::internal::Benchmark* b, - RenderEngine::RenderEngineType type) { - b->Arg(static_cast<int64_t>(type)); - b->ArgName(RenderEngineTypeName(type)); -} - -/** - * Run a benchmark once using SKIA_GL_THREADED. - */ -static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) { - AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED); -} - -/////////////////////////////////////////////////////////////////////////////// // Helpers for calling drawLayers /////////////////////////////////////////////////////////////////////////////// @@ -104,7 +64,8 @@ std::pair<uint32_t, uint32_t> getDisplaySize() { return std::pair<uint32_t, uint32_t>(width, height); } -static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) { +static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded, + RenderEngine::GraphicsApi graphicsApi) { auto args = RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) .setImageCacheSize(1) @@ -112,7 +73,8 @@ static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngi .setPrecacheToneMapperShaderOnly(false) .setSupportsBackgroundBlur(true) .setContextPriority(RenderEngine::ContextPriority::REALTIME) - .setRenderEngineType(type) + .setThreaded(threaded) + .setGraphicsApi(graphicsApi) .build(); return RenderEngine::create(args); } @@ -214,8 +176,11 @@ static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& // Benchmarks /////////////////////////////////////////////////////////////////////////////// -void BM_blur(benchmark::State& benchState) { - auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range())); +template <class... Args> +void BM_blur(benchmark::State& benchState, Args&&... args) { + auto args_tuple = std::make_tuple(std::move(args)...); + auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); // Initially use cpu access so we can decode into it with AImageDecoder. auto [width, height] = getDisplaySize(); @@ -259,4 +224,5 @@ void BM_blur(benchmark::State& benchState) { benchDrawLayers(*re, layers, benchState, "blurred"); } -BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded); +BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL); diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 818d0350c0..de05268a67 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -33,7 +33,7 @@ #include <memory> /** - * Allows to set RenderEngine backend to GLES (default) or SkiaGL (NOT yet supported). + * Allows to override the RenderEngine backend. */ #define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" @@ -92,15 +92,20 @@ public: REALTIME = 4, }; - enum class RenderEngineType { - SKIA_GL = 3, - SKIA_GL_THREADED = 4, - SKIA_VK = 5, - SKIA_VK_THREADED = 6, + enum class Threaded { + NO, + YES, + }; + + enum class GraphicsApi { + GL, + VK, }; static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); + static bool canSupport(GraphicsApi); + virtual ~RenderEngine() = 0; // ----- BEGIN DEPRECATED INTERFACE ----- @@ -176,10 +181,9 @@ public: // query is required to be thread safe. virtual bool supportsBackgroundBlur() = 0; - // Returns the current type of RenderEngine instance that was created. // TODO(b/180767535): This is only implemented to allow for backend-specific behavior, which // we should not allow in general, so remove this. - RenderEngineType getRenderEngineType() const { return mRenderEngineType; } + bool isThreaded() const { return mThreaded == Threaded::YES; } static void validateInputBufferUsage(const sp<GraphicBuffer>&); static void validateOutputBufferUsage(const sp<GraphicBuffer>&); @@ -191,9 +195,9 @@ public: virtual void setEnableTracing(bool /*tracingEnabled*/) {} protected: - RenderEngine() : RenderEngine(RenderEngineType::SKIA_GL) {} + RenderEngine() : RenderEngine(Threaded::NO) {} - RenderEngine(RenderEngineType type) : mRenderEngineType(type) {} + RenderEngine(Threaded threaded) : mThreaded(threaded) {} // Maps GPU resources for this buffer. // Note that work may be deferred to an additional thread, i.e. this call @@ -228,7 +232,7 @@ protected: friend class impl::ExternalTexture; friend class threaded::RenderEngineThreaded; friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test; - const RenderEngineType mRenderEngineType; + const Threaded mThreaded; // Update protectedContext mode depending on whether or not any layer has a protected buffer. void updateProtectedContext(const std::vector<LayerSettings>&, @@ -251,7 +255,8 @@ struct RenderEngineCreationArgs { bool precacheToneMapperShaderOnly; bool supportsBackgroundBlur; RenderEngine::ContextPriority contextPriority; - RenderEngine::RenderEngineType renderEngineType; + RenderEngine::Threaded threaded; + RenderEngine::GraphicsApi graphicsApi; struct Builder; @@ -261,14 +266,16 @@ private: bool _enableProtectedContext, bool _precacheToneMapperShaderOnly, bool _supportsBackgroundBlur, RenderEngine::ContextPriority _contextPriority, - RenderEngine::RenderEngineType _renderEngineType) + RenderEngine::Threaded _threaded, + RenderEngine::GraphicsApi _graphicsApi) : pixelFormat(_pixelFormat), imageCacheSize(_imageCacheSize), enableProtectedContext(_enableProtectedContext), precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly), supportsBackgroundBlur(_supportsBackgroundBlur), contextPriority(_contextPriority), - renderEngineType(_renderEngineType) {} + threaded(_threaded), + graphicsApi(_graphicsApi) {} RenderEngineCreationArgs() = delete; }; @@ -299,14 +306,18 @@ struct RenderEngineCreationArgs::Builder { this->contextPriority = contextPriority; return *this; } - Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) { - this->renderEngineType = renderEngineType; + Builder& setThreaded(RenderEngine::Threaded threaded) { + this->threaded = threaded; + return *this; + } + Builder& setGraphicsApi(RenderEngine::GraphicsApi graphicsApi) { + this->graphicsApi = graphicsApi; return *this; } RenderEngineCreationArgs build() const { return RenderEngineCreationArgs(pixelFormat, imageCacheSize, enableProtectedContext, precacheToneMapperShaderOnly, supportsBackgroundBlur, - contextPriority, renderEngineType); + contextPriority, threaded, graphicsApi); } private: @@ -317,8 +328,8 @@ private: bool precacheToneMapperShaderOnly = false; bool supportsBackgroundBlur = false; RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM; - RenderEngine::RenderEngineType renderEngineType = - RenderEngine::RenderEngineType::SKIA_GL_THREADED; + RenderEngine::Threaded threaded = RenderEngine::Threaded::YES; + RenderEngine::GraphicsApi graphicsApi = RenderEngine::GraphicsApi::GL; }; } // namespace renderengine diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index 92fe4c0b47..ee95e59d90 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -77,7 +77,7 @@ AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer backendFormat, isOutputBuffer); } else { - LOG_ALWAYS_FATAL("Unexpected backend %d", backend); + LOG_ALWAYS_FATAL("Unexpected backend %u", static_cast<unsigned>(backend)); } mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); @@ -145,8 +145,8 @@ void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i " "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i " "fSampleCount: %u fLevelCount: %u colorType %i", - msg, tex.isValid(), dataspace, tex.width(), tex.height(), - tex.hasMipmaps(), tex.isProtected(), + msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(), + tex.height(), tex.hasMipmaps(), tex.isProtected(), static_cast<int>(tex.textureType()), retrievedImageInfo, imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount, colorType); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 2053c6a34f..fea4129ec0 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -236,7 +236,13 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo err = selectEGLConfig(display, format, 0, &config); if (err != NO_ERROR) { // this EGL is too lame for android - LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up" + " (format: %d, vendor: %s, version: %s, extensions: %s, Client" + " API: %s)", + format, eglQueryString(display, EGL_VENDOR), + eglQueryString(display, EGL_VERSION), + eglQueryString(display, EGL_EXTENSIONS), + eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); } } } @@ -262,7 +268,7 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext, EGLSurface protectedPlaceholder) - : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat), args.supportsBackgroundBlur), mEGLDisplay(display), mEGLContext(ctxt), diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 88326e77d7..6e393f03fa 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -53,6 +53,7 @@ #include <SkSurface.h> #include <SkTileMode.h> #include <android-base/stringprintf.h> +#include <common/FlagManager.h> #include <gui/FenceMonitor.h> #include <gui/TraceUtils.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> @@ -269,9 +270,9 @@ void SkiaRenderEngine::setEnableTracing(bool tracingEnabled) { SkAndroidFrameworkTraceUtil::setEnableTracing(tracingEnabled); } -SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, +SkiaRenderEngine::SkiaRenderEngine(Threaded threaded, PixelFormat pixelFormat, bool supportsBackgroundBlur) - : RenderEngine(type), mDefaultPixelFormat(pixelFormat) { + : RenderEngine(threaded), mDefaultPixelFormat(pixelFormat) { if (supportsBackgroundBlur) { ALOGD("Background Blurs Enabled"); mBlurFilter = new KawaseBlurFilter(); @@ -389,10 +390,9 @@ void SkiaRenderEngine::ensureGrContextsCreated() { void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) { // Only run this if RE is running on its own thread. This - // way the access to GL operations is guaranteed to be happening on the + // way the access to GL/VK operations is guaranteed to be happening on the // same thread. - if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED && - mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) { + if (!isThreaded()) { return; } // We don't attempt to map a buffer if the buffer contains protected content. In GL this is @@ -420,6 +420,9 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, mGraphicBufferExternalRefs[buffer->getId()]++; if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) { + if (FlagManager::getInstance().renderable_buffer_usage()) { + isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER; + } std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(grContext, buffer->toAHardwareBuffer(), @@ -761,10 +764,11 @@ void SkiaRenderEngine::drawLayersInternal( // save a snapshot of the activeSurface to use as input to the blur shaders blurInput = activeSurface->makeImageSnapshot(); - // blit the offscreen framebuffer into the destination AHB, but only - // if there are blur regions. backgroundBlurRadius blurs the entire - // image below, so it can skip this step. - if (layer.blurRegions.size()) { + // blit the offscreen framebuffer into the destination AHB. This ensures that + // even if the blurred image does not cover the screen (for example, during + // a rotation animation, or if blur regions are used), the entire screen is + // initialized. + if (layer.blurRegions.size() || FlagManager::getInstance().restore_blur_step()) { SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); if (CC_UNLIKELY(mCapture->isCaptureRunning())) { diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index ac134afa64..e88d44cca6 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -59,7 +59,7 @@ class BlurFilter; class SkiaRenderEngine : public RenderEngine { public: static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args); - SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, bool supportsBackgroundBlur); + SkiaRenderEngine(Threaded, PixelFormat pixelFormat, bool supportsBackgroundBlur); ~SkiaRenderEngine() override; std::future<void> primeCache(bool shouldPrimeUltraHDR) override final; diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index ba20d1f223..f2f1b5d0ca 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -25,6 +25,7 @@ #include <GrContextOptions.h> #include <vk/GrVkExtensions.h> #include <vk/GrVkTypes.h> +#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> #include <include/gpu/ganesh/vk/GrVkDirectContext.h> #include <android-base/stringprintf.h> @@ -32,9 +33,8 @@ #include <sync/sync.h> #include <utils/Trace.h> -#include <cstdint> #include <memory> -#include <vector> +#include <string> #include <vulkan/vulkan.h> #include "log/log_main.h" @@ -42,549 +42,49 @@ namespace android { namespace renderengine { -struct VulkanFuncs { - PFN_vkCreateSemaphore vkCreateSemaphore = nullptr; - PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr; - PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr; - PFN_vkDestroySemaphore vkDestroySemaphore = nullptr; - - PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr; - PFN_vkDestroyDevice vkDestroyDevice = nullptr; - PFN_vkDestroyInstance vkDestroyInstance = nullptr; -}; - -// Ref-Count a semaphore -struct DestroySemaphoreInfo { - VkSemaphore mSemaphore; - // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia - // (including by the GPU) and inside SkiaVkRenderEngine. So we always start with two refs, one - // owned by Skia and one owned by the SkiaVkRenderEngine. The refs are decremented each time - // delete_semaphore* is called with this object. Skia will call destroy_semaphore* once it is - // done with the semaphore and the GPU has finished work on the semaphore. SkiaVkRenderEngine - // calls delete_semaphore* after sending the semaphore to Skia and exporting it if need be. - int mRefs = 2; - - DestroySemaphoreInfo(VkSemaphore semaphore) : mSemaphore(semaphore) {} -}; - -struct VulkanInterface { - bool initialized = false; - VkInstance instance; - VkPhysicalDevice physicalDevice; - VkDevice device; - VkQueue queue; - int queueIndex; - uint32_t apiVersion; - GrVkExtensions grExtensions; - VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr; - VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr; - VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr; - GrVkGetProc grGetProc; - bool isProtected; - bool isRealtimePriority; - - VulkanFuncs funcs; - - std::vector<std::string> instanceExtensionNames; - std::vector<std::string> deviceExtensionNames; - - GrVkBackendContext getBackendContext() { - GrVkBackendContext backendContext; - backendContext.fInstance = instance; - backendContext.fPhysicalDevice = physicalDevice; - backendContext.fDevice = device; - backendContext.fQueue = queue; - backendContext.fGraphicsQueueIndex = queueIndex; - backendContext.fMaxAPIVersion = apiVersion; - backendContext.fVkExtensions = &grExtensions; - backendContext.fDeviceFeatures2 = physicalDeviceFeatures2; - backendContext.fGetProc = grGetProc; - backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo; - return backendContext; - }; - - VkSemaphore createExportableSemaphore() { - VkExportSemaphoreCreateInfo exportInfo; - exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; - exportInfo.pNext = nullptr; - exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - - VkSemaphoreCreateInfo semaphoreInfo; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semaphoreInfo.pNext = &exportInfo; - semaphoreInfo.flags = 0; - - VkSemaphore semaphore; - VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore); - if (VK_SUCCESS != err) { - ALOGE("%s: failed to create semaphore. err %d\n", __func__, err); - return VK_NULL_HANDLE; - } - - return semaphore; - } - - // syncFd cannot be <= 0 - VkSemaphore importSemaphoreFromSyncFd(int syncFd) { - VkSemaphoreCreateInfo semaphoreInfo; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semaphoreInfo.pNext = nullptr; - semaphoreInfo.flags = 0; - - VkSemaphore semaphore; - VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore); - if (VK_SUCCESS != err) { - ALOGE("%s: failed to create import semaphore", __func__); - return VK_NULL_HANDLE; - } - - VkImportSemaphoreFdInfoKHR importInfo; - importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; - importInfo.pNext = nullptr; - importInfo.semaphore = semaphore; - importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; - importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - importInfo.fd = syncFd; - - err = funcs.vkImportSemaphoreFdKHR(device, &importInfo); - if (VK_SUCCESS != err) { - funcs.vkDestroySemaphore(device, semaphore, nullptr); - ALOGE("%s: failed to import semaphore", __func__); - return VK_NULL_HANDLE; - } - - return semaphore; - } - - int exportSemaphoreSyncFd(VkSemaphore semaphore) { - int res; - - VkSemaphoreGetFdInfoKHR getFdInfo; - getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; - getFdInfo.pNext = nullptr; - getFdInfo.semaphore = semaphore; - getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - - VkResult err = funcs.vkGetSemaphoreFdKHR(device, &getFdInfo, &res); - if (VK_SUCCESS != err) { - ALOGE("%s: failed to export semaphore, err: %d", __func__, err); - return -1; - } - return res; - } - - void destroySemaphore(VkSemaphore semaphore) { - funcs.vkDestroySemaphore(device, semaphore, nullptr); - } -}; - -static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { - if (device != VK_NULL_HANDLE) { - return vkGetDeviceProcAddr(device, proc_name); - } - return vkGetInstanceProcAddr(instance, proc_name); -}; - -#define BAIL(fmt, ...) \ - { \ - ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \ - return interface; \ - } - -#define CHECK_NONNULL(expr) \ - if ((expr) == nullptr) { \ - BAIL("[%s] null", #expr); \ - } - -#define VK_CHECK(expr) \ - if ((expr) != VK_SUCCESS) { \ - BAIL("[%s] failed. err = %d", #expr, expr); \ - return interface; \ - } - -#define VK_GET_PROC(F) \ - PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \ - CHECK_NONNULL(vk##F) -#define VK_GET_INST_PROC(instance, F) \ - PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \ - CHECK_NONNULL(vk##F) -#define VK_GET_DEV_PROC(device, F) \ - PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \ - CHECK_NONNULL(vk##F) - -VulkanInterface initVulkanInterface(bool protectedContent = false) { - VulkanInterface interface; - - VK_GET_PROC(EnumerateInstanceVersion); - uint32_t instanceVersion; - VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion)); - - if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) { - return interface; - } - - const VkApplicationInfo appInfo = { - VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0, - VK_MAKE_VERSION(1, 1, 0), - }; - - VK_GET_PROC(EnumerateInstanceExtensionProperties); - - uint32_t extensionCount = 0; - VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr)); - std::vector<VkExtensionProperties> instanceExtensions(extensionCount); - VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, - instanceExtensions.data())); - std::vector<const char*> enabledInstanceExtensionNames; - enabledInstanceExtensionNames.reserve(instanceExtensions.size()); - interface.instanceExtensionNames.reserve(instanceExtensions.size()); - for (const auto& instExt : instanceExtensions) { - enabledInstanceExtensionNames.push_back(instExt.extensionName); - interface.instanceExtensionNames.push_back(instExt.extensionName); - } - - const VkInstanceCreateInfo instanceCreateInfo = { - VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - nullptr, - 0, - &appInfo, - 0, - nullptr, - (uint32_t)enabledInstanceExtensionNames.size(), - enabledInstanceExtensionNames.data(), - }; - - VK_GET_PROC(CreateInstance); - VkInstance instance; - VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); - - VK_GET_INST_PROC(instance, DestroyInstance); - interface.funcs.vkDestroyInstance = vkDestroyInstance; - VK_GET_INST_PROC(instance, EnumeratePhysicalDevices); - VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties); - VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2); - VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties); - VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties2); - VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2); - VK_GET_INST_PROC(instance, CreateDevice); - - uint32_t physdevCount; - VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr)); - if (physdevCount == 0) { - BAIL("Could not find any physical devices"); - } - - physdevCount = 1; - VkPhysicalDevice physicalDevice; - VkResult enumeratePhysDevsErr = - vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice); - if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) { - BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d", - enumeratePhysDevsErr); - } - - VkPhysicalDeviceProperties2 physDevProps = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - 0, - {}, - }; - VkPhysicalDeviceProtectedMemoryProperties protMemProps = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, - 0, - {}, - }; - - if (protectedContent) { - physDevProps.pNext = &protMemProps; - } - - vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps); - if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { - BAIL("Could not find a Vulkan 1.1+ physical device"); - } - - // Check for syncfd support. Bail if we cannot both import and export them. - VkPhysicalDeviceExternalSemaphoreInfo semInfo = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, - nullptr, - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - }; - VkExternalSemaphoreProperties semProps = { - VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0, - }; - vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps); - - bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes & - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && - (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && - (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) && - (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); - - if (!sufficientSemaphoreSyncFdSupport) { - BAIL("Vulkan device does not support sufficient external semaphore sync fd features. " - "exportFromImportedHandleTypes 0x%x (needed 0x%x) " - "compatibleHandleTypes 0x%x (needed 0x%x) " - "externalSemaphoreFeatures 0x%x (needed 0x%x) ", - semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - semProps.externalSemaphoreFeatures, - VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | - VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); - } else { - ALOGD("Vulkan device supports sufficient external semaphore sync fd features. " - "exportFromImportedHandleTypes 0x%x (needed 0x%x) " - "compatibleHandleTypes 0x%x (needed 0x%x) " - "externalSemaphoreFeatures 0x%x (needed 0x%x) ", - semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - semProps.externalSemaphoreFeatures, - VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | - VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); - } - - uint32_t queueCount; - vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, nullptr); - if (queueCount == 0) { - BAIL("Could not find queues for physical device"); - } - - std::vector<VkQueueFamilyProperties2> queueProps(queueCount); - std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount); - VkQueueGlobalPriorityKHR queuePriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR; - // Even though we don't yet know if the VK_EXT_global_priority extension is available, - // we can safely add the request to the pNext chain, and if the extension is not - // available, it will be ignored. - for (uint32_t i = 0; i < queueCount; ++i) { - queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT; - queuePriorityProps[i].pNext = nullptr; - queueProps[i].pNext = &queuePriorityProps[i]; - } - vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, queueProps.data()); - - int graphicsQueueIndex = -1; - for (uint32_t i = 0; i < queueCount; ++i) { - // Look at potential answers to the VK_EXT_global_priority query. If answers were - // provided, we may adjust the queuePriority. - if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - for (uint32_t j = 0; j < queuePriorityProps[i].priorityCount; j++) { - if (queuePriorityProps[i].priorities[j] > queuePriority) { - queuePriority = queuePriorityProps[i].priorities[j]; - } - } - if (queuePriority == VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR) { - interface.isRealtimePriority = true; - } - graphicsQueueIndex = i; - break; - } - } - - if (graphicsQueueIndex == -1) { - BAIL("Could not find a graphics queue family"); - } - - uint32_t deviceExtensionCount; - VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, - nullptr)); - std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount); - VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, - deviceExtensions.data())); - - std::vector<const char*> enabledDeviceExtensionNames; - enabledDeviceExtensionNames.reserve(deviceExtensions.size()); - interface.deviceExtensionNames.reserve(deviceExtensions.size()); - for (const auto& devExt : deviceExtensions) { - enabledDeviceExtensionNames.push_back(devExt.extensionName); - interface.deviceExtensionNames.push_back(devExt.extensionName); - } - - interface.grExtensions.init(sGetProc, instance, physicalDevice, - enabledInstanceExtensionNames.size(), - enabledInstanceExtensionNames.data(), - enabledDeviceExtensionNames.size(), - enabledDeviceExtensionNames.data()); - - if (!interface.grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { - BAIL("Vulkan driver doesn't support external semaphore fd"); - } - - interface.physicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2; - interface.physicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - interface.physicalDeviceFeatures2->pNext = nullptr; - - interface.samplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures; - interface.samplerYcbcrConversionFeatures->sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; - interface.samplerYcbcrConversionFeatures->pNext = nullptr; - - interface.physicalDeviceFeatures2->pNext = interface.samplerYcbcrConversionFeatures; - void** tailPnext = &interface.samplerYcbcrConversionFeatures->pNext; - - if (protectedContent) { - interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryFeatures; - interface.protectedMemoryFeatures->sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; - interface.protectedMemoryFeatures->pNext = nullptr; - *tailPnext = interface.protectedMemoryFeatures; - tailPnext = &interface.protectedMemoryFeatures->pNext; - } - - vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2); - // Looks like this would slow things down and we can't depend on it on all platforms - interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; - - if (protectedContent && !interface.protectedMemoryFeatures->protectedMemory) { - BAIL("Protected memory not supported"); - } - - float queuePriorities[1] = {0.0f}; - void* queueNextPtr = nullptr; - - VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = { - VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT, - nullptr, - // If queue priority is supported, RE should always have realtime priority. - queuePriority, - }; - - if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { - queueNextPtr = &queuePriorityCreateInfo; - } - - VkDeviceQueueCreateFlags deviceQueueCreateFlags = - (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0); - - const VkDeviceQueueCreateInfo queueInfo = { - VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - queueNextPtr, - deviceQueueCreateFlags, - (uint32_t)graphicsQueueIndex, - 1, - queuePriorities, - }; - - const VkDeviceCreateInfo deviceInfo = { - VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - interface.physicalDeviceFeatures2, - 0, - 1, - &queueInfo, - 0, - nullptr, - (uint32_t)enabledDeviceExtensionNames.size(), - enabledDeviceExtensionNames.data(), - nullptr, - }; - - ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent); - VkDevice device; - VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device)); - ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent); - - VkQueue graphicsQueue; - VK_GET_DEV_PROC(device, GetDeviceQueue2); - const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr, - deviceQueueCreateFlags, - (uint32_t)graphicsQueueIndex, 0}; - vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue); - - VK_GET_DEV_PROC(device, DeviceWaitIdle); - VK_GET_DEV_PROC(device, DestroyDevice); - interface.funcs.vkDeviceWaitIdle = vkDeviceWaitIdle; - interface.funcs.vkDestroyDevice = vkDestroyDevice; - - VK_GET_DEV_PROC(device, CreateSemaphore); - VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR); - VK_GET_DEV_PROC(device, GetSemaphoreFdKHR); - VK_GET_DEV_PROC(device, DestroySemaphore); - interface.funcs.vkCreateSemaphore = vkCreateSemaphore; - interface.funcs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR; - interface.funcs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR; - interface.funcs.vkDestroySemaphore = vkDestroySemaphore; - - // At this point, everything's succeeded and we can continue - interface.initialized = true; - interface.instance = instance; - interface.physicalDevice = physicalDevice; - interface.device = device; - interface.queue = graphicsQueue; - interface.queueIndex = graphicsQueueIndex; - interface.apiVersion = physDevProps.properties.apiVersion; - // grExtensions already constructed - // feature pointers already constructed - interface.grGetProc = sGetProc; - interface.isProtected = protectedContent; - // funcs already initialized - - ALOGD("%s: Success init Vulkan interface", __func__); - return interface; -} - -void teardownVulkanInterface(VulkanInterface* interface) { - interface->initialized = false; - - if (interface->device != VK_NULL_HANDLE) { - interface->funcs.vkDeviceWaitIdle(interface->device); - interface->funcs.vkDestroyDevice(interface->device, nullptr); - interface->device = VK_NULL_HANDLE; - } - if (interface->instance != VK_NULL_HANDLE) { - interface->funcs.vkDestroyInstance(interface->instance, nullptr); - interface->instance = VK_NULL_HANDLE; - } - - if (interface->protectedMemoryFeatures) { - delete interface->protectedMemoryFeatures; - } - - if (interface->samplerYcbcrConversionFeatures) { - delete interface->samplerYcbcrConversionFeatures; - } - - if (interface->physicalDeviceFeatures2) { - delete interface->physicalDeviceFeatures2; - } - - interface->samplerYcbcrConversionFeatures = nullptr; - interface->physicalDeviceFeatures2 = nullptr; - interface->protectedMemoryFeatures = nullptr; -} - -static VulkanInterface sVulkanInterface; -static VulkanInterface sProtectedContentVulkanInterface; +static skia::VulkanInterface sVulkanInterface; +static skia::VulkanInterface sProtectedContentVulkanInterface; static void sSetupVulkanInterface() { - if (!sVulkanInterface.initialized) { - sVulkanInterface = initVulkanInterface(false /* no protected content */); + if (!sVulkanInterface.isInitialized()) { + sVulkanInterface.init(false /* no protected content */); // We will have to abort if non-protected VkDevice creation fails (then nothing works). - LOG_ALWAYS_FATAL_IF(!sVulkanInterface.initialized, + LOG_ALWAYS_FATAL_IF(!sVulkanInterface.isInitialized(), "Could not initialize Vulkan RenderEngine!"); } - if (!sProtectedContentVulkanInterface.initialized) { - sProtectedContentVulkanInterface = initVulkanInterface(true /* protected content */); - if (!sProtectedContentVulkanInterface.initialized) { + if (!sProtectedContentVulkanInterface.isInitialized()) { + sProtectedContentVulkanInterface.init(true /* protected content */); + if (!sProtectedContentVulkanInterface.isInitialized()) { ALOGE("Could not initialize protected content Vulkan RenderEngine."); } } } +bool RenderEngine::canSupport(GraphicsApi graphicsApi) { + switch (graphicsApi) { + case GraphicsApi::GL: + return true; + case GraphicsApi::VK: { + if (!sVulkanInterface.isInitialized()) { + sVulkanInterface.init(false /* no protected content */); + ALOGD("%s: initialized == %s.", __func__, + sVulkanInterface.isInitialized() ? "true" : "false"); + } + return sVulkanInterface.isInitialized(); + } + } +} + namespace skia { using base::StringAppendF; -bool SkiaVkRenderEngine::canSupportSkiaVkRenderEngine() { - VulkanInterface temp = initVulkanInterface(false /* no protected content */); - ALOGD("SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(): initialized == %s.", - temp.initialized ? "true" : "false"); - return temp.initialized; -} - std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args)); engine->ensureGrContextsCreated(); - if (sVulkanInterface.initialized) { + if (sVulkanInterface.isInitialized()) { ALOGD("SkiaVkRenderEngine::%s: successfully initialized SkiaVkRenderEngine", __func__); return engine; } else { @@ -596,7 +96,7 @@ std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( } SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args) - : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat), args.supportsBackgroundBlur) {} SkiaVkRenderEngine::~SkiaVkRenderEngine() { @@ -619,29 +119,17 @@ SkiaRenderEngine::Contexts SkiaVkRenderEngine::createDirectContexts( } bool SkiaVkRenderEngine::supportsProtectedContentImpl() const { - return sProtectedContentVulkanInterface.initialized; + return sProtectedContentVulkanInterface.isInitialized(); } bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) { return true; } -static void delete_semaphore(void* semaphore) { - DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore); - --info->mRefs; - if (!info->mRefs) { - sVulkanInterface.destroySemaphore(info->mSemaphore); - delete info; - } -} - -static void delete_semaphore_protected(void* semaphore) { - DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore); - --info->mRefs; - if (!info->mRefs) { - sProtectedContentVulkanInterface.destroySemaphore(info->mSemaphore); - delete info; - } +static void unref_semaphore(void* semaphore) { + SkiaVkRenderEngine::DestroySemaphoreInfo* info = + reinterpret_cast<SkiaVkRenderEngine::DestroySemaphoreInfo*>(semaphore); + info->unref(); } static VulkanInterface& getVulkanInterface(bool protectedContext) { @@ -664,8 +152,7 @@ void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, base::borrowed_fd base::unique_fd fenceDup(dupedFd); VkSemaphore waitSemaphore = getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release()); - GrBackendSemaphore beSemaphore; - beSemaphore.initVulkan(waitSemaphore); + GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(waitSemaphore); grContext->wait(1, &beSemaphore, true /* delete after wait */); } @@ -673,16 +160,15 @@ base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { VulkanInterface& vi = getVulkanInterface(isProtected()); VkSemaphore semaphore = vi.createExportableSemaphore(); - GrBackendSemaphore backendSemaphore; - backendSemaphore.initVulkan(semaphore); + GrBackendSemaphore backendSemaphore = GrBackendSemaphores::MakeVk(semaphore); GrFlushInfo flushInfo; DestroySemaphoreInfo* destroySemaphoreInfo = nullptr; if (semaphore != VK_NULL_HANDLE) { - destroySemaphoreInfo = new DestroySemaphoreInfo(semaphore); + destroySemaphoreInfo = new DestroySemaphoreInfo(vi, semaphore); flushInfo.fNumSemaphores = 1; flushInfo.fSignalSemaphores = &backendSemaphore; - flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore; + flushInfo.fFinishedProc = unref_semaphore; flushInfo.fFinishedContext = destroySemaphoreInfo; } GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); @@ -702,7 +188,7 @@ base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { int SkiaVkRenderEngine::getContextPriority() { // EGL_CONTEXT_PRIORITY_REALTIME_NV constexpr int kRealtimePriority = 0x3357; - if (getVulkanInterface(isProtected()).isRealtimePriority) { + if (getVulkanInterface(isProtected()).isRealtimePriority()) { return kRealtimePriority; } else { return 0; @@ -711,21 +197,21 @@ int SkiaVkRenderEngine::getContextPriority() { void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { StringAppendF(&result, "\n ------------RE Vulkan----------\n"); - StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.initialized); + StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized()); StringAppendF(&result, "\n Vulkan protected device initialized: %d\n", - sProtectedContentVulkanInterface.initialized); + sProtectedContentVulkanInterface.isInitialized()); - if (!sVulkanInterface.initialized) { + if (!sVulkanInterface.isInitialized()) { return; } StringAppendF(&result, "\n Instance extensions:\n"); - for (const auto& name : sVulkanInterface.instanceExtensionNames) { + for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) { StringAppendF(&result, "\n %s\n", name.c_str()); } StringAppendF(&result, "\n Device extensions:\n"); - for (const auto& name : sVulkanInterface.deviceExtensionNames) { + for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) { StringAppendF(&result, "\n %s\n", name.c_str()); } } diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index 2e0cf45220..ca0dcbf4ae 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -20,6 +20,7 @@ #include <vk/GrVkBackendContext.h> #include "SkiaRenderEngine.h" +#include "VulkanInterface.h" namespace android { namespace renderengine { @@ -27,13 +28,47 @@ namespace skia { class SkiaVkRenderEngine : public SkiaRenderEngine { public: - // Returns false if Vulkan implementation can't support SkiaVkRenderEngine. - static bool canSupportSkiaVkRenderEngine(); static std::unique_ptr<SkiaVkRenderEngine> create(const RenderEngineCreationArgs& args); ~SkiaVkRenderEngine() override; int getContextPriority() override; + class DestroySemaphoreInfo { + public: + DestroySemaphoreInfo() = delete; + DestroySemaphoreInfo(const DestroySemaphoreInfo&) = delete; + DestroySemaphoreInfo& operator=(const DestroySemaphoreInfo&) = delete; + DestroySemaphoreInfo& operator=(DestroySemaphoreInfo&&) = delete; + + DestroySemaphoreInfo(VulkanInterface& vulkanInterface, std::vector<VkSemaphore> semaphores) + : mVulkanInterface(vulkanInterface), mSemaphores(std::move(semaphores)) {} + DestroySemaphoreInfo(VulkanInterface& vulkanInterface, VkSemaphore semaphore) + : DestroySemaphoreInfo(vulkanInterface, std::vector<VkSemaphore>(1, semaphore)) {} + + void unref() { + --mRefs; + if (!mRefs) { + for (VkSemaphore semaphore : mSemaphores) { + mVulkanInterface.destroySemaphore(semaphore); + } + delete this; + } + } + + private: + ~DestroySemaphoreInfo() = default; + + VulkanInterface& mVulkanInterface; + std::vector<VkSemaphore> mSemaphores; + // We need to make sure we don't delete the VkSemaphore until it is done being used by both + // Skia (including by the GPU) and inside SkiaVkRenderEngine. So we always start with two + // refs, one owned by Skia and one owned by the SkiaVkRenderEngine. The refs are decremented + // each time unref() is called on this object. Skia will call unref() once it is done with + // the semaphore and the GPU has finished work on the semaphore. SkiaVkRenderEngine calls + // unref() after sending the semaphore to Skia and exporting it if need be. + int mRefs = 2; + }; + protected: // Implementations of abstract SkiaRenderEngine functions specific to // rendering backend diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp new file mode 100644 index 0000000000..453cdc1196 --- /dev/null +++ b/libs/renderengine/skia/VulkanInterface.cpp @@ -0,0 +1,582 @@ +/* + * Copyright 2024 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 "RenderEngine" + +#include "VulkanInterface.h" + +#include <include/gpu/GpuTypes.h> +#include <log/log_main.h> +#include <utils/Timers.h> + +#include <cinttypes> +#include <sstream> + +namespace android { +namespace renderengine { +namespace skia { + +GrVkBackendContext VulkanInterface::getBackendContext() { + GrVkBackendContext backendContext; + backendContext.fInstance = mInstance; + backendContext.fPhysicalDevice = mPhysicalDevice; + backendContext.fDevice = mDevice; + backendContext.fQueue = mQueue; + backendContext.fGraphicsQueueIndex = mQueueIndex; + backendContext.fMaxAPIVersion = mApiVersion; + backendContext.fVkExtensions = &mGrExtensions; + backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2; + backendContext.fGetProc = mGrGetProc; + backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo; + backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived + backendContext.fDeviceLostProc = onVkDeviceFault; + return backendContext; +}; + +VkSemaphore VulkanInterface::createExportableSemaphore() { + VkExportSemaphoreCreateInfo exportInfo; + exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; + exportInfo.pNext = nullptr; + exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = &exportInfo; + semaphoreInfo.flags = 0; + + VkSemaphore semaphore; + VkResult err = mFuncs.vkCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to create semaphore. err %d\n", __func__, err); + return VK_NULL_HANDLE; + } + + return semaphore; +} + +// syncFd cannot be <= 0 +VkSemaphore VulkanInterface::importSemaphoreFromSyncFd(int syncFd) { + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = nullptr; + semaphoreInfo.flags = 0; + + VkSemaphore semaphore; + VkResult err = mFuncs.vkCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to create import semaphore", __func__); + return VK_NULL_HANDLE; + } + + VkImportSemaphoreFdInfoKHR importInfo; + importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; + importInfo.pNext = nullptr; + importInfo.semaphore = semaphore; + importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; + importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + importInfo.fd = syncFd; + + err = mFuncs.vkImportSemaphoreFdKHR(mDevice, &importInfo); + if (VK_SUCCESS != err) { + mFuncs.vkDestroySemaphore(mDevice, semaphore, nullptr); + ALOGE("%s: failed to import semaphore", __func__); + return VK_NULL_HANDLE; + } + + return semaphore; +} + +int VulkanInterface::exportSemaphoreSyncFd(VkSemaphore semaphore) { + int res; + + VkSemaphoreGetFdInfoKHR getFdInfo; + getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; + getFdInfo.pNext = nullptr; + getFdInfo.semaphore = semaphore; + getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + VkResult err = mFuncs.vkGetSemaphoreFdKHR(mDevice, &getFdInfo, &res); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to export semaphore, err: %d", __func__, err); + return -1; + } + return res; +} + +void VulkanInterface::destroySemaphore(VkSemaphore semaphore) { + mFuncs.vkDestroySemaphore(mDevice, semaphore, nullptr); +} + +void VulkanInterface::onVkDeviceFault(void* callbackContext, const std::string& description, + const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, + const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, + const std::vector<std::byte>& vendorBinaryData) { + VulkanInterface* interface = static_cast<VulkanInterface*>(callbackContext); + const std::string protectedStr = interface->mIsProtected ? "protected" : "non-protected"; + // The final crash string should contain as much differentiating info as possible, up to 1024 + // bytes. As this final message is constructed, the same information is also dumped to the logs + // but in a more verbose format. Building the crash string is unsightly, so the clearer logging + // statement is always placed first to give context. + ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", protectedStr.c_str(), description.c_str()); + std::stringstream crashMsg; + crashMsg << "VK_ERROR_DEVICE_LOST (" << protectedStr; + + if (!addressInfos.empty()) { + ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size()); + crashMsg << ", " << addressInfos.size() << " address info ("; + for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) { + ALOGE(" addressType: %d", (int)addressInfo.addressType); + ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress); + ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision); + crashMsg << addressInfo.addressType << ":" << addressInfo.reportedAddress << ":" + << addressInfo.addressPrecision << ", "; + } + crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " + crashMsg << ")"; + } + + if (!vendorInfos.empty()) { + ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size()); + crashMsg << ", " << vendorInfos.size() << " vendor info ("; + for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) { + ALOGE(" description: %s", vendorInfo.description); + ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode); + ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData); + // Omit descriptions for individual vendor info structs in the crash string, as the + // fault code and fault data fields should be enough for clustering, and the verbosity + // isn't worth it. Additionally, vendors may just set the general description field of + // the overall fault to the description of the first element in this list, and that + // overall description will be placed at the end of the crash string. + crashMsg << vendorInfo.vendorFaultCode << ":" << vendorInfo.vendorFaultData << ", "; + } + crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " + crashMsg << ")"; + } + + if (!vendorBinaryData.empty()) { + // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports + ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics" + " Stack team if you observe this message).", + vendorBinaryData.size()); + crashMsg << ", " << vendorBinaryData.size() << " bytes binary"; + } + + crashMsg << "): " << description; + LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str()); +}; + +static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { + if (device != VK_NULL_HANDLE) { + return vkGetDeviceProcAddr(device, proc_name); + } + return vkGetInstanceProcAddr(instance, proc_name); +}; + +#define BAIL(fmt, ...) \ + { \ + ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \ + return; \ + } + +#define CHECK_NONNULL(expr) \ + if ((expr) == nullptr) { \ + BAIL("[%s] null", #expr); \ + } + +#define VK_CHECK(expr) \ + if ((expr) != VK_SUCCESS) { \ + BAIL("[%s] failed. err = %d", #expr, expr); \ + return; \ + } + +#define VK_GET_PROC(F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \ + CHECK_NONNULL(vk##F) +#define VK_GET_INST_PROC(instance, F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \ + CHECK_NONNULL(vk##F) +#define VK_GET_DEV_PROC(device, F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \ + CHECK_NONNULL(vk##F) + +void VulkanInterface::init(bool protectedContent) { + if (isInitialized()) { + ALOGW("Called init on already initialized VulkanInterface"); + return; + } + + const nsecs_t timeBefore = systemTime(); + + VK_GET_PROC(EnumerateInstanceVersion); + uint32_t instanceVersion; + VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion)); + + if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) { + return; + } + + const VkApplicationInfo appInfo = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0, + VK_MAKE_VERSION(1, 1, 0), + }; + + VK_GET_PROC(EnumerateInstanceExtensionProperties); + + uint32_t extensionCount = 0; + VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr)); + std::vector<VkExtensionProperties> instanceExtensions(extensionCount); + VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, + instanceExtensions.data())); + std::vector<const char*> enabledInstanceExtensionNames; + enabledInstanceExtensionNames.reserve(instanceExtensions.size()); + mInstanceExtensionNames.reserve(instanceExtensions.size()); + for (const auto& instExt : instanceExtensions) { + enabledInstanceExtensionNames.push_back(instExt.extensionName); + mInstanceExtensionNames.push_back(instExt.extensionName); + } + + const VkInstanceCreateInfo instanceCreateInfo = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + nullptr, + 0, + &appInfo, + 0, + nullptr, + (uint32_t)enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), + }; + + VK_GET_PROC(CreateInstance); + VkInstance instance; + VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); + + VK_GET_INST_PROC(instance, DestroyInstance); + mFuncs.vkDestroyInstance = vkDestroyInstance; + VK_GET_INST_PROC(instance, EnumeratePhysicalDevices); + VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2); + VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties2); + VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2); + VK_GET_INST_PROC(instance, CreateDevice); + + uint32_t physdevCount; + VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr)); + if (physdevCount == 0) { + BAIL("Could not find any physical devices"); + } + + physdevCount = 1; + VkPhysicalDevice physicalDevice; + VkResult enumeratePhysDevsErr = + vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice); + if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) { + BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d", + enumeratePhysDevsErr); + } + + VkPhysicalDeviceProperties2 physDevProps = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + 0, + {}, + }; + VkPhysicalDeviceProtectedMemoryProperties protMemProps = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, + 0, + {}, + }; + + if (protectedContent) { + physDevProps.pNext = &protMemProps; + } + + vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps); + if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { + BAIL("Could not find a Vulkan 1.1+ physical device"); + } + + if (physDevProps.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) { + // TODO: b/326633110 - SkiaVK is not working correctly on swiftshader path. + BAIL("CPU implementations of Vulkan is not supported"); + } + + // Check for syncfd support. Bail if we cannot both import and export them. + VkPhysicalDeviceExternalSemaphoreInfo semInfo = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, + nullptr, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + VkExternalSemaphoreProperties semProps = { + VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0, + }; + vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps); + + bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes & + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && + (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && + (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) && + (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + + if (!sufficientSemaphoreSyncFdSupport) { + BAIL("Vulkan device does not support sufficient external semaphore sync fd features. " + "exportFromImportedHandleTypes 0x%x (needed 0x%x) " + "compatibleHandleTypes 0x%x (needed 0x%x) " + "externalSemaphoreFeatures 0x%x (needed 0x%x) ", + semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.externalSemaphoreFeatures, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + } else { + ALOGD("Vulkan device supports sufficient external semaphore sync fd features. " + "exportFromImportedHandleTypes 0x%x (needed 0x%x) " + "compatibleHandleTypes 0x%x (needed 0x%x) " + "externalSemaphoreFeatures 0x%x (needed 0x%x) ", + semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.externalSemaphoreFeatures, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + } + + uint32_t queueCount; + vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, nullptr); + if (queueCount == 0) { + BAIL("Could not find queues for physical device"); + } + + std::vector<VkQueueFamilyProperties2> queueProps(queueCount); + std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount); + VkQueueGlobalPriorityKHR queuePriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR; + // Even though we don't yet know if the VK_EXT_global_priority extension is available, + // we can safely add the request to the pNext chain, and if the extension is not + // available, it will be ignored. + for (uint32_t i = 0; i < queueCount; ++i) { + queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT; + queuePriorityProps[i].pNext = nullptr; + queueProps[i].pNext = &queuePriorityProps[i]; + } + vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, queueProps.data()); + + int graphicsQueueIndex = -1; + for (uint32_t i = 0; i < queueCount; ++i) { + // Look at potential answers to the VK_EXT_global_priority query. If answers were + // provided, we may adjust the queuePriority. + if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + for (uint32_t j = 0; j < queuePriorityProps[i].priorityCount; j++) { + if (queuePriorityProps[i].priorities[j] > queuePriority) { + queuePriority = queuePriorityProps[i].priorities[j]; + } + } + if (queuePriority == VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR) { + mIsRealtimePriority = true; + } + graphicsQueueIndex = i; + break; + } + } + + if (graphicsQueueIndex == -1) { + BAIL("Could not find a graphics queue family"); + } + + uint32_t deviceExtensionCount; + VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, + nullptr)); + std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount); + VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, + deviceExtensions.data())); + + std::vector<const char*> enabledDeviceExtensionNames; + enabledDeviceExtensionNames.reserve(deviceExtensions.size()); + mDeviceExtensionNames.reserve(deviceExtensions.size()); + for (const auto& devExt : deviceExtensions) { + enabledDeviceExtensionNames.push_back(devExt.extensionName); + mDeviceExtensionNames.push_back(devExt.extensionName); + } + + mGrExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data()); + + if (!mGrExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { + BAIL("Vulkan driver doesn't support external semaphore fd"); + } + + mPhysicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2; + mPhysicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + mPhysicalDeviceFeatures2->pNext = nullptr; + + mSamplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures; + mSamplerYcbcrConversionFeatures->sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + mSamplerYcbcrConversionFeatures->pNext = nullptr; + + mPhysicalDeviceFeatures2->pNext = mSamplerYcbcrConversionFeatures; + void** tailPnext = &mSamplerYcbcrConversionFeatures->pNext; + + if (protectedContent) { + mProtectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryFeatures; + mProtectedMemoryFeatures->sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; + mProtectedMemoryFeatures->pNext = nullptr; + *tailPnext = mProtectedMemoryFeatures; + tailPnext = &mProtectedMemoryFeatures->pNext; + } + + if (mGrExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { + mDeviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT; + mDeviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; + mDeviceFaultFeatures->pNext = nullptr; + *tailPnext = mDeviceFaultFeatures; + tailPnext = &mDeviceFaultFeatures->pNext; + } + + vkGetPhysicalDeviceFeatures2(physicalDevice, mPhysicalDeviceFeatures2); + // Looks like this would slow things down and we can't depend on it on all platforms + mPhysicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; + + if (protectedContent && !mProtectedMemoryFeatures->protectedMemory) { + BAIL("Protected memory not supported"); + } + + float queuePriorities[1] = {0.0f}; + void* queueNextPtr = nullptr; + + VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT, + nullptr, + // If queue priority is supported, RE should always have realtime priority. + queuePriority, + }; + + if (mGrExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { + queueNextPtr = &queuePriorityCreateInfo; + } + + VkDeviceQueueCreateFlags deviceQueueCreateFlags = + (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0); + + const VkDeviceQueueCreateInfo queueInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + queueNextPtr, + deviceQueueCreateFlags, + (uint32_t)graphicsQueueIndex, + 1, + queuePriorities, + }; + + const VkDeviceCreateInfo deviceInfo = { + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + mPhysicalDeviceFeatures2, + 0, + 1, + &queueInfo, + 0, + nullptr, + (uint32_t)enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data(), + nullptr, + }; + + ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent); + VkDevice device; + VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device)); + ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent); + + VkQueue graphicsQueue; + VK_GET_DEV_PROC(device, GetDeviceQueue2); + const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr, + deviceQueueCreateFlags, + (uint32_t)graphicsQueueIndex, 0}; + vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue); + + VK_GET_DEV_PROC(device, DeviceWaitIdle); + VK_GET_DEV_PROC(device, DestroyDevice); + mFuncs.vkDeviceWaitIdle = vkDeviceWaitIdle; + mFuncs.vkDestroyDevice = vkDestroyDevice; + + VK_GET_DEV_PROC(device, CreateSemaphore); + VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR); + VK_GET_DEV_PROC(device, GetSemaphoreFdKHR); + VK_GET_DEV_PROC(device, DestroySemaphore); + mFuncs.vkCreateSemaphore = vkCreateSemaphore; + mFuncs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR; + mFuncs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR; + mFuncs.vkDestroySemaphore = vkDestroySemaphore; + + // At this point, everything's succeeded and we can continue + mInitialized = true; + mInstance = instance; + mPhysicalDevice = physicalDevice; + mDevice = device; + mQueue = graphicsQueue; + mQueueIndex = graphicsQueueIndex; + mApiVersion = physDevProps.properties.apiVersion; + // grExtensions already constructed + // feature pointers already constructed + mGrGetProc = sGetProc; + mIsProtected = protectedContent; + // mIsRealtimePriority already initialized by constructor + // funcs already initialized + + const nsecs_t timeAfter = systemTime(); + const float initTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; + ALOGD("%s: Success init Vulkan interface in %f ms", __func__, initTimeMs); +} + +// TODO: b/293371537 - Iterate on this. +// Currently unused, but copied over from its original location for potential future use. This +// should likely be improved to walk the pNext chain of mPhysicalDeviceFeatures2 and free everything +// like HWUI's VulkanManager. Also, not all fields are being reset. +void VulkanInterface::teardown() { + mInitialized = false; + + if (mDevice != VK_NULL_HANDLE) { + mFuncs.vkDeviceWaitIdle(mDevice); + mFuncs.vkDestroyDevice(mDevice, nullptr); + mDevice = VK_NULL_HANDLE; + } + if (mInstance != VK_NULL_HANDLE) { + mFuncs.vkDestroyInstance(mInstance, nullptr); + mInstance = VK_NULL_HANDLE; + } + + if (mProtectedMemoryFeatures) { + delete mProtectedMemoryFeatures; + } + + if (mSamplerYcbcrConversionFeatures) { + delete mSamplerYcbcrConversionFeatures; + } + + if (mPhysicalDeviceFeatures2) { + delete mPhysicalDeviceFeatures2; + } + + if (mDeviceFaultFeatures) { + delete mDeviceFaultFeatures; + } + + mSamplerYcbcrConversionFeatures = nullptr; + mPhysicalDeviceFeatures2 = nullptr; + mProtectedMemoryFeatures = nullptr; + mDeviceFaultFeatures = nullptr; +} + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h new file mode 100644 index 0000000000..c3936d9869 --- /dev/null +++ b/libs/renderengine/skia/VulkanInterface.h @@ -0,0 +1,95 @@ +/* + * Copyright 2024 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 <include/gpu/vk/GrVkBackendContext.h> +#include <include/gpu/vk/GrVkExtensions.h> + +#include <vulkan/vulkan.h> + +using namespace skgpu; + +namespace android { +namespace renderengine { +namespace skia { + +class VulkanInterface { +public: + // Create an uninitialized interface. Initialize with `init`. + VulkanInterface() = default; + ~VulkanInterface() = default; + VulkanInterface(const VulkanInterface&) = delete; + VulkanInterface& operator=(const VulkanInterface&) = delete; + VulkanInterface& operator=(VulkanInterface&&) = delete; + + void init(bool protectedContent = false); + void teardown(); + + // TODO: b/293371537 - Graphite variant (external/skia/include/gpu/vk/VulkanBackendContext.h) + GrVkBackendContext getBackendContext(); + VkSemaphore createExportableSemaphore(); + VkSemaphore importSemaphoreFromSyncFd(int syncFd); + int exportSemaphoreSyncFd(VkSemaphore semaphore); + void destroySemaphore(VkSemaphore semaphore); + + bool isInitialized() const { return mInitialized; } + bool isRealtimePriority() const { return mIsRealtimePriority; } + const std::vector<std::string>& getInstanceExtensionNames() { return mInstanceExtensionNames; } + const std::vector<std::string>& getDeviceExtensionNames() { return mDeviceExtensionNames; } + +private: + struct VulkanFuncs { + PFN_vkCreateSemaphore vkCreateSemaphore = nullptr; + PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr; + PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr; + PFN_vkDestroySemaphore vkDestroySemaphore = nullptr; + + PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr; + PFN_vkDestroyDevice vkDestroyDevice = nullptr; + PFN_vkDestroyInstance vkDestroyInstance = nullptr; + }; + + static void onVkDeviceFault(void* callbackContext, const std::string& description, + const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, + const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, + const std::vector<std::byte>& vendorBinaryData); + + bool mInitialized = false; + VkInstance mInstance = VK_NULL_HANDLE; + VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; + VkDevice mDevice = VK_NULL_HANDLE; + VkQueue mQueue = VK_NULL_HANDLE; + int mQueueIndex = 0; + uint32_t mApiVersion = 0; + GrVkExtensions mGrExtensions; + VkPhysicalDeviceFeatures2* mPhysicalDeviceFeatures2 = nullptr; + VkPhysicalDeviceSamplerYcbcrConversionFeatures* mSamplerYcbcrConversionFeatures = nullptr; + VkPhysicalDeviceProtectedMemoryFeatures* mProtectedMemoryFeatures = nullptr; + VkPhysicalDeviceFaultFeaturesEXT* mDeviceFaultFeatures = nullptr; + GrVkGetProc mGrGetProc = nullptr; + bool mIsProtected = false; + bool mIsRealtimePriority = false; + + VulkanFuncs mFuncs; + + std::vector<std::string> mInstanceExtensionNames; + std::vector<std::string> mDeviceExtensionNames; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index 5c9820cdc5..09f09a697a 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -111,7 +111,7 @@ sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, constexpr int kSampleCount = 1; constexpr bool kMipmapped = false; constexpr SkSurfaceProps* kProps = nullptr; - sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, scaledInfo, + sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kNo, scaledInfo, kSampleCount, kTopLeft_GrSurfaceOrigin, kProps, kMipmapped, input->isProtected()); LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__); diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 50e166d2a7..0eea187407 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { @@ -44,6 +45,7 @@ cc_test { "librenderengine_mocks", "libshaders", "libtonemap", + "libsurfaceflinger_common", ], header_libs: [ "libtonemap_headers", @@ -61,5 +63,6 @@ cc_test { "libsync", "libui", "libutils", + "server_configurable_flags", ], } diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 11d4fdebdc..7b8eb8470f 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -106,25 +106,9 @@ public: virtual ~RenderEngineFactory() = default; virtual std::string name() = 0; - virtual renderengine::RenderEngine::RenderEngineType type() = 0; - virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0; - virtual bool typeSupported() = 0; -}; - -class SkiaVkRenderEngineFactory : public RenderEngineFactory { -public: - std::string name() override { return "SkiaVkRenderEngineFactory"; } - - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_VK; - } - - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - std::unique_ptr<renderengine::RenderEngine> re = createSkiaVkRenderEngine(); - return re; - } - - std::unique_ptr<renderengine::skia::SkiaVkRenderEngine> createSkiaVkRenderEngine() { + virtual renderengine::RenderEngine::GraphicsApi graphicsApi() = 0; + bool apiSupported() { return renderengine::RenderEngine::canSupport(graphicsApi()); } + std::unique_ptr<renderengine::RenderEngine> createRenderEngine() { renderengine::RenderEngineCreationArgs reCreationArgs = renderengine::RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) @@ -133,65 +117,29 @@ public: .setPrecacheToneMapperShaderOnly(false) .setSupportsBackgroundBlur(true) .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) + .setThreaded(renderengine::RenderEngine::Threaded::NO) + .setGraphicsApi(graphicsApi()) .build(); - return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs); + return renderengine::RenderEngine::create(reCreationArgs); } - - bool typeSupported() override { - return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(); - } - void skip() { GTEST_SKIP(); } }; -class SkiaGLESRenderEngineFactory : public RenderEngineFactory { +class SkiaVkRenderEngineFactory : public RenderEngineFactory { public: - std::string name() override { return "SkiaGLRenderEngineFactory"; } - - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL; - } + std::string name() override { return "SkiaVkRenderEngineFactory"; } - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - renderengine::RenderEngineCreationArgs reCreationArgs = - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) - .build(); - return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); + renderengine::RenderEngine::GraphicsApi graphicsApi() override { + return renderengine::RenderEngine::GraphicsApi::VK; } - - bool typeSupported() override { return true; } }; -class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory { +class SkiaGLESRenderEngineFactory : public RenderEngineFactory { public: - std::string name() override { return "SkiaGLCMRenderEngineFactory"; } + std::string name() override { return "SkiaGLRenderEngineFactory"; } - renderengine::RenderEngine::RenderEngineType type() { - return renderengine::RenderEngine::RenderEngineType::SKIA_GL; + renderengine::RenderEngine::GraphicsApi graphicsApi() { + return renderengine::RenderEngine::GraphicsApi::GL; } - - std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override { - renderengine::RenderEngineCreationArgs reCreationArgs = - renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .setRenderEngineType(type()) - .build(); - return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); - } - - bool typeSupported() override { return true; } }; class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> { @@ -1526,7 +1474,7 @@ INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, std::make_shared<SkiaVkRenderEngineFactory>())); TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1534,7 +1482,7 @@ TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { } TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1561,7 +1509,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) { } TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1595,7 +1543,7 @@ TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { } TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1616,7 +1564,7 @@ TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1624,7 +1572,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1632,7 +1580,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1640,7 +1588,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1648,7 +1596,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1656,7 +1604,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1664,7 +1612,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1672,7 +1620,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1680,7 +1628,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1688,7 +1636,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1696,7 +1644,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1706,7 +1654,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1717,7 +1665,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1726,7 +1674,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1734,7 +1682,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1742,7 +1690,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_color } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1750,7 +1698,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1758,7 +1706,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) } TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1766,7 +1714,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1774,7 +1722,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1782,7 +1730,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1790,7 +1738,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1798,7 +1746,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1806,7 +1754,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1814,7 +1762,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1822,7 +1770,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSourc } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1830,7 +1778,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1838,7 +1786,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1846,7 +1794,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1856,7 +1804,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1867,7 +1815,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_o TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -1876,7 +1824,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_o } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1884,7 +1832,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1892,7 +1840,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqu } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1900,7 +1848,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSour } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1908,7 +1856,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBuffer } TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1916,7 +1864,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1924,7 +1872,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1932,7 +1880,7 @@ TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1940,7 +1888,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1948,7 +1896,7 @@ TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1956,7 +1904,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1964,7 +1912,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1972,7 +1920,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1980,7 +1928,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1988,7 +1936,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -1996,7 +1944,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2006,7 +1954,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -2017,7 +1965,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_b TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported()) { + if (!renderEngineFactory->apiSupported()) { GTEST_SKIP(); } @@ -2026,7 +1974,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_b } TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2034,7 +1982,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2042,7 +1990,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_buffe } TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2050,7 +1998,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2058,7 +2006,7 @@ TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource } TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2066,7 +2014,7 @@ TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { } TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2074,7 +2022,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) { } TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2082,7 +2030,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { } TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2090,7 +2038,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2108,7 +2056,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2131,7 +2079,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2155,7 +2103,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2180,7 +2128,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2206,7 +2154,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) { } TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2235,7 +2183,7 @@ TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) { } TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2271,7 +2219,7 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { if (mRE->canSkipPostRenderCleanup()) { // Skia's Vk backend may keep the texture alive beyond drawLayersInternal, so // it never gets added to the cleanup list. In those cases, we can skip. - EXPECT_TRUE(GetParam()->type() == renderengine::RenderEngine::RenderEngineType::SKIA_VK); + EXPECT_TRUE(GetParam()->graphicsApi() == renderengine::RenderEngine::GraphicsApi::VK); } else { mRE->cleanupPostRender(); EXPECT_TRUE(mRE->canSkipPostRenderCleanup()); @@ -2279,7 +2227,7 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { } TEST_P(RenderEngineTest, testRoundedCornersCrop) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2332,7 +2280,7 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2380,7 +2328,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { } TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2417,7 +2365,7 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { } TEST_P(RenderEngineTest, testRoundedCornersXY) { - if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2460,7 +2408,7 @@ TEST_P(RenderEngineTest, testRoundedCornersXY) { } TEST_P(RenderEngineTest, testClear) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2492,7 +2440,7 @@ TEST_P(RenderEngineTest, testClear) { } TEST_P(RenderEngineTest, testDisableBlendingBuffer) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2543,7 +2491,7 @@ TEST_P(RenderEngineTest, testDisableBlendingBuffer) { } TEST_P(RenderEngineTest, testBorder) { - if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2588,7 +2536,7 @@ TEST_P(RenderEngineTest, testBorder) { } TEST_P(RenderEngineTest, testDimming) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2663,7 +2611,7 @@ TEST_P(RenderEngineTest, testDimming) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2741,7 +2689,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2804,7 +2752,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) { } TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_deviceHandles) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2868,7 +2816,7 @@ TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_devi } TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2922,7 +2870,7 @@ TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) { } TEST_P(RenderEngineTest, test_isOpaque) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -2972,7 +2920,7 @@ TEST_P(RenderEngineTest, test_isOpaque) { } TEST_P(RenderEngineTest, test_tonemapPQMatches) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -2989,7 +2937,7 @@ TEST_P(RenderEngineTest, test_tonemapPQMatches) { } TEST_P(RenderEngineTest, test_tonemapHLGMatches) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } @@ -3006,7 +2954,7 @@ TEST_P(RenderEngineTest, test_tonemapHLGMatches) { } TEST_P(RenderEngineTest, r8_behaves_as_mask) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3066,7 +3014,7 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { } TEST_P(RenderEngineTest, r8_respects_color_transform) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3131,7 +3079,7 @@ TEST_P(RenderEngineTest, r8_respects_color_transform) { } TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); @@ -3199,7 +3147,7 @@ TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { } TEST_P(RenderEngineTest, primeShaderCache) { - if (!GetParam()->typeSupported()) { + if (!GetParam()->apiSupported()) { GTEST_SKIP(); } initializeRenderEngine(); diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp index 1b9adba063..d56dbb2a7b 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -35,8 +35,7 @@ struct RenderEngineThreadedTest : public ::testing::Test { void SetUp() override { mThreadedRE = renderengine::threaded::RenderEngineThreaded::create( - [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }, - renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED); + [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }); } std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE; diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index 367bee89f9..f4cebc05ec 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -33,13 +33,12 @@ namespace android { namespace renderengine { namespace threaded { -std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory, - RenderEngineType type) { - return std::make_unique<RenderEngineThreaded>(std::move(factory), type); +std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) { + return std::make_unique<RenderEngineThreaded>(std::move(factory)); } -RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type) - : RenderEngine(type) { +RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) + : RenderEngine(Threaded::YES) { ATRACE_CALL(); std::lock_guard lockThread(mThreadMutex); @@ -125,8 +124,10 @@ void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_S } void RenderEngineThreaded::waitUntilInitialized() const { - std::unique_lock<std::mutex> lock(mInitializedMutex); - mInitializedCondition.wait(lock, [=] { return mIsInitialized; }); + if (!mIsInitialized) { + std::unique_lock<std::mutex> lock(mInitializedMutex); + mInitializedCondition.wait(lock, [this] { return mIsInitialized.load(); }); + } } std::future<void> RenderEngineThreaded::primeCache(bool shouldPrimeUltraHDR) { diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index 74af2bd776..d440c961e7 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -37,10 +37,9 @@ using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::Render */ class RenderEngineThreaded : public RenderEngine { public: - static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory, - RenderEngineType type); + static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory); - RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type); + RenderEngineThreaded(CreateInstanceFactory factory); ~RenderEngineThreaded() override; std::future<void> primeCache(bool shouldPrimeUltraHDR) override; @@ -97,7 +96,7 @@ private: // Used to allow select thread safe methods to be accessed without requiring the // method to be invoked on the RenderEngine thread - bool mIsInitialized = false; + std::atomic_bool mIsInitialized = false; mutable std::mutex mInitializedMutex; mutable std::condition_variable mInitializedCondition; diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index d992aa5105..cc92bc30a0 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -21,6 +21,18 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "libsensor_flags", + package: "com.android.hardware.libsensor.flags", + srcs: ["libsensor_flags.aconfig"], +} + +cc_aconfig_library { + name: "libsensor_flags_c_lib", + host_supported: true, + aconfig_declarations: "libsensor_flags", +} + cc_library { name: "libsensor", @@ -52,6 +64,10 @@ cc_library { "android.companion.virtual.virtualdevice_aidl-cpp", ], + static_libs: [ + "libsensor_flags_c_lib", + ], + export_include_dirs: ["include"], export_shared_lib_headers: [ diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index d112a1265c..9411e204e9 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -37,6 +37,8 @@ #include <sensor/Sensor.h> #include <sensor/SensorEventQueue.h> +#include <com_android_hardware_libsensor_flags.h> + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -88,49 +90,51 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) SensorManager* sensorManager; auto iterator = sPackageInstances.find(packageName); + const uid_t uid = IPCThreadState::self()->getCallingUid(); + const int deviceId = getDeviceIdForUid(uid); + + // Return the cached instance if the device association of the package has not changed. if (iterator != sPackageInstances.end()) { sensorManager = iterator->second; - } else { - String16 opPackageName = packageName; - const uid_t uid = IPCThreadState::self()->getCallingUid(); - - // It is possible that the calling code has no access to the package name. - // In this case we will get the packages for the calling UID and pick the - // first one for attributing the app op. This will work correctly for - // runtime permissions as for legacy apps we will toggle the app op for - // all packages in the UID. The caveat is that the operation may be attributed - // to the wrong package and stats based on app ops may be slightly off. - if (opPackageName.size() <= 0) { - sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != nullptr) { - Vector<String16> packages; - interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); - if (!packages.isEmpty()) { - opPackageName = packages[0]; - } else { - ALOGE("No packages for calling UID"); - } + if (sensorManager->mDeviceId == deviceId) { + return *sensorManager; + } + } + + // It is possible that the calling code has no access to the package name. + // In this case we will get the packages for the calling UID and pick the + // first one for attributing the app op. This will work correctly for + // runtime permissions as for legacy apps we will toggle the app op for + // all packages in the UID. The caveat is that the operation may be attributed + // to the wrong package and stats based on app ops may be slightly off. + String16 opPackageName = packageName; + if (opPackageName.size() <= 0) { + sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); + if (binder != nullptr) { + Vector<String16> packages; + interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); + if (!packages.isEmpty()) { + opPackageName = packages[0]; } else { - ALOGE("Cannot get permission service"); + ALOGE("No packages for calling UID"); } + } else { + ALOGE("Cannot get permission service"); } + } - // Check if the calling UID is observed on a virtual device. If so, provide that device's - // sensors by default instead of the default device's sensors. - const int deviceId = getDeviceIdForUid(uid); - sensorManager = new SensorManager(opPackageName, deviceId); - - // If we had no package name, we looked it up from the UID and the sensor - // manager instance we created should also be mapped to the empty package - // name, to avoid looking up the packages for a UID and get the same result. - if (packageName.size() <= 0) { - sPackageInstances.insert(std::make_pair(String16(), sensorManager)); - } + sensorManager = new SensorManager(opPackageName, deviceId); - // Stash the per package sensor manager. - sPackageInstances.insert(std::make_pair(opPackageName, sensorManager)); + // If we had no package name, we looked it up from the UID and the sensor + // manager instance we created should also be mapped to the empty package + // name, to avoid looking up the packages for a UID and get the same result. + if (packageName.size() <= 0) { + sPackageInstances.insert(std::make_pair(String16(), sensorManager)); } + // Stash the per package sensor manager. + sPackageInstances.insert(std::make_pair(opPackageName, sensorManager)); + return *sensorManager; } @@ -190,6 +194,9 @@ void SensorManager::sensorManagerDied() { } status_t SensorManager::assertStateLocked() { +#if COM_ANDROID_HARDWARE_LIBSENSOR_FLAGS(SENSORMANAGER_PING_BINDER) + if (mSensorServer == nullptr) { +#else bool initSensorManager = false; if (mSensorServer == nullptr) { initSensorManager = true; @@ -201,6 +208,7 @@ status_t SensorManager::assertStateLocked() { } } if (initSensorManager) { +#endif waitForSensorService(&mSensorServer); LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL"); @@ -248,6 +256,22 @@ ssize_t SensorManager::getSensorList(Sensor const* const** list) { return static_cast<ssize_t>(mSensors.size()); } +ssize_t SensorManager::getDefaultDeviceSensorList(Vector<Sensor> & list) { + Mutex::Autolock _l(mLock); + status_t err = assertStateLocked(); + if (err < 0) { + return static_cast<ssize_t>(err); + } + + if (mDeviceId == DEVICE_ID_DEFAULT) { + list = mSensors; + } else { + list = mSensorServer->getSensorList(mOpPackageName); + } + + return static_cast<ssize_t>(list.size()); +} + ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) { Mutex::Autolock _l(mLock); status_t err = assertStateLocked(); diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index e67fac7617..49f050a0ac 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h @@ -58,6 +58,7 @@ public: ~SensorManager(); ssize_t getSensorList(Sensor const* const** list); + ssize_t getDefaultDeviceSensorList(Vector<Sensor> & list); ssize_t getDynamicSensorList(Vector<Sensor>& list); ssize_t getDynamicSensorList(Sensor const* const** list); ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list); diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig new file mode 100644 index 0000000000..ef4d7370b5 --- /dev/null +++ b/libs/sensor/libsensor_flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.hardware.libsensor.flags" + +flag { + name: "sensormanager_ping_binder" + namespace: "sensors" + description: "Whether to pingBinder on SensorManager init" + bug: "322228259" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp index 57c74ee565..3f3ad9343c 100644 --- a/libs/sensorprivacy/SensorPrivacyManager.cpp +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -108,7 +108,7 @@ bool SensorPrivacyManager::isSensorPrivacyEnabled() bool SensorPrivacyManager::isToggleSensorPrivacyEnabled(int sensor) { - sp<hardware::ISensorPrivacyManager> service = getService(); + sp<hardware::ISensorPrivacyManager> service = getService(); if (service != nullptr) { bool result; service->isCombinedToggleSensorPrivacyEnabled(sensor, &result); @@ -143,6 +143,38 @@ status_t SensorPrivacyManager::isToggleSensorPrivacyEnabled(int toggleType, int return UNKNOWN_ERROR; } +int SensorPrivacyManager::getToggleSensorPrivacyState(int toggleType, int sensor) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + int result; + service->getToggleSensorPrivacyState(toggleType, sensor, &result); + return result; + } + // if the SensorPrivacyManager is not available then assume sensor privacy is disabled + return DISABLED; +} + +std::vector<String16> SensorPrivacyManager::getCameraPrivacyAllowlist(){ + sp<hardware::ISensorPrivacyManager> service = getService(); + std::vector<String16> result; + if (service != nullptr) { + service->getCameraPrivacyAllowlist(&result); + return result; + } + return result; +} + +bool SensorPrivacyManager::isCameraPrivacyEnabled(String16 packageName){ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + bool result; + service->isCameraPrivacyEnabled(packageName, &result); + return result; + } + return false; +} + status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<hardware::ISensorPrivacyManager> service = getService(); diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl index eccd54c3eb..694af00a87 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl @@ -21,4 +21,5 @@ package android.hardware; */ oneway interface ISensorPrivacyListener { void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled); + void onSensorPrivacyStateChanged(int toggleType, int sensor, int state); } diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl index 49a1e1ea05..f7071872bf 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl @@ -41,4 +41,15 @@ interface ISensorPrivacyManager { void setToggleSensorPrivacy(int userId, int source, int sensor, boolean enable); void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable); + + List<String> getCameraPrivacyAllowlist(); + + int getToggleSensorPrivacyState(int toggleType, int sensor); + + void setToggleSensorPrivacyState(int userId, int source, int sensor, int state); + + void setToggleSensorPrivacyStateForProfileGroup(int userId, int source, int sensor, int state); + + boolean isCameraPrivacyEnabled(String packageName); + } diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h index fc5fdf7900..8935b76adc 100644 --- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h @@ -32,12 +32,20 @@ class SensorPrivacyManager public: enum { TOGGLE_SENSOR_MICROPHONE = 1, - TOGGLE_SENSOR_CAMERA = 2 + TOGGLE_SENSOR_CAMERA = 2, + TOGGLE_SENSOR_UNKNOWN = -1 }; enum { TOGGLE_TYPE_SOFTWARE = 1, - TOGGLE_TYPE_HARDWARE = 2 + TOGGLE_TYPE_HARDWARE = 2, + TOGGLE_TYPE_UNKNOWN = -1 + }; + + enum { + ENABLED = 1, + DISABLED = 2, + ENABLED_EXCEPT_ALLOWLISTED_APPS = 3 }; SensorPrivacyManager(); @@ -51,6 +59,9 @@ public: bool isToggleSensorPrivacyEnabled(int sensor); bool isToggleSensorPrivacyEnabled(int toggleType, int sensor); status_t isToggleSensorPrivacyEnabled(int toggleType, int sensor, bool &result); + int getToggleSensorPrivacyState(int toggleType, int sensor); + std::vector<String16> getCameraPrivacyAllowlist(); + bool isCameraPrivacyEnabled(String16 packageName); status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp index 960f845488..82fbfda0da 100644 --- a/libs/shaders/Android.bp +++ b/libs/shaders/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library_static { diff --git a/libs/shaders/tests/Android.bp b/libs/shaders/tests/Android.bp index 5639d744df..2103679efe 100644 --- a/libs/shaders/tests/Android.bp +++ b/libs/shaders/tests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp index 8c8815d0e4..d569ac3ba6 100644 --- a/libs/tonemap/Android.bp +++ b/libs/tonemap/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_library_static { diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp index 5c5fc6c8b3..79ee7c2de9 100644 --- a/libs/tonemap/tests/Android.bp +++ b/libs/tonemap/tests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { diff --git a/libs/tracing_perfetto/.clang-format b/libs/tracing_perfetto/.clang-format new file mode 100644 index 0000000000..f3974548f6 --- /dev/null +++ b/libs/tracing_perfetto/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: Google +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: false + +ColumnLimit: 80 +ContinuationIndentWidth: 4 +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +IndentWidth: 2 +PointerAlignment: Left +UseTab: Never +PenaltyExcessCharacter: 32
\ No newline at end of file diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp new file mode 100644 index 0000000000..3a4c869e46 --- /dev/null +++ b/libs/tracing_perfetto/Android.bp @@ -0,0 +1,49 @@ +// Copyright 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // 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"], +} + +cc_library_shared { + name: "libtracing_perfetto", + export_include_dirs: [ + "include", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wno-enum-compare", + "-Wno-unused-function", + ], + + srcs: [ + "tracing_perfetto.cpp", + "tracing_perfetto_internal.cpp", + ], + + shared_libs: [ + "libcutils", + "libperfetto_c", + "android.os.flags-aconfig-cc-host", + ], + + host_supported: true, +} diff --git a/libs/tracing_perfetto/OWNERS b/libs/tracing_perfetto/OWNERS new file mode 100644 index 0000000000..e2d4b4636a --- /dev/null +++ b/libs/tracing_perfetto/OWNERS @@ -0,0 +1,2 @@ +zezeozue@google.com +biswarupp@google.com
\ No newline at end of file diff --git a/libs/tracing_perfetto/include/trace_categories.h b/libs/tracing_perfetto/include/trace_categories.h new file mode 100644 index 0000000000..6d4168b772 --- /dev/null +++ b/libs/tracing_perfetto/include/trace_categories.h @@ -0,0 +1,65 @@ +/* + * Copyright 2024 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 TRACE_CATEGORIES_H +#define TRACE_CATEGORIES_H + +/** + * Keep these in sync with frameworks/base/core/java/android/os/Trace.java. + */ +#define TRACE_CATEGORY_ALWAYS (1 << 0) +#define TRACE_CATEGORY_GRAPHICS (1 << 1) +#define TRACE_CATEGORY_INPUT (1 << 2) +#define TRACE_CATEGORY_VIEW (1 << 3) +#define TRACE_CATEGORY_WEBVIEW (1 << 4) +#define TRACE_CATEGORY_WINDOW_MANAGER (1 << 5) +#define TRACE_CATEGORY_ACTIVITY_MANAGER (1 << 6) +#define TRACE_CATEGORY_SYNC_MANAGER (1 << 7) +#define TRACE_CATEGORY_AUDIO (1 << 8) +#define TRACE_CATEGORY_VIDEO (1 << 9) +#define TRACE_CATEGORY_CAMERA (1 << 10) +#define TRACE_CATEGORY_HAL (1 << 11) +#define TRACE_CATEGORY_APP (1 << 12) +#define TRACE_CATEGORY_RESOURCES (1 << 13) +#define TRACE_CATEGORY_DALVIK (1 << 14) +#define TRACE_CATEGORY_RS (1 << 15) +#define TRACE_CATEGORY_BIONIC (1 << 16) +#define TRACE_CATEGORY_POWER (1 << 17) +#define TRACE_CATEGORY_PACKAGE_MANAGER (1 << 18) +#define TRACE_CATEGORY_SYSTEM_SERVER (1 << 19) +#define TRACE_CATEGORY_DATABASE (1 << 20) +#define TRACE_CATEGORY_NETWORK (1 << 21) +#define TRACE_CATEGORY_ADB (1 << 22) +#define TRACE_CATEGORY_VIBRATOR (1 << 23) +#define TRACE_CATEGORY_AIDL (1 << 24) +#define TRACE_CATEGORY_NNAPI (1 << 25) +#define TRACE_CATEGORY_RRO (1 << 26) +#define TRACE_CATEGORY_THERMAL (1 << 27) + +// Allow all categories except TRACE_CATEGORY_APP +#define TRACE_CATEGORIES \ + TRACE_CATEGORY_ALWAYS | TRACE_CATEGORY_GRAPHICS | TRACE_CATEGORY_INPUT | \ + TRACE_CATEGORY_VIEW | TRACE_CATEGORY_WEBVIEW | \ + TRACE_CATEGORY_WINDOW_MANAGER | TRACE_CATEGORY_ACTIVITY_MANAGER | \ + TRACE_CATEGORY_SYNC_MANAGER | TRACE_CATEGORY_AUDIO | \ + TRACE_CATEGORY_VIDEO | TRACE_CATEGORY_CAMERA | TRACE_CATEGORY_HAL | \ + TRACE_CATEGORY_RESOURCES | TRACE_CATEGORY_DALVIK | TRACE_CATEGORY_RS | \ + TRACE_CATEGORY_BIONIC | TRACE_CATEGORY_POWER | \ + TRACE_CATEGORY_PACKAGE_MANAGER | TRACE_CATEGORY_SYSTEM_SERVER | \ + TRACE_CATEGORY_DATABASE | TRACE_CATEGORY_NETWORK | TRACE_CATEGORY_ADB | \ + TRACE_CATEGORY_VIBRATOR | TRACE_CATEGORY_AIDL | TRACE_CATEGORY_NNAPI | \ + TRACE_CATEGORY_RRO | TRACE_CATEGORY_THERMAL +#endif // TRACE_CATEGORIES_H diff --git a/libs/tracing_perfetto/include/trace_result.h b/libs/tracing_perfetto/include/trace_result.h new file mode 100644 index 0000000000..f7581fc0fb --- /dev/null +++ b/libs/tracing_perfetto/include/trace_result.h @@ -0,0 +1,30 @@ +/* + * Copyright 2024 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 TRACE_RESULT_H +#define TRACE_RESULT_H + +namespace tracing_perfetto { + +enum class Result { + SUCCESS, + NOT_SUPPORTED, + INVALID_INPUT, +}; + +} + +#endif // TRACE_RESULT_H diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h new file mode 100644 index 0000000000..4e3c83fca3 --- /dev/null +++ b/libs/tracing_perfetto/include/tracing_perfetto.h @@ -0,0 +1,53 @@ +/* + * Copyright 2024 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 TRACING_PERFETTO_H +#define TRACING_PERFETTO_H + +#include <stdint.h> + +#include "trace_result.h" + +namespace tracing_perfetto { + +void registerWithPerfetto(bool test = false); + +Result traceBegin(uint64_t category, const char* name); + +Result traceEnd(uint64_t category); + +Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie); + +Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie); + +Result traceAsyncBeginForTrack(uint64_t category, const char* name, + const char* trackName, int32_t cookie); + +Result traceAsyncEndForTrack(uint64_t category, const char* trackName, + int32_t cookie); + +Result traceInstant(uint64_t category, const char* name); + +Result traceInstantForTrack(uint64_t category, const char* trackName, + const char* name); + +Result traceCounter(uint64_t category, const char* name, int64_t value); + +uint64_t getEnabledCategories(); + +} // namespace tracing_perfetto + +#endif // TRACING_PERFETTO_H diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp new file mode 100644 index 0000000000..a35b0e0c83 --- /dev/null +++ b/libs/tracing_perfetto/tests/Android.bp @@ -0,0 +1,45 @@ +// Copyright 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // 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"], +} + +cc_test { + name: "libtracing_perfetto_tests", + static_libs: [ + "libflagtest", + "libgmock", + ], + cflags: [ + "-Wall", + "-Werror", + ], + shared_libs: [ + "android.os.flags-aconfig-cc-host", + "libbase", + "libperfetto_c", + "libtracing_perfetto", + ], + srcs: [ + "tracing_perfetto_test.cpp", + "utils.cpp", + ], + test_suites: ["device-tests"], +} diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp new file mode 100644 index 0000000000..7716b9a316 --- /dev/null +++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2024 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 "tracing_perfetto.h" + +#include <thread> + +#include <android_os.h> +#include <flag_macros.h> + +#include "gtest/gtest.h" +#include "perfetto/public/abi/data_source_abi.h" +#include "perfetto/public/abi/heap_buffer.h" +#include "perfetto/public/abi/pb_decoder_abi.h" +#include "perfetto/public/abi/tracing_session_abi.h" +#include "perfetto/public/abi/track_event_abi.h" +#include "perfetto/public/data_source.h" +#include "perfetto/public/pb_decoder.h" +#include "perfetto/public/producer.h" +#include "perfetto/public/protos/config/trace_config.pzc.h" +#include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h" +#include "perfetto/public/protos/trace/test_event.pzc.h" +#include "perfetto/public/protos/trace/trace.pzc.h" +#include "perfetto/public/protos/trace/trace_packet.pzc.h" +#include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h" +#include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h" +#include "perfetto/public/protos/trace/track_event/track_event.pzc.h" +#include "perfetto/public/protos/trace/trigger.pzc.h" +#include "perfetto/public/te_category_macros.h" +#include "perfetto/public/te_macros.h" +#include "perfetto/public/track_event.h" +#include "trace_categories.h" +#include "utils.h" + +namespace tracing_perfetto { + +using ::perfetto::shlib::test_utils::AllFieldsWithId; +using ::perfetto::shlib::test_utils::FieldView; +using ::perfetto::shlib::test_utils::IdFieldView; +using ::perfetto::shlib::test_utils::MsgField; +using ::perfetto::shlib::test_utils::PbField; +using ::perfetto::shlib::test_utils::StringField; +using ::perfetto::shlib::test_utils::TracingSession; +using ::perfetto::shlib::test_utils::VarIntField; +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::UnorderedElementsAre; + +const auto PERFETTO_SDK_TRACING = ACONFIG_FLAG(android::os, perfetto_sdk_tracing); + +class TracingPerfettoTest : public testing::Test { + protected: + void SetUp() override { + tracing_perfetto::registerWithPerfetto(true /* test */); + } +}; + +// TODO(b/303199244): Add tests for all the library functions. + +TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstant, + REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) { + TracingSession tracing_session = + TracingSession::Builder().set_data_source_name("track_event").Build(); + tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, ""); + + tracing_session.StopBlocking(); + std::vector<uint8_t> data = tracing_session.ReadBlocking(); + bool found = false; + for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { + ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, + MsgField(_))); + IdFieldView track_event( + trace_field, perfetto_protos_TracePacket_track_event_field_number); + if (track_event.size() == 0) { + continue; + } + found = true; + IdFieldView cat_iid_fields( + track_event.front(), + perfetto_protos_TrackEvent_category_iids_field_number); + ASSERT_THAT(cat_iid_fields, ElementsAre(VarIntField(_))); + uint64_t cat_iid = cat_iid_fields.front().value.integer64; + EXPECT_THAT( + trace_field, + AllFieldsWithId( + perfetto_protos_TracePacket_interned_data_field_number, + ElementsAre(AllFieldsWithId( + perfetto_protos_InternedData_event_categories_field_number, + ElementsAre(MsgField(UnorderedElementsAre( + PbField(perfetto_protos_EventCategory_iid_field_number, + VarIntField(cat_iid)), + PbField(perfetto_protos_EventCategory_name_field_number, + StringField("input"))))))))); + } + EXPECT_TRUE(found); +} + +} // namespace tracing_perfetto
\ No newline at end of file diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp new file mode 100644 index 0000000000..9c4202808a --- /dev/null +++ b/libs/tracing_perfetto/tests/utils.cpp @@ -0,0 +1,219 @@ +/* + * Copyright 2024 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. + */ + +// Copied from //external/perfetto/src/shared_lib/test/utils.cc + +#include "utils.h" + +#include "perfetto/public/abi/heap_buffer.h" +#include "perfetto/public/pb_msg.h" +#include "perfetto/public/pb_utils.h" +#include "perfetto/public/protos/config/data_source_config.pzc.h" +#include "perfetto/public/protos/config/trace_config.pzc.h" +#include "perfetto/public/protos/config/track_event/track_event_config.pzc.h" +#include "perfetto/public/tracing_session.h" + +namespace perfetto { +namespace shlib { +namespace test_utils { +namespace { + +std::string ToHexChars(uint8_t val) { + std::string ret; + uint8_t high_nibble = (val & 0xF0) >> 4; + uint8_t low_nibble = (val & 0xF); + static const char hex_chars[] = "0123456789ABCDEF"; + ret.push_back(hex_chars[high_nibble]); + ret.push_back(hex_chars[low_nibble]); + return ret; +} + +} // namespace + +TracingSession TracingSession::Builder::Build() { + struct PerfettoPbMsgWriter writer; + struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer); + + struct perfetto_protos_TraceConfig cfg; + PerfettoPbMsgInit(&cfg.msg, &writer); + + { + struct perfetto_protos_TraceConfig_BufferConfig buffers; + perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers); + + perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024); + + perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers); + } + + { + struct perfetto_protos_TraceConfig_DataSource data_sources; + perfetto_protos_TraceConfig_begin_data_sources(&cfg, &data_sources); + + { + struct perfetto_protos_DataSourceConfig ds_cfg; + perfetto_protos_TraceConfig_DataSource_begin_config(&data_sources, + &ds_cfg); + + perfetto_protos_DataSourceConfig_set_cstr_name(&ds_cfg, + data_source_name_.c_str()); + if (!enabled_categories_.empty() && !disabled_categories_.empty()) { + perfetto_protos_TrackEventConfig te_cfg; + perfetto_protos_DataSourceConfig_begin_track_event_config(&ds_cfg, + &te_cfg); + for (const std::string& cat : enabled_categories_) { + perfetto_protos_TrackEventConfig_set_enabled_categories( + &te_cfg, cat.data(), cat.size()); + } + for (const std::string& cat : disabled_categories_) { + perfetto_protos_TrackEventConfig_set_disabled_categories( + &te_cfg, cat.data(), cat.size()); + } + perfetto_protos_DataSourceConfig_end_track_event_config(&ds_cfg, + &te_cfg); + } + + perfetto_protos_TraceConfig_DataSource_end_config(&data_sources, &ds_cfg); + } + + perfetto_protos_TraceConfig_end_data_sources(&cfg, &data_sources); + } + size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer); + std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]); + PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size); + PerfettoHeapBufferDestroy(hb, &writer.writer); + + struct PerfettoTracingSessionImpl* ts = + PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS); + + PerfettoTracingSessionSetup(ts, ser.get(), cfg_size); + + PerfettoTracingSessionStartBlocking(ts); + + return TracingSession::Adopt(ts); +} + +TracingSession TracingSession::Adopt(struct PerfettoTracingSessionImpl* session) { + TracingSession ret; + ret.session_ = session; + ret.stopped_ = std::make_unique<WaitableEvent>(); + PerfettoTracingSessionSetStopCb( + ret.session_, + [](struct PerfettoTracingSessionImpl*, void* arg) { + static_cast<WaitableEvent*>(arg)->Notify(); + }, + ret.stopped_.get()); + return ret; +} + +TracingSession::TracingSession(TracingSession&& other) noexcept { + session_ = other.session_; + other.session_ = nullptr; + stopped_ = std::move(other.stopped_); + other.stopped_ = nullptr; +} + +TracingSession::~TracingSession() { + if (!session_) { + return; + } + if (!stopped_->IsNotified()) { + PerfettoTracingSessionStopBlocking(session_); + stopped_->WaitForNotification(); + } + PerfettoTracingSessionDestroy(session_); +} + +bool TracingSession::FlushBlocking(uint32_t timeout_ms) { + WaitableEvent notification; + bool result; + auto* cb = new std::function<void(bool)>([&](bool success) { + result = success; + notification.Notify(); + }); + PerfettoTracingSessionFlushAsync( + session_, timeout_ms, + [](PerfettoTracingSessionImpl*, bool success, void* user_arg) { + auto* f = reinterpret_cast<std::function<void(bool)>*>(user_arg); + (*f)(success); + delete f; + }, + cb); + notification.WaitForNotification(); + return result; +} + +void TracingSession::WaitForStopped() { + stopped_->WaitForNotification(); +} + +void TracingSession::StopBlocking() { + PerfettoTracingSessionStopBlocking(session_); +} + +std::vector<uint8_t> TracingSession::ReadBlocking() { + std::vector<uint8_t> data; + PerfettoTracingSessionReadTraceBlocking( + session_, + [](struct PerfettoTracingSessionImpl*, const void* trace_data, + size_t size, bool, void* user_arg) { + auto& dst = *static_cast<std::vector<uint8_t>*>(user_arg); + auto* src = static_cast<const uint8_t*>(trace_data); + dst.insert(dst.end(), src, src + size); + }, + &data); + return data; +} + +} // namespace test_utils +} // namespace shlib +} // namespace perfetto + +void PrintTo(const PerfettoPbDecoderField& field, std::ostream* pos) { + std::ostream& os = *pos; + PerfettoPbDecoderStatus status = + static_cast<PerfettoPbDecoderStatus>(field.status); + switch (status) { + case PERFETTO_PB_DECODER_ERROR: + os << "MALFORMED PROTOBUF"; + break; + case PERFETTO_PB_DECODER_DONE: + os << "DECODER DONE"; + break; + case PERFETTO_PB_DECODER_OK: + switch (field.wire_type) { + case PERFETTO_PB_WIRE_TYPE_DELIMITED: + os << "\""; + for (size_t i = 0; i < field.value.delimited.len; i++) { + os << perfetto::shlib::test_utils::ToHexChars( + field.value.delimited.start[i]) + << " "; + } + os << "\""; + break; + case PERFETTO_PB_WIRE_TYPE_VARINT: + os << "varint: " << field.value.integer64; + break; + case PERFETTO_PB_WIRE_TYPE_FIXED32: + os << "fixed32: " << field.value.integer32; + break; + case PERFETTO_PB_WIRE_TYPE_FIXED64: + os << "fixed64: " << field.value.integer64; + break; + } + break; + } +} diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h new file mode 100644 index 0000000000..4353554963 --- /dev/null +++ b/libs/tracing_perfetto/tests/utils.h @@ -0,0 +1,452 @@ +/* + * Copyright 2024 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. + */ + +// Copied from //external/perfetto/src/shared_lib/test/utils.h + +#ifndef UTILS_H +#define UTILS_H + +#include <cassert> +#include <condition_variable> +#include <cstdint> +#include <functional> +#include <iterator> +#include <memory> +#include <mutex> +#include <ostream> +#include <string> +#include <vector> + +#include "gmock/gmock-matchers.h" +#include "gmock/gmock-more-matchers.h" +#include "gtest/gtest-matchers.h" +#include "gtest/gtest.h" +#include "perfetto/public/abi/pb_decoder_abi.h" +#include "perfetto/public/pb_utils.h" +#include "perfetto/public/tracing_session.h" + +// Pretty printer for gtest +void PrintTo(const PerfettoPbDecoderField& field, std::ostream*); + +namespace perfetto { +namespace shlib { +namespace test_utils { + +class WaitableEvent { + public: + WaitableEvent() = default; + void Notify() { + std::unique_lock<std::mutex> lock(m_); + notified_ = true; + cv_.notify_one(); + } + bool WaitForNotification() { + std::unique_lock<std::mutex> lock(m_); + cv_.wait(lock, [this] { return notified_; }); + return notified_; + } + bool IsNotified() { + std::unique_lock<std::mutex> lock(m_); + return notified_; + } + + private: + std::mutex m_; + std::condition_variable cv_; + bool notified_ = false; +}; + +class TracingSession { + public: + class Builder { + public: + Builder() = default; + Builder& set_data_source_name(std::string data_source_name) { + data_source_name_ = std::move(data_source_name); + return *this; + } + Builder& add_enabled_category(std::string category) { + enabled_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_disabled_category(std::string category) { + disabled_categories_.push_back(std::move(category)); + return *this; + } + TracingSession Build(); + + private: + std::string data_source_name_; + std::vector<std::string> enabled_categories_; + std::vector<std::string> disabled_categories_; + }; + + static TracingSession Adopt(struct PerfettoTracingSessionImpl*); + + TracingSession(TracingSession&&) noexcept; + + ~TracingSession(); + + struct PerfettoTracingSessionImpl* session() const { + return session_; + } + + bool FlushBlocking(uint32_t timeout_ms); + void WaitForStopped(); + void StopBlocking(); + std::vector<uint8_t> ReadBlocking(); + + private: + TracingSession() = default; + struct PerfettoTracingSessionImpl* session_; + std::unique_ptr<WaitableEvent> stopped_; +}; + +template <typename FieldSkipper> +class FieldViewBase { + public: + class Iterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = const PerfettoPbDecoderField; + using pointer = value_type; + using reference = value_type; + reference operator*() const { + struct PerfettoPbDecoder decoder; + decoder.read_ptr = read_ptr_; + decoder.end_ptr = end_ptr_; + struct PerfettoPbDecoderField field; + do { + field = PerfettoPbDecoderParseField(&decoder); + } while (field.status == PERFETTO_PB_DECODER_OK && + skipper_.ShouldSkip(field)); + return field; + } + Iterator& operator++() { + struct PerfettoPbDecoder decoder; + decoder.read_ptr = read_ptr_; + decoder.end_ptr = end_ptr_; + PerfettoPbDecoderSkipField(&decoder); + read_ptr_ = decoder.read_ptr; + AdvanceToFirstInterestingField(); + return *this; + } + Iterator operator++(int) { + Iterator tmp = *this; + ++(*this); + return tmp; + } + + friend bool operator==(const Iterator& a, const Iterator& b) { + return a.read_ptr_ == b.read_ptr_; + } + friend bool operator!=(const Iterator& a, const Iterator& b) { + return a.read_ptr_ != b.read_ptr_; + } + + private: + Iterator(const uint8_t* read_ptr, const uint8_t* end_ptr, + const FieldSkipper& skipper) + : read_ptr_(read_ptr), end_ptr_(end_ptr), skipper_(skipper) { + AdvanceToFirstInterestingField(); + } + void AdvanceToFirstInterestingField() { + struct PerfettoPbDecoder decoder; + decoder.read_ptr = read_ptr_; + decoder.end_ptr = end_ptr_; + struct PerfettoPbDecoderField field; + const uint8_t* prev_read_ptr; + do { + prev_read_ptr = decoder.read_ptr; + field = PerfettoPbDecoderParseField(&decoder); + } while (field.status == PERFETTO_PB_DECODER_OK && + skipper_.ShouldSkip(field)); + if (field.status == PERFETTO_PB_DECODER_OK) { + read_ptr_ = prev_read_ptr; + } else { + read_ptr_ = decoder.read_ptr; + } + } + friend class FieldViewBase<FieldSkipper>; + const uint8_t* read_ptr_; + const uint8_t* end_ptr_; + const FieldSkipper& skipper_; + }; + using value_type = const PerfettoPbDecoderField; + using const_iterator = Iterator; + template <typename... Args> + explicit FieldViewBase(const uint8_t* begin, const uint8_t* end, Args... args) + : begin_(begin), end_(end), s_(args...) { + } + template <typename... Args> + explicit FieldViewBase(const std::vector<uint8_t>& data, Args... args) + : FieldViewBase(data.data(), data.data() + data.size(), args...) { + } + template <typename... Args> + explicit FieldViewBase(const struct PerfettoPbDecoderField& field, + Args... args) + : s_(args...) { + if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) { + abort(); + } + begin_ = field.value.delimited.start; + end_ = begin_ + field.value.delimited.len; + } + Iterator begin() const { + return Iterator(begin_, end_, s_); + } + Iterator end() const { + return Iterator(end_, end_, s_); + } + PerfettoPbDecoderField front() const { + return *begin(); + } + + size_t size() const { + size_t count = 0; + for (auto field : *this) { + (void)field; + count++; + } + return count; + } + + bool ok() const { + for (auto field : *this) { + if (field.status != PERFETTO_PB_DECODER_OK) { + return false; + } + } + return true; + } + + private: + const uint8_t* begin_; + const uint8_t* end_; + FieldSkipper s_; +}; + +// Pretty printer for gtest +template <typename FieldSkipper> +void PrintTo(const FieldViewBase<FieldSkipper>& field_view, std::ostream* pos) { + std::ostream& os = *pos; + os << "{"; + for (PerfettoPbDecoderField f : field_view) { + PrintTo(f, pos); + os << ", "; + } + os << "}"; +} + +class IdFieldSkipper { + public: + explicit IdFieldSkipper(uint32_t id) : id_(id) { + } + explicit IdFieldSkipper(int32_t id) : id_(static_cast<uint32_t>(id)) { + } + bool ShouldSkip(const struct PerfettoPbDecoderField& field) const { + return field.id != id_; + } + + private: + uint32_t id_; +}; + +class NoFieldSkipper { + public: + NoFieldSkipper() = default; + bool ShouldSkip(const struct PerfettoPbDecoderField&) const { + return false; + } +}; + +// View over all the fields of a contiguous serialized protobuf message. +// +// Examples: +// +// for (struct PerfettoPbDecoderField field : FieldView(msg_begin, msg_end)) { +// //... +// } +// FieldView fields2(/*PerfettoPbDecoderField*/ nested_field); +// FieldView fields3(/*std::vector<uint8_t>*/ data); +// size_t num = fields1.size(); // The number of fields. +// bool ok = fields1.ok(); // Checks that the message is not malformed. +using FieldView = FieldViewBase<NoFieldSkipper>; + +// Like `FieldView`, but only considers fields with a specific id. +// +// Examples: +// +// IdFieldView fields(msg_begin, msg_end, id) +using IdFieldView = FieldViewBase<IdFieldSkipper>; + +// Matches a PerfettoPbDecoderField with the specified id. Accepts another +// matcher to match the contents of the field. +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, PbField(900, VarIntField(5))); +template <typename M> +auto PbField(int32_t id, M m) { + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::id, id), m); +} + +// Matches a PerfettoPbDecoderField submessage field. Accepts a container +// matcher for the subfields. +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, MsgField(ElementsAre(...))); +template <typename M> +auto MsgField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { return FieldView(field); }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_DELIMITED), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField length delimited field. Accepts a string +// matcher. +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, StringField("string")); +template <typename M> +auto StringField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return std::string( + reinterpret_cast<const char*>(field.value.delimited.start), + field.value.delimited.len); + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_DELIMITED), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField VarInt field. Accepts an integer matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, VarIntField(1))); +template <typename M> +auto VarIntField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.integer64; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_VARINT), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField fixed64 field. Accepts an integer matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, Fixed64Field(1))); +template <typename M> +auto Fixed64Field(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.integer64; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_FIXED64), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField fixed32 field. Accepts an integer matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, Fixed32Field(1))); +template <typename M> +auto Fixed32Field(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.integer32; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_FIXED32), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField double field. Accepts a double matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, DoubleField(1.0))); +template <typename M> +auto DoubleField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.double_val; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_FIXED64), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField float field. Accepts a float matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, FloatField(1.0))); +template <typename M> +auto FloatField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.float_val; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_FIXED32), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField submessage field. Accepts a container +// matcher for the subfields. +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, AllFieldsWithId(900, ElementsAre(...))); +template <typename M> +auto AllFieldsWithId(int32_t id, M m) { + auto f = [id](const PerfettoPbDecoderField& field) { + return IdFieldView(field, id); + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_DELIMITED), + testing::ResultOf(f, m)); +} + +} // namespace test_utils +} // namespace shlib +} // namespace perfetto + +#endif // UTILS_H diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp new file mode 100644 index 0000000000..c7fb8bd9a8 --- /dev/null +++ b/libs/tracing_perfetto/tracing_perfetto.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2024 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 "tracing_perfetto.h" + +#include <cutils/trace.h> + +#include "perfetto/public/te_category_macros.h" +#include "trace_categories.h" +#include "tracing_perfetto_internal.h" + +namespace tracing_perfetto { + +void registerWithPerfetto(bool test) { + internal::registerWithPerfetto(test); +} + +Result traceBegin(uint64_t category, const char* name) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceBegin(*perfettoTeCategory, name); + } else { + atrace_begin(category, name); + return Result::SUCCESS; + } +} + +Result traceEnd(uint64_t category) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceEnd(*perfettoTeCategory); + } else { + atrace_end(category); + return Result::SUCCESS; + } +} + +Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie); + } else { + atrace_async_begin(category, name, cookie); + return Result::SUCCESS; + } +} + +Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie); + } else { + atrace_async_end(category, name, cookie); + return Result::SUCCESS; + } +} + +Result traceAsyncBeginForTrack(uint64_t category, const char* name, + const char* trackName, int32_t cookie) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie); + } else { + atrace_async_for_track_begin(category, trackName, name, cookie); + return Result::SUCCESS; + } +} + +Result traceAsyncEndForTrack(uint64_t category, const char* trackName, + int32_t cookie) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie); + } else { + atrace_async_for_track_end(category, trackName, cookie); + return Result::SUCCESS; + } +} + +Result traceInstant(uint64_t category, const char* name) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceInstant(*perfettoTeCategory, name); + } else { + atrace_instant(category, name); + return Result::SUCCESS; + } +} + +Result traceInstantForTrack(uint64_t category, const char* trackName, + const char* name) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name); + } else { + atrace_instant_for_track(category, trackName, name); + return Result::SUCCESS; + } +} + +Result traceCounter(uint64_t category, const char* name, int64_t value) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceCounter(*perfettoTeCategory, name, value); + } else { + atrace_int64(category, name, value); + return Result::SUCCESS; + } +} + +uint64_t getEnabledCategories() { + if (internal::isPerfettoSdkTracingEnabled()) { + return internal::getDefaultCategories(); + } else { + return atrace_get_enabled_tags(); + } +} + +} // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp new file mode 100644 index 0000000000..58ba428610 --- /dev/null +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -0,0 +1,229 @@ +/* + * Copyright 2024 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 FRAMEWORK_CATEGORIES(C) \ + C(always, "always", "Always category") \ + C(graphics, "graphics", "Graphics category") \ + C(input, "input", "Input category") \ + C(view, "view", "View category") \ + C(webview, "webview", "WebView category") \ + C(windowmanager, "wm", "WindowManager category") \ + C(activitymanager, "am", "ActivityManager category") \ + C(syncmanager, "syncmanager", "SyncManager category") \ + C(audio, "audio", "Audio category") \ + C(video, "video", "Video category") \ + C(camera, "camera", "Camera category") \ + C(hal, "hal", "HAL category") \ + C(app, "app", "App category") \ + C(resources, "res", "Resources category") \ + C(dalvik, "dalvik", "Dalvik category") \ + C(rs, "rs", "RS category") \ + C(bionic, "bionic", "Bionic category") \ + C(power, "power", "Power category") \ + C(packagemanager, "packagemanager", "PackageManager category") \ + C(systemserver, "ss", "System Server category") \ + C(database, "database", "Database category") \ + C(network, "network", "Network category") \ + C(adb, "adb", "ADB category") \ + C(vibrator, "vibrator", "Vibrator category") \ + C(aidl, "aidl", "AIDL category") \ + C(nnapi, "nnapi", "NNAPI category") \ + C(rro, "rro", "RRO category") \ + C(thermal, "thermal", "Thermal category") + +#include "tracing_perfetto_internal.h" + +#include <inttypes.h> + +#include <mutex> + +#include <android_os.h> + +#include "perfetto/public/compiler.h" +#include "perfetto/public/producer.h" +#include "perfetto/public/te_category_macros.h" +#include "perfetto/public/te_macros.h" +#include "perfetto/public/track_event.h" +#include "trace_categories.h" +#include "trace_result.h" + +namespace tracing_perfetto { + +namespace internal { + +namespace { + +PERFETTO_TE_CATEGORIES_DECLARE(FRAMEWORK_CATEGORIES); + +PERFETTO_TE_CATEGORIES_DEFINE(FRAMEWORK_CATEGORIES); + +struct PerfettoTeCategory* toCategory(uint64_t inCategory) { + switch (inCategory) { + case TRACE_CATEGORY_ALWAYS: + return &always; + case TRACE_CATEGORY_GRAPHICS: + return &graphics; + case TRACE_CATEGORY_INPUT: + return &input; + case TRACE_CATEGORY_VIEW: + return &view; + case TRACE_CATEGORY_WEBVIEW: + return &webview; + case TRACE_CATEGORY_WINDOW_MANAGER: + return &windowmanager; + case TRACE_CATEGORY_ACTIVITY_MANAGER: + return &activitymanager; + case TRACE_CATEGORY_SYNC_MANAGER: + return &syncmanager; + case TRACE_CATEGORY_AUDIO: + return &audio; + case TRACE_CATEGORY_VIDEO: + return &video; + case TRACE_CATEGORY_CAMERA: + return &camera; + case TRACE_CATEGORY_HAL: + return &hal; + case TRACE_CATEGORY_APP: + return &app; + case TRACE_CATEGORY_RESOURCES: + return &resources; + case TRACE_CATEGORY_DALVIK: + return &dalvik; + case TRACE_CATEGORY_RS: + return &rs; + case TRACE_CATEGORY_BIONIC: + return &bionic; + case TRACE_CATEGORY_POWER: + return &power; + case TRACE_CATEGORY_PACKAGE_MANAGER: + return &packagemanager; + case TRACE_CATEGORY_SYSTEM_SERVER: + return &systemserver; + case TRACE_CATEGORY_DATABASE: + return &database; + case TRACE_CATEGORY_NETWORK: + return &network; + case TRACE_CATEGORY_ADB: + return &adb; + case TRACE_CATEGORY_VIBRATOR: + return &vibrator; + case TRACE_CATEGORY_AIDL: + return &aidl; + case TRACE_CATEGORY_NNAPI: + return &nnapi; + case TRACE_CATEGORY_RRO: + return &rro; + case TRACE_CATEGORY_THERMAL: + return &thermal; + default: + return nullptr; + } +} + +} // namespace + +bool isPerfettoSdkTracingEnabled() { + return android::os::perfetto_sdk_tracing(); +} + +struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) { + if (!isPerfettoSdkTracingEnabled()) { + return nullptr; + } + + struct PerfettoTeCategory* perfettoCategory = toCategory(category); + bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( + (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED)); + return enabled ? perfettoCategory : nullptr; +} + +void registerWithPerfetto(bool test) { + if (!isPerfettoSdkTracingEnabled()) { + return; + } + static std::once_flag registration; + std::call_once(registration, [test]() { + struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); + args.backends = test ? PERFETTO_BACKEND_IN_PROCESS : PERFETTO_BACKEND_SYSTEM; + PerfettoProducerInit(args); + PerfettoTeInit(); + PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES); + }); +} + +Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) { + PERFETTO_TE(category, PERFETTO_TE_SLICE_BEGIN(name)); + return Result::SUCCESS; +} + +Result perfettoTraceEnd(const struct PerfettoTeCategory& category) { + PERFETTO_TE(category, PERFETTO_TE_SLICE_END()); + return Result::SUCCESS; +} + +Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, + const char* trackName, uint64_t cookie) { + PERFETTO_TE( + category, PERFETTO_TE_SLICE_BEGIN(name), + PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + return Result::SUCCESS; +} + +Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, + const char* trackName, uint64_t cookie) { + PERFETTO_TE( + category, PERFETTO_TE_SLICE_END(), + PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + return Result::SUCCESS; +} + +Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, + uint64_t cookie) { + return perfettoTraceAsyncBeginForTrack(category, name, name, cookie); +} + +Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name, + uint64_t cookie) { + return perfettoTraceAsyncEndForTrack(category, name, cookie); +} + +Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) { + PERFETTO_TE(category, PERFETTO_TE_INSTANT(name)); + return Result::SUCCESS; +} + +Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, + const char* trackName, const char* name) { + PERFETTO_TE( + category, PERFETTO_TE_INSTANT(name), + PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid())); + return Result::SUCCESS; +} + +Result perfettoTraceCounter(const struct PerfettoTeCategory& category, + [[maybe_unused]] const char* name, int64_t value) { + PERFETTO_TE(category, PERFETTO_TE_COUNTER(), + PERFETTO_TE_INT_COUNTER(value)); + return Result::SUCCESS; +} + +uint64_t getDefaultCategories() { + return TRACE_CATEGORIES; +} + +} // namespace internal + +} // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h new file mode 100644 index 0000000000..9a579f162a --- /dev/null +++ b/libs/tracing_perfetto/tracing_perfetto_internal.h @@ -0,0 +1,65 @@ +/* + * Copyright 2024 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 TRACING_PERFETTO_INTERNAL_H +#define TRACING_PERFETTO_INTERNAL_H + +#include <stdint.h> + +#include "include/trace_result.h" +#include "perfetto/public/te_category_macros.h" + +namespace tracing_perfetto { + +namespace internal { + +bool isPerfettoSdkTracingEnabled(); + +struct PerfettoTeCategory* toPerfettoCategory(uint64_t category); + +void registerWithPerfetto(bool test = false); + +Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name); + +Result perfettoTraceEnd(const struct PerfettoTeCategory& category); + +Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, + uint64_t cookie); + +Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name, + uint64_t cookie); + +Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, + const char* trackName, uint64_t cookie); + +Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, + const char* trackName, uint64_t cookie); + +Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name); + +Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, + const char* trackName, const char* name); + +Result perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name, + int64_t value); + +uint64_t getDefaultCategories(); + +} // namespace internal + +} // namespace tracing_perfetto + +#endif // TRACING_PERFETTO_INTERNAL_H diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index ec0ab4e464..9cb298a132 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["frameworks_native_libs_ui_license"], + default_team: "trendy_team_android_core_graphics_stack", } // Added automatically by a large-scale-change diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index e9b5decee8..a5aca9912f 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -384,8 +384,8 @@ std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const { status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t* outStride, - buffer_handle_t* outBufferHandles, bool importBuffers) const { + uint32_t* outStride, buffer_handle_t* outBufferHandles, + bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo = {}; descriptorInfo.width = width; descriptorInfo.height = height; @@ -400,6 +400,8 @@ status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t wid return error; } + constexpr auto bufferCount = 1; + auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp index 474d381dbb..152b35a505 100644 --- a/libs/ui/Gralloc3.cpp +++ b/libs/ui/Gralloc3.cpp @@ -371,7 +371,7 @@ std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const { status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); @@ -383,6 +383,8 @@ status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t wid return error; } + constexpr auto bufferCount = 1; + auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 03ff58a76c..2a607308d1 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -262,37 +262,8 @@ void Gralloc4Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* ou status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, void** outData, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) const { - std::vector<ui::PlaneLayout> planeLayouts; - status_t err = getPlaneLayouts(bufferHandle, &planeLayouts); - - if (err == NO_ERROR && !planeLayouts.empty()) { - if (outBytesPerPixel) { - int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits; - for (const auto& planeLayout : planeLayouts) { - if (bitsPerPixel != planeLayout.sampleIncrementInBits) { - bitsPerPixel = -1; - } - } - if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) { - *outBytesPerPixel = bitsPerPixel / 8; - } else { - *outBytesPerPixel = -1; - } - } - if (outBytesPerStride) { - int32_t bytesPerStride = planeLayouts.front().strideInBytes; - for (const auto& planeLayout : planeLayouts) { - if (bytesPerStride != planeLayout.strideInBytes) { - bytesPerStride = -1; - } - } - if (bytesPerStride >= 0) { - *outBytesPerStride = bytesPerStride; - } else { - *outBytesPerStride = -1; - } - } - } + if (outBytesPerPixel) *outBytesPerPixel = -1; + if (outBytesPerStride) *outBytesPerStride = -1; auto buffer = const_cast<native_handle_t*>(bufferHandle); @@ -1069,7 +1040,7 @@ std::string Gralloc4Allocator::dumpDebugInfo(bool less) const { status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const { IMapper::BufferDescriptorInfo descriptorInfo; if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, @@ -1084,6 +1055,8 @@ status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, return error; } + constexpr auto bufferCount = 1; + if (mAidlAllocator) { AllocationResult result; #pragma clang diagnostic push diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp index 25850f71cb..11064ae7fc 100644 --- a/libs/ui/Gralloc5.cpp +++ b/libs/ui/Gralloc5.cpp @@ -19,6 +19,7 @@ #include <ui/Gralloc5.h> +#include <aidl/android/hardware/graphics/allocator/AllocationError.h> #include <aidlcommonsupport/NativeHandle.h> #include <android/binder_manager.h> #include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h> @@ -223,55 +224,75 @@ std::string Gralloc5Allocator::dumpDebugInfo(bool less) const { status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t bufferCount, uint32_t *outStride, - buffer_handle_t *outBufferHandles, bool importBuffers) const { - auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage); + uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers) const { + auto result = allocate(GraphicBufferAllocator::AllocationRequest{ + .importBuffer = importBuffers, + .width = width, + .height = height, + .format = format, + .layerCount = layerCount, + .usage = usage, + .requestorName = requestorName, + }); + + *outStride = result.stride; + outBufferHandles[0] = result.handle; + return result.status; +} + +GraphicBufferAllocator::AllocationResult Gralloc5Allocator::allocate( + const GraphicBufferAllocator::AllocationRequest& request) const { + auto descriptorInfo = makeDescriptor(request.requestorName, request.width, request.height, + request.format, request.layerCount, request.usage); if (!descriptorInfo) { - return BAD_VALUE; + return GraphicBufferAllocator::AllocationResult{BAD_VALUE}; + } + + descriptorInfo->additionalOptions.reserve(request.extras.size()); + for (const auto& option : request.extras) { + ExtendableType type; + type.name = option.name; + type.value = option.value; + descriptorInfo->additionalOptions.push_back(std::move(type)); } AllocationResult result; - auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result); + auto status = mAllocator->allocate2(*descriptorInfo, 1, &result); if (!status.isOk()) { auto error = status.getExceptionCode(); if (error == EX_SERVICE_SPECIFIC) { - error = status.getServiceSpecificError(); - } - if (error == OK) { - error = UNKNOWN_ERROR; + switch (static_cast<AllocationError>(status.getServiceSpecificError())) { + case AllocationError::BAD_DESCRIPTOR: + error = BAD_VALUE; + break; + case AllocationError::NO_RESOURCES: + error = NO_MEMORY; + break; + default: + error = UNKNOWN_ERROR; + break; + } } - return error; + return GraphicBufferAllocator::AllocationResult{error}; } - if (importBuffers) { - for (uint32_t i = 0; i < bufferCount; i++) { - auto handle = makeFromAidl(result.buffers[i]); - auto error = mMapper.importBuffer(handle, &outBufferHandles[i]); - native_handle_delete(handle); - if (error != NO_ERROR) { - for (uint32_t j = 0; j < i; j++) { - mMapper.freeBuffer(outBufferHandles[j]); - outBufferHandles[j] = nullptr; - } - return error; - } + GraphicBufferAllocator::AllocationResult ret{OK}; + if (request.importBuffer) { + auto handle = makeFromAidl(result.buffers[0]); + auto error = mMapper.importBuffer(handle, &ret.handle); + native_handle_delete(handle); + if (error != NO_ERROR) { + return GraphicBufferAllocator::AllocationResult{error}; } } else { - for (uint32_t i = 0; i < bufferCount; i++) { - outBufferHandles[i] = dupFromAidl(result.buffers[i]); - if (!outBufferHandles[i]) { - for (uint32_t j = 0; j < i; j++) { - auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]); - native_handle_close(buffer); - native_handle_delete(buffer); - outBufferHandles[j] = nullptr; - } - return NO_MEMORY; - } + ret.handle = dupFromAidl(result.buffers[0]); + if (!ret.handle) { + return GraphicBufferAllocator::AllocationResult{NO_MEMORY}; } } - *outStride = result.stride; + ret.stride = result.stride; // Release all the resources held by AllocationResult (specifically any remaining FDs) result = {}; @@ -280,7 +301,7 @@ status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, // is marked apex_available (b/214400477) and libbinder isn't (which of course is correct) // IPCThreadState::self()->flushCommands(); - return OK; + return ret; } void Gralloc5Mapper::preload() { @@ -576,37 +597,8 @@ void Gralloc5Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t *ou status_t Gralloc5Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect &bounds, int acquireFence, void **outData, int32_t *outBytesPerPixel, int32_t *outBytesPerStride) const { - std::vector<ui::PlaneLayout> planeLayouts; - status_t err = getPlaneLayouts(bufferHandle, &planeLayouts); - - if (err == NO_ERROR && !planeLayouts.empty()) { - if (outBytesPerPixel) { - int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits; - for (const auto &planeLayout : planeLayouts) { - if (bitsPerPixel != planeLayout.sampleIncrementInBits) { - bitsPerPixel = -1; - } - } - if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) { - *outBytesPerPixel = bitsPerPixel / 8; - } else { - *outBytesPerPixel = -1; - } - } - if (outBytesPerStride) { - int32_t bytesPerStride = planeLayouts.front().strideInBytes; - for (const auto &planeLayout : planeLayouts) { - if (bytesPerStride != planeLayout.strideInBytes) { - bytesPerStride = -1; - } - } - if (bytesPerStride >= 0) { - *outBytesPerStride = bytesPerStride; - } else { - *outBytesPerStride = -1; - } - } - } + if (outBytesPerPixel) *outBytesPerPixel = -1; + if (outBytesPerStride) *outBytesPerStride = -1; auto status = mMapper->v5.lock(bufferHandle, usage, bounds, acquireFence, outData); diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 429760ffe0..ffb6cdb6ef 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -22,7 +22,7 @@ #include <cutils/atomic.h> #include <grallocusage/GrallocUsageConversion.h> - +#include <sync/sync.h> #include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> #include <utils/Trace.h> @@ -40,6 +40,38 @@ static uint64_t getUniqueId() { return id; } +static void resolveLegacyByteLayoutFromPlaneLayout(const std::vector<ui::PlaneLayout>& planeLayouts, + int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { + if (planeLayouts.empty()) return; + if (outBytesPerPixel) { + int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits; + for (const auto& planeLayout : planeLayouts) { + if (bitsPerPixel != planeLayout.sampleIncrementInBits) { + bitsPerPixel = -1; + } + } + if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) { + *outBytesPerPixel = bitsPerPixel / 8; + } else { + *outBytesPerPixel = -1; + } + } + if (outBytesPerStride) { + int32_t bytesPerStride = planeLayouts.front().strideInBytes; + for (const auto& planeLayout : planeLayouts) { + if (bytesPerStride != planeLayout.strideInBytes) { + bytesPerStride = -1; + } + } + if (bytesPerStride >= 0) { + *outBytesPerStride = bytesPerStride; + } else { + *outBytesPerStride = -1; + } + } +} + sp<GraphicBuffer> GraphicBuffer::from(ANativeWindowBuffer* anwb) { return static_cast<GraphicBuffer *>(anwb); } @@ -106,6 +138,26 @@ GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod m inUsage, inStride); } +GraphicBuffer::GraphicBuffer(const GraphicBufferAllocator::AllocationRequest& request) + : GraphicBuffer() { + GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); + auto result = allocator.allocate(request); + mInitCheck = result.status; + if (result.status == NO_ERROR) { + handle = result.handle; + stride = result.stride; + + mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); + + width = static_cast<int>(request.width); + height = static_cast<int>(request.height); + format = request.format; + layerCount = request.layerCount; + usage = request.usage; + usage_deprecated = int(usage); + } +} + GraphicBuffer::~GraphicBuffer() { ATRACE_CALL(); @@ -143,6 +195,10 @@ ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const const_cast<GraphicBuffer*>(this)); } +status_t GraphicBuffer::getDataspace(ui::Dataspace* outDataspace) const { + return mBufferMapper.getDataspace(handle, outDataspace); +} + status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage) { @@ -255,10 +311,7 @@ status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr, return BAD_VALUE; } - status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr, outBytesPerPixel, - outBytesPerStride); - - return res; + return lockAsync(inUsage, rect, vaddr, -1, outBytesPerPixel, outBytesPerStride); } status_t GraphicBuffer::lockYCbCr(uint32_t inUsage, android_ycbcr* ycbcr) @@ -278,14 +331,12 @@ status_t GraphicBuffer::lockYCbCr(uint32_t inUsage, const Rect& rect, width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lockYCbCr(handle, inUsage, rect, ycbcr); - return res; + return lockAsyncYCbCr(inUsage, rect, ycbcr, -1); } status_t GraphicBuffer::unlock() { - status_t res = getBufferMapper().unlock(handle); - return res; + return unlockAsync(nullptr); } status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd, @@ -312,10 +363,49 @@ status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerU return BAD_VALUE; } - status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect, - vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); + // Resolve the bpp & bps before doing a lock in case this fails we don't have to worry about + // doing an unlock + int32_t legacyBpp = -1, legacyBps = -1; + if (outBytesPerPixel || outBytesPerStride) { + const auto mapperVersion = getBufferMapperVersion(); + // For gralloc2 we need to guess at the bpp & bps + // For gralloc3 the lock() call will return it + // For gralloc4 & later the PlaneLayout metadata query is vastly superior and we + // resolve bpp & bps just for compatibility + + // TODO: See if we can't just remove gralloc2 support. + if (mapperVersion == GraphicBufferMapper::GRALLOC_2) { + legacyBpp = bytesPerPixel(format); + if (legacyBpp > 0) { + legacyBps = stride * legacyBpp; + } else { + legacyBpp = -1; + } + } else if (mapperVersion >= GraphicBufferMapper::GRALLOC_4) { + auto planeLayout = getBufferMapper().getPlaneLayouts(handle); + if (!planeLayout.has_value()) return planeLayout.asStatus(); + resolveLegacyByteLayoutFromPlaneLayout(planeLayout.value(), &legacyBpp, &legacyBps); + } + } - return res; + const uint64_t usage = static_cast<uint64_t>( + android_convertGralloc1To0Usage(inProducerUsage, inConsumerUsage)); + + auto result = getBufferMapper().lock(handle, usage, rect, base::unique_fd{fenceFd}); + + if (!result.has_value()) { + return result.error().asStatus(); + } + auto value = result.value(); + *vaddr = value.address; + + if (outBytesPerPixel) { + *outBytesPerPixel = legacyBpp != -1 ? legacyBpp : value.bytesPerPixel; + } + if (outBytesPerStride) { + *outBytesPerStride = legacyBps != -1 ? legacyBps : value.bytesPerStride; + } + return OK; } status_t GraphicBuffer::lockAsyncYCbCr(uint32_t inUsage, android_ycbcr* ycbcr, @@ -336,14 +426,18 @@ status_t GraphicBuffer::lockAsyncYCbCr(uint32_t inUsage, const Rect& rect, width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lockAsyncYCbCr(handle, inUsage, rect, ycbcr, fenceFd); - return res; + auto result = getBufferMapper().lockYCbCr(handle, static_cast<int64_t>(inUsage), rect, + base::unique_fd{fenceFd}); + if (!result.has_value()) { + return result.error().asStatus(); + } + *ycbcr = result.value(); + return OK; } status_t GraphicBuffer::unlockAsync(int *fenceFd) { - status_t res = getBufferMapper().unlockAsync(handle, fenceFd); - return res; + return getBufferMapper().unlockAsync(handle, fenceFd); } status_t GraphicBuffer::isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index eb0bd4ed0a..98082fb81e 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -113,6 +113,79 @@ void GraphicBufferAllocator::dumpToSystemLog(bool less) { ALOGD("%s", s.c_str()); } +auto GraphicBufferAllocator::allocate(const AllocationRequest& request) -> AllocationResult { + ATRACE_CALL(); + if (!request.width || !request.height) { + return AllocationResult(BAD_VALUE); + } + + const auto width = request.width; + const auto height = request.height; + + const uint32_t bpp = bytesPerPixel(request.format); + if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": Requesting too large a buffer size", + request.width, request.height, request.layerCount, request.format, request.usage); + return AllocationResult(BAD_VALUE); + } + + if (request.layerCount < 1) { + return AllocationResult(BAD_VALUE); + } + + auto result = mAllocator->allocate(request); + if (result.status == UNKNOWN_TRANSACTION) { + if (!request.extras.empty()) { + ALOGE("Failed to allocate with additional options, allocator version mis-match? " + "gralloc version = %d", + (int)mMapper.getMapperVersion()); + return result; + } + // If there's no additional options, fall back to previous allocate version + result.status = mAllocator->allocate(request.requestorName, request.width, request.height, + request.format, request.layerCount, request.usage, + &result.stride, &result.handle, request.importBuffer); + } + + if (result.status != NO_ERROR) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": %d", + request.width, request.height, request.layerCount, request.format, request.usage, + result.status); + return result; + } + + if (!request.importBuffer) { + return result; + } + size_t bufSize; + + // if stride has no meaning or is too large, + // approximate size with the input width instead + if ((result.stride) != 0 && + std::numeric_limits<size_t>::max() / height / (result.stride) < static_cast<size_t>(bpp)) { + bufSize = static_cast<size_t>(width) * height * bpp; + } else { + bufSize = static_cast<size_t>((result.stride)) * height * bpp; + } + + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + alloc_rec_t rec; + rec.width = width; + rec.height = height; + rec.stride = result.stride; + rec.format = request.format; + rec.layerCount = request.layerCount; + rec.usage = request.usage; + rec.size = bufSize; + rec.requestorName = request.requestorName; + list.add(result.handle, rec); + + return result; +} + status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride, @@ -141,7 +214,7 @@ status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage, - 1, stride, handle, importBuffer); + stride, handle, importBuffer); if (error != NO_ERROR) { ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " "usage %" PRIx64 ": %d", diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index 7086e0470c..b6ab2f5a47 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -41,9 +41,13 @@ #include <system/graphics.h> +using unique_fd = ::android::base::unique_fd; + namespace android { // --------------------------------------------------------------------------- +using LockResult = GraphicBufferMapper::LockResult; + ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) void GraphicBufferMapper::preloadHal() { @@ -135,63 +139,86 @@ status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle) return NO_ERROR; } -status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, - void** vaddr, int32_t* outBytesPerPixel, - int32_t* outBytesPerStride) { - return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride); -} - -status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage, - const Rect& bounds, android_ycbcr *ycbcr) -{ - return lockAsyncYCbCr(handle, usage, bounds, ycbcr, -1); -} +ui::Result<LockResult> GraphicBufferMapper::lock(buffer_handle_t handle, int64_t usage, + const Rect& bounds, unique_fd&& acquireFence) { + ATRACE_CALL(); -status_t GraphicBufferMapper::unlock(buffer_handle_t handle) -{ - int32_t fenceFd = -1; - status_t error = unlockAsync(handle, &fenceFd); - if (error == NO_ERROR && fenceFd >= 0) { - sync_wait(fenceFd, -1); - close(fenceFd); + LockResult result; + status_t status = mMapper->lock(handle, usage, bounds, acquireFence.release(), &result.address, + &result.bytesPerPixel, &result.bytesPerStride); + if (status != OK) { + return base::unexpected(ui::Error::statusToCode(status)); + } else { + return result; } - return error; -} - -status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, - void** vaddr, int fenceFd, int32_t* outBytesPerPixel, - int32_t* outBytesPerStride) { - return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel, - outBytesPerStride); } -status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, - uint64_t consumerUsage, const Rect& bounds, void** vaddr, - int fenceFd, int32_t* outBytesPerPixel, - int32_t* outBytesPerStride) { +ui::Result<android_ycbcr> GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, int64_t usage, + const Rect& bounds, + base::unique_fd&& acquireFence) { ATRACE_CALL(); - const uint64_t usage = static_cast<uint64_t>( - android_convertGralloc1To0Usage(producerUsage, consumerUsage)); - return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel, - outBytesPerStride); + android_ycbcr result = {}; + status_t status = mMapper->lock(handle, usage, bounds, acquireFence.release(), &result); + if (status != OK) { + return base::unexpected(ui::Error::statusToCode(status)); + } else { + return result; + } } -status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd) -{ +status_t GraphicBufferMapper::unlock(buffer_handle_t handle, base::unique_fd* outFence) { ATRACE_CALL(); + int fence = mMapper->unlock(handle); + if (outFence) { + *outFence = unique_fd{fence}; + } else { + sync_wait(fence, -1); + close(fence); + } + return OK; +} - return mMapper->lock(handle, usage, bounds, fenceFd, ycbcr); +status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + void** vaddr) { + auto result = lock(handle, static_cast<int64_t>(usage), bounds); + if (!result.has_value()) return result.asStatus(); + auto val = result.value(); + *vaddr = val.address; + return OK; } -status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd) -{ - ATRACE_CALL(); +status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + android_ycbcr* ycbcr) { + auto result = lockYCbCr(handle, static_cast<int64_t>(usage), bounds); + if (!result.has_value()) return result.asStatus(); + *ycbcr = result.value(); + return OK; +} + +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + void** vaddr, int fenceFd) { + auto result = lock(handle, static_cast<int64_t>(usage), bounds, unique_fd{fenceFd}); + if (!result.has_value()) return result.asStatus(); + auto val = result.value(); + *vaddr = val.address; + return OK; +} - *fenceFd = mMapper->unlock(handle); +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, + uint64_t consumerUsage, const Rect& bounds, void** vaddr, + int fenceFd) { + return lockAsync(handle, android_convertGralloc1To0Usage(producerUsage, consumerUsage), bounds, + vaddr, fenceFd); +} - return NO_ERROR; +status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, + const Rect& bounds, android_ycbcr* ycbcr, + int fenceFd) { + auto result = lockYCbCr(handle, static_cast<int64_t>(usage), bounds, unique_fd{fenceFd}); + if (!result.has_value()) return result.asStatus(); + *ycbcr = result.value(); + return OK; } status_t GraphicBufferMapper::isSupported(uint32_t width, uint32_t height, @@ -287,6 +314,17 @@ status_t GraphicBufferMapper::getPlaneLayouts(buffer_handle_t bufferHandle, return mMapper->getPlaneLayouts(bufferHandle, outPlaneLayouts); } +ui::Result<std::vector<ui::PlaneLayout>> GraphicBufferMapper::getPlaneLayouts( + buffer_handle_t bufferHandle) { + std::vector<ui::PlaneLayout> temp; + status_t status = mMapper->getPlaneLayouts(bufferHandle, &temp); + if (status == OK) { + return std::move(temp); + } else { + return base::unexpected(ui::Error::statusToCode(status)); + } +} + status_t GraphicBufferMapper::getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) { return mMapper->getDataspace(bufferHandle, outDataspace); diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h index 496ba57789..e6015e0b5e 100644 --- a/libs/ui/include/ui/Gralloc.h +++ b/libs/ui/include/ui/Gralloc.h @@ -23,6 +23,7 @@ #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <utils/StrongPointer.h> +#include "GraphicBufferAllocator.h" #include <string> @@ -218,9 +219,13 @@ public: */ virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t* outStride, - buffer_handle_t* outBufferHandles, + uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers = true) const = 0; + + virtual GraphicBufferAllocator::AllocationResult allocate( + const GraphicBufferAllocator::AllocationRequest&) const { + return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION); + } }; } // namespace android diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h index a7b6f49206..e50bb3af3e 100644 --- a/libs/ui/include/ui/Gralloc2.h +++ b/libs/ui/include/ui/Gralloc2.h @@ -81,9 +81,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc2Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h index 7367549964..035684abcd 100644 --- a/libs/ui/include/ui/Gralloc3.h +++ b/libs/ui/include/ui/Gralloc3.h @@ -82,9 +82,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc3Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h index df43be87cd..0f469c0b7e 100644 --- a/libs/ui/include/ui/Gralloc4.h +++ b/libs/ui/include/ui/Gralloc4.h @@ -174,9 +174,8 @@ public: std::string dumpDebugInfo(bool less = true) const override; status_t allocate(std::string requestorName, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, - uint32_t* outStride, buffer_handle_t* outBufferHandles, - bool importBuffers = true) const override; + PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride, + buffer_handle_t* outBufferHandles, bool importBuffers = true) const override; private: const Gralloc4Mapper& mMapper; diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h index 44b97d1a6f..f9e8f5e9fd 100644 --- a/libs/ui/include/ui/Gralloc5.h +++ b/libs/ui/include/ui/Gralloc5.h @@ -172,10 +172,12 @@ public: [[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t bufferCount, uint32_t *outStride, - buffer_handle_t *outBufferHandles, + uint32_t* outStride, buffer_handle_t* outBufferHandles, bool importBuffers) const override; + [[nodiscard]] GraphicBufferAllocator::AllocationResult allocate( + const GraphicBufferAllocator::AllocationRequest&) const override; + private: const Gralloc5Mapper &mMapper; std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator; diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index f859848b0c..652d8ba709 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -26,6 +26,7 @@ #include <android/hardware_buffer.h> #include <ui/ANativeObjectBase.h> +#include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> @@ -103,6 +104,8 @@ public: uint32_t inLayerCount, uint64_t inUsage, std::string requestorName = "<Unknown>"); + GraphicBuffer(const GraphicBufferAllocator::AllocationRequest&); + // Create a GraphicBuffer from an existing handle. enum HandleWrapMethod : uint8_t { // Wrap and use the handle directly. It assumes the handle has been @@ -169,6 +172,8 @@ public: mGenerationNumber = generation; } + status_t getDataspace(ui::Dataspace* outDataspace) const; + // This function is privileged. It requires access to the allocator // device or service, which usually involves adding suitable selinux // rules. diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 3ed988c4c2..8f461e193b 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -22,6 +22,7 @@ #include <memory> #include <string> +#include <vector> #include <cutils/native_handle.h> @@ -42,6 +43,35 @@ class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> public: static inline GraphicBufferAllocator& get() { return getInstance(); } + struct AdditionalOptions { + const char* name; + int64_t value; + }; + + struct AllocationRequest { + bool importBuffer; + uint32_t width; + uint32_t height; + PixelFormat format; + uint32_t layerCount; + uint64_t usage; + std::string requestorName; + std::vector<AdditionalOptions> extras; + }; + + struct AllocationResult { + status_t status; + buffer_handle_t handle = nullptr; + uint32_t stride = 0; + + explicit AllocationResult(status_t status) : status(status) {} + + explicit AllocationResult(buffer_handle_t handle, uint32_t stride) + : status(OK), handle(handle), stride(stride) {} + }; + + AllocationResult allocate(const AllocationRequest&); + /** * Allocates and imports a gralloc buffer. * diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h index 3a5167ab25..9da1447aed 100644 --- a/libs/ui/include/ui/GraphicBufferMapper.h +++ b/libs/ui/include/ui/GraphicBufferMapper.h @@ -22,9 +22,11 @@ #include <memory> +#include <android-base/unique_fd.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> +#include <ui/Result.h> #include <utils/Singleton.h> // Needed by code that still uses the GRALLOC_USAGE_* constants. @@ -38,6 +40,12 @@ namespace android { class GrallocMapper; +/** + * This class is a thin wrapper over the various gralloc HALs. It is a "raw" wrapper, having + * version-specific behaviors & features. It is not recommend for general use. It is instead + * strongly recommended to use AHardwareBuffer or ui::GraphicBuffer which will provide stronger + * API compatibility & consistency behaviors. + */ class GraphicBufferMapper : public Singleton<GraphicBufferMapper> { public: @@ -66,27 +74,50 @@ public: void getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts); - status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, - int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); + struct LockResult { + void* address = nullptr; + /** + * Note: bytesPerPixel is only populated if version is gralloc 3 + * Gralloc 4 & later should use instead getPlaneLayout() + */ + int32_t bytesPerPixel = -1; + /** + * Note: bytesPerPixel is only populated if version is gralloc 3 + * Gralloc 4 & later should use instead getPlaneLayout() + */ + int32_t bytesPerStride = -1; + }; + + ui::Result<LockResult> lock(buffer_handle_t handle, int64_t usage, const Rect& bounds, + base::unique_fd&& acquireFence = {}); + + ui::Result<android_ycbcr> lockYCbCr(buffer_handle_t handle, int64_t usage, const Rect& bounds, + base::unique_fd&& acquireFence = {}); + + status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr); status_t lockYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr); - status_t unlock(buffer_handle_t handle); - status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, - int fenceFd, int32_t* outBytesPerPixel = nullptr, - int32_t* outBytesPerStride = nullptr); + int fenceFd); status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, - const Rect& bounds, void** vaddr, int fenceFd, - int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); + const Rect& bounds, void** vaddr, int fenceFd); status_t lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd); - status_t unlockAsync(buffer_handle_t handle, int *fenceFd); + status_t unlock(buffer_handle_t handle, base::unique_fd* outFence = nullptr); + status_t unlockAsync(buffer_handle_t handle, int* fenceFd) { + base::unique_fd temp; + status_t result = unlock(handle, fenceFd ? &temp : nullptr); + if (fenceFd) { + *fenceFd = temp.release(); + } + return result; + } status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, bool* outSupported); @@ -122,6 +153,7 @@ public: status_t getChromaSiting(buffer_handle_t bufferHandle, ui::ChromaSiting* outChromaSiting); status_t getPlaneLayouts(buffer_handle_t bufferHandle, std::vector<ui::PlaneLayout>* outPlaneLayouts); + ui::Result<std::vector<ui::PlaneLayout>> getPlaneLayouts(buffer_handle_t bufferHandle); status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace); status_t setDataspace(buffer_handle_t bufferHandle, ui::Dataspace dataspace); status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode); diff --git a/libs/ui/include/ui/Result.h b/libs/ui/include/ui/Result.h new file mode 100644 index 0000000000..d73c3e23b7 --- /dev/null +++ b/libs/ui/include/ui/Result.h @@ -0,0 +1,121 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/expected.h> +#include <utils/Errors.h> + +namespace android::ui { + +enum class ErrorCode : int32_t { + /** + * No error. + */ + None = 0, + /** + * Invalid BufferDescriptor. + */ + BadDescriptor = 1, + /** + * Invalid buffer handle. + */ + BadBuffer = 2, + /** + * Invalid HardwareBufferDescription. + */ + BadValue = 3, + /** + * Resource unavailable. + */ + NoResources = 5, + /** + * Permanent failure. + */ + Unsupported = 7, +}; + +class Error { +public: + Error(ErrorCode err) : mCode(err) {} + + Error(ErrorCode err, std::string&& message) : mCode(err), mMessage(std::move(message)) {} + Error(ErrorCode err, const std::string_view& message) : mCode(err), mMessage(message) {} + + static constexpr status_t codeToStatus(ErrorCode code) { + switch (code) { + case ErrorCode::None: + return OK; + case ErrorCode::BadDescriptor: + return BAD_VALUE; + case ErrorCode::BadValue: + return BAD_VALUE; + case ErrorCode::BadBuffer: + return BAD_TYPE; + case ErrorCode::NoResources: + return NO_MEMORY; + case ErrorCode::Unsupported: + return INVALID_OPERATION; + default: + return UNKNOWN_ERROR; + } + } + + static constexpr ErrorCode statusToCode(status_t status) { + switch (status) { + case OK: + return ErrorCode::None; + case BAD_VALUE: + return ErrorCode::BadValue; + case BAD_TYPE: + return ErrorCode::BadBuffer; + case NO_MEMORY: + return ErrorCode::NoResources; + case INVALID_OPERATION: + return ErrorCode::Unsupported; + default: + return ErrorCode::Unsupported; + } + } + + constexpr status_t asStatus() const { return codeToStatus(mCode); } + + ErrorCode code() const { return mCode; } + + const std::string& message() const { return mMessage; } + + bool operator==(const ErrorCode code) { return mCode == code; } + +private: + ErrorCode mCode; + std::string mMessage; +}; + +template <typename T> +class Result : public base::expected<T, Error> { +public: + using base::expected<T, Error>::expected; + + [[nodiscard]] constexpr status_t asStatus() const { + return this->has_value() ? OK : this->error().asStatus(); + } + + [[nodiscard]] constexpr ErrorCode errorCode() const { + return this->has_value() ? ErrorCode::None : this->error().code(); + } +}; + +} // namespace android::ui diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 8ce017d7a3..2d8a1e326c 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -21,6 +21,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_libs_ui_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_test { @@ -54,6 +55,17 @@ cc_test { } cc_test { + name: "DisplayIdentification_test", + shared_libs: ["libui"], + static_libs: ["libgmock"], + srcs: ["DisplayIdentification_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], +} + +cc_test { name: "FlattenableHelpers_test", shared_libs: ["libui"], srcs: ["FlattenableHelpers_test.cpp"], diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp index f4c0afa71b..efca083e6e 100644 --- a/libs/ui/tests/GraphicBufferAllocator_test.cpp +++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp @@ -51,7 +51,7 @@ public: std::cout << "Setting expected stride to " << stride << std::endl; EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())), allocate) - .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err))); + .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err))); } std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; } }; diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h index d62e3e2192..d02b3873e0 100644 --- a/libs/ui/tests/mock/MockGrallocAllocator.h +++ b/libs/ui/tests/mock/MockGrallocAllocator.h @@ -35,7 +35,7 @@ public: MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override)); MOCK_METHOD(status_t, allocate, (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format, - uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + uint32_t layerCount, uint64_t usage, uint32_t* outStride, buffer_handle_t* outBufferHandles, bool less), (const, override)); }; diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp index 5d6070cf95..cc233b917b 100644 --- a/libs/ui/tools/Android.bp +++ b/libs/ui/tools/Android.bp @@ -21,6 +21,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_native_libs_ui_license"], + default_team: "trendy_team_android_core_graphics_stack", } cc_defaults { diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp index 80e911c65d..c97e496083 100644 --- a/libs/vibrator/ExternalVibration.cpp +++ b/libs/vibrator/ExternalVibration.cpp @@ -17,7 +17,7 @@ #include <vibrator/ExternalVibration.h> #include <vibrator/ExternalVibrationUtils.h> -#include <android/os/IExternalVibratorService.h> +#include <android/os/ExternalVibrationScale.h> #include <binder/Parcel.h> #include <log/log.h> #include <utils/Errors.h> @@ -65,24 +65,36 @@ inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const { return mToken == rhs.mToken; } -os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(int externalVibrationScale) { - switch (externalVibrationScale) { - case IExternalVibratorService::SCALE_MUTE: - return os::HapticScale::MUTE; - case IExternalVibratorService::SCALE_VERY_LOW: - return os::HapticScale::VERY_LOW; - case IExternalVibratorService::SCALE_LOW: - return os::HapticScale::LOW; - case IExternalVibratorService::SCALE_NONE: - return os::HapticScale::NONE; - case IExternalVibratorService::SCALE_HIGH: - return os::HapticScale::HIGH; - case IExternalVibratorService::SCALE_VERY_HIGH: - return os::HapticScale::VERY_HIGH; +os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale( + os::ExternalVibrationScale externalVibrationScale) { + os::HapticLevel scaleLevel = os::HapticLevel::NONE; + + switch (externalVibrationScale.scaleLevel) { + case os::ExternalVibrationScale::ScaleLevel::SCALE_MUTE: + scaleLevel = os::HapticLevel::MUTE; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_VERY_LOW: + scaleLevel = os::HapticLevel::VERY_LOW; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_LOW: + scaleLevel = os::HapticLevel::LOW; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_NONE: + scaleLevel = os::HapticLevel::NONE; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_HIGH: + scaleLevel = os::HapticLevel::HIGH; + break; + case os::ExternalVibrationScale::ScaleLevel::SCALE_VERY_HIGH: + scaleLevel = os::HapticLevel::VERY_HIGH; + break; default: - ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", externalVibrationScale); - return os::HapticScale::NONE; - } + ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", + externalVibrationScale.scaleLevel); + } + + return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/ + externalVibrationScale.adaptiveHapticsScale}; } } // namespace os diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp index 980b08ba36..761ac1bf3b 100644 --- a/libs/vibrator/ExternalVibrationUtils.cpp +++ b/libs/vibrator/ExternalVibrationUtils.cpp @@ -26,30 +26,30 @@ static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f; static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f; static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; -float getHapticScaleGamma(HapticScale scale) { - switch (scale) { - case HapticScale::VERY_LOW: +float getHapticScaleGamma(HapticLevel level) { + switch (level) { + case HapticLevel::VERY_LOW: return 2.0f; - case HapticScale::LOW: + case HapticLevel::LOW: return 1.5f; - case HapticScale::HIGH: + case HapticLevel::HIGH: return 0.5f; - case HapticScale::VERY_HIGH: + case HapticLevel::VERY_HIGH: return 0.25f; default: return 1.0f; } } -float getHapticMaxAmplitudeRatio(HapticScale scale) { - switch (scale) { - case HapticScale::VERY_LOW: +float getHapticMaxAmplitudeRatio(HapticLevel level) { + switch (level) { + case HapticLevel::VERY_LOW: return HAPTIC_SCALE_VERY_LOW_RATIO; - case HapticScale::LOW: + case HapticLevel::LOW: return HAPTIC_SCALE_LOW_RATIO; - case HapticScale::NONE: - case HapticScale::HIGH: - case HapticScale::VERY_HIGH: + case HapticLevel::NONE: + case HapticLevel::HIGH: + case HapticLevel::VERY_HIGH: return 1.0f; default: return 0.0f; @@ -57,19 +57,28 @@ float getHapticMaxAmplitudeRatio(HapticScale scale) { } void applyHapticScale(float* buffer, size_t length, HapticScale scale) { - if (scale == HapticScale::MUTE) { + if (scale.isScaleMute()) { memset(buffer, 0, length * sizeof(float)); return; } - if (scale == HapticScale::NONE) { + if (scale.isScaleNone()) { return; } - float gamma = getHapticScaleGamma(scale); - float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale); + HapticLevel hapticLevel = scale.getLevel(); + float adaptiveScaleFactor = scale.getAdaptiveScaleFactor(); + float gamma = getHapticScaleGamma(hapticLevel); + float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel); + for (size_t i = 0; i < length; i++) { - float sign = buffer[i] >= 0 ? 1.0 : -1.0; - buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) - * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + if (hapticLevel != HapticLevel::NONE) { + float sign = buffer[i] >= 0 ? 1.0 : -1.0; + buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + } + + if (adaptiveScaleFactor != 1.0f) { + buffer[i] *= adaptiveScaleFactor; + } } } @@ -89,13 +98,13 @@ void clipHapticData(float* buffer, size_t length, float limit) { } // namespace bool isValidHapticScale(HapticScale scale) { - switch (scale) { - case HapticScale::MUTE: - case HapticScale::VERY_LOW: - case HapticScale::LOW: - case HapticScale::NONE: - case HapticScale::HIGH: - case HapticScale::VERY_HIGH: + switch (scale.getLevel()) { + case HapticLevel::MUTE: + case HapticLevel::VERY_LOW: + case HapticLevel::LOW: + case HapticLevel::NONE: + case HapticLevel::HIGH: + case HapticLevel::VERY_HIGH: return true; } return false; diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h index 00cd3cd256..ac2767e17e 100644 --- a/libs/vibrator/include/vibrator/ExternalVibration.h +++ b/libs/vibrator/include/vibrator/ExternalVibration.h @@ -24,6 +24,7 @@ #include <system/audio.h> #include <utils/RefBase.h> #include <vibrator/ExternalVibrationUtils.h> +#include <android/os/ExternalVibrationScale.h> namespace android { namespace os { @@ -45,10 +46,11 @@ public : audio_attributes_t getAudioAttributes() const { return mAttrs; } sp<IExternalVibrationController> getController() { return mController; } - /* Converts the scale from non-public ExternalVibrationService into the HapticScale + /* Converts the scale from non-public ExternalVibrationService into the HapticScaleLevel * used by the utils. */ - static os::HapticScale externalVibrationScaleToHapticScale(int externalVibrationScale); + static os::HapticScale externalVibrationScaleToHapticScale( + os::ExternalVibrationScale externalVibrationScale); private: int32_t mUid; diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h index ca219d3cbf..d9a2b81ddf 100644 --- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -19,7 +19,7 @@ namespace android::os { -enum class HapticScale { +enum class HapticLevel { MUTE = -100, VERY_LOW = -2, LOW = -1, @@ -28,10 +28,41 @@ enum class HapticScale { VERY_HIGH = 2, }; +class HapticScale { +private: +HapticLevel mLevel = HapticLevel::NONE; +float mAdaptiveScaleFactor = 1.0f; + +public: +constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor) + : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {} +constexpr HapticScale(HapticLevel level) : mLevel(level) {} +constexpr HapticScale() {} + +HapticLevel getLevel() const { return mLevel; } +float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; } + +bool operator==(const HapticScale& other) const { + return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor; +} + +bool isScaleNone() const { + return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f; +} + +bool isScaleMute() const { + return mLevel == HapticLevel::MUTE; +} + +static HapticScale mute() { + return {/*level=*/os::HapticLevel::MUTE}; +} +}; + bool isValidHapticScale(HapticScale scale); -/* Scales the haptic data in given buffer using the selected HapticScale and ensuring no absolute - * value will be larger than the absolute of given limit. +/* Scales the haptic data in given buffer using the selected HapticScaleLevel and ensuring no + * absolute value will be larger than the absolute of given limit. * The limit will be ignored if it is NaN or zero. */ void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit); |