diff options
Diffstat (limited to 'libs')
78 files changed, 1767 insertions, 164 deletions
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 589df9aceb..ef0ae4f654 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -54,6 +54,11 @@ uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500; // Another arbitrary value a binder count needs to drop below before another callback will be called uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000; +std::atomic<uint32_t> BpBinder::sBinderProxyCount(0); +std::atomic<uint32_t> BpBinder::sBinderProxyCountWarned(0); + +static constexpr uint32_t kBinderProxyCountWarnInterval = 5000; + // Log any transactions for which the data exceeds this size #define LOG_TRANSACTIONS_OVER_SIZE (300 * 1024) @@ -193,6 +198,18 @@ sp<BpBinder> BpBinder::create(int32_t handle) { } sTrackingMap[trackedUid]++; } + uint32_t numProxies = sBinderProxyCount.fetch_add(1, std::memory_order_relaxed); + uint32_t numLastWarned = sBinderProxyCountWarned.load(std::memory_order_relaxed); + uint32_t numNextWarn = numLastWarned + kBinderProxyCountWarnInterval; + if (numProxies >= numNextWarn) { + // Multiple threads can get here, make sure only one of them gets to + // update the warn counter. + if (sBinderProxyCountWarned.compare_exchange_strong(numLastWarned, + numNextWarn, + std::memory_order_relaxed)) { + ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies); + } + } return sp<BpBinder>::make(BinderHandle{handle}, trackedUid); } @@ -604,6 +621,7 @@ BpBinder::~BpBinder() { } } } + --sBinderProxyCount; if (ipc) { ipc->expungeHandle(binderHandle(), this); @@ -688,6 +706,11 @@ uint32_t BpBinder::getBinderProxyCount(uint32_t uid) return 0; } +uint32_t BpBinder::getBinderProxyCount() +{ + return sBinderProxyCount.load(); +} + void BpBinder::getCountByUid(Vector<uint32_t>& uids, Vector<uint32_t>& counts) { AutoMutex _l(sTrackingLock); diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 5496d61b70..a5c6094362 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -87,6 +87,7 @@ public: static void setCountByUidEnabled(bool enable); static void setLimitCallback(binder_proxy_limit_callback cb); static void setBinderProxyCountWatermarks(int high, int low); + static uint32_t getBinderProxyCount(); std::optional<int32_t> getDebugBinderHandle() const; @@ -208,6 +209,8 @@ private: static uint32_t sBinderProxyCountLowWatermark; static bool sBinderProxyThrottleCreate; static std::unordered_map<int32_t,uint32_t> sLastLimitCallbackMap; + static std::atomic<uint32_t> sBinderProxyCount; + static std::atomic<uint32_t> sBinderProxyCountWarned; }; } // namespace android diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h index 76c7aacb7c..4786c89c89 100644 --- a/libs/binder/ndk/include_ndk/android/binder_status.h +++ b/libs/binder/ndk/include_ndk/android/binder_status.h @@ -25,6 +25,7 @@ #pragma once +#include <assert.h> #include <errno.h> #include <stdbool.h> #include <stdint.h> diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp index 8ee396e256..8fb755cdac 100644 --- a/libs/binder/ndk/tests/Android.bp +++ b/libs/binder/ndk/tests/Android.bp @@ -80,6 +80,28 @@ cc_test { require_root: true, } +cc_test_host { + name: "libbinder_ndk_unit_test_host", + defaults: ["test_libbinder_ndk_defaults"], + srcs: ["libbinder_ndk_unit_test_host.cpp"], + test_suites: [ + "general-tests", + ], + test_options: { + unit_test: true, + }, + static_libs: [ + "libbase", + "libbinder_ndk", + "libbinder", + "libcutils", + "libfakeservicemanager", + "libgmock", + "liblog", + "libutils", + ], +} + cc_test { name: "binderVendorDoubleLoadTest", vendor: true, diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 25b8e975b3..15708ca738 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -39,7 +39,6 @@ #include <condition_variable> #include <iostream> #include <mutex> -#include <optional> #include <thread> #include "android/binder_ibinder.h" @@ -433,21 +432,6 @@ TEST(NdkBinder, GetLazyService) { EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); } -TEST(NdkBinder, IsUpdatable) { - bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.light.ILights/default"); - EXPECT_EQ(isUpdatable, false); -} - -TEST(NdkBinder, GetUpdatableViaApex) { - std::optional<std::string> updatableViaApex; - AServiceManager_getUpdatableApexName( - "android.hardware.light.ILights/default", &updatableViaApex, - [](const char* apexName, void* context) { - *static_cast<std::optional<std::string>*>(context) = apexName; - }); - EXPECT_EQ(updatableViaApex, std::nullopt) << *updatableViaApex; -} - // This is too slow TEST(NdkBinder, CheckLazyServiceShutDown) { ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService)); diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp new file mode 100644 index 0000000000..0a3021d0b0 --- /dev/null +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test_host.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_manager.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <utils/String16.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + +#include <optional> + +#include "fakeservicemanager/FakeServiceManager.h" + +using android::FakeServiceManager; +using android::setDefaultServiceManager; +using android::sp; +using android::String16; +using android::String8; +using testing::_; +using testing::Eq; +using testing::Mock; +using testing::NiceMock; +using testing::Optional; +using testing::Return; + +struct MockServiceManager : FakeServiceManager { + MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&)); +}; + +struct AServiceManager : testing::Test { + static sp<MockServiceManager> mockSM; + + static void InitMock() { + mockSM = new NiceMock<MockServiceManager>; + setDefaultServiceManager(mockSM); + } + + void TearDown() override { Mock::VerifyAndClear(mockSM.get()); } + + void ExpectUpdatableViaApexReturns(std::optional<String16> apexName) { + EXPECT_CALL(*mockSM, updatableViaApex(_)).WillRepeatedly(Return(apexName)); + } +}; + +sp<MockServiceManager> AServiceManager::mockSM; + +TEST_F(AServiceManager, isUpdatableViaApex) { + auto apexFoo = String16("com.android.hardware.foo"); + ExpectUpdatableViaApexReturns(apexFoo); + + bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.foo.IFoo/default"); + EXPECT_EQ(isUpdatable, true); +} + +TEST_F(AServiceManager, isUpdatableViaApex_Not) { + ExpectUpdatableViaApexReturns(std::nullopt); + + bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.foo.IFoo/default"); + EXPECT_EQ(isUpdatable, false); +} + +void getUpdatableApexNameCallback(const char* apexName, void* context) { + *(static_cast<std::optional<std::string>*>(context)) = apexName; +} + +TEST_F(AServiceManager, getUpdatableApexName) { + auto apexFoo = String16("com.android.hardware.foo"); + ExpectUpdatableViaApexReturns(apexFoo); + + std::optional<std::string> result; + AServiceManager_getUpdatableApexName("android.hardware.foo.IFoo/default", &result, + getUpdatableApexNameCallback); + EXPECT_THAT(result, Optional(std::string(String8(apexFoo)))); +} + +TEST_F(AServiceManager, getUpdatableApexName_Null) { + ExpectUpdatableViaApexReturns(std::nullopt); + + std::optional<std::string> result; + AServiceManager_getUpdatableApexName("android.hardware.foo.IFoo/default", &result, + getUpdatableApexNameCallback); + EXPECT_THAT(result, Eq(std::nullopt)); +} + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + AServiceManager::InitMock(); + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index e021af0264..0396869167 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -1441,6 +1441,36 @@ TEST_F(BinderLibTest, HangingServices) { EXPECT_GE(epochMsAfter, epochMsBefore + delay); } +TEST_F(BinderLibTest, BinderProxyCount) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_NE(server, nullptr); + + uint32_t initialCount = BpBinder::getBinderProxyCount(); + size_t iterations = 100; + { + uint32_t count = initialCount; + std::vector<sp<IBinder> > proxies; + sp<IBinder> proxy; + // Create binder proxies and verify the count. + for (size_t i = 0; i < iterations; i++) { + ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); + proxies.push_back(reply.readStrongBinder()); + EXPECT_EQ(BpBinder::getBinderProxyCount(), ++count); + } + // Remove every other one and verify the count. + auto it = proxies.begin(); + for (size_t i = 0; it != proxies.end(); i++) { + if (i % 2 == 0) { + it = proxies.erase(it); + EXPECT_EQ(BpBinder::getBinderProxyCount(), --count); + } + } + } + EXPECT_EQ(BpBinder::getBinderProxyCount(), initialCount); +} + class BinderLibRpcTestBase : public BinderLibTest { public: void SetUp() override { diff --git a/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp index 09cb2162f7..b80ac53ba2 100644 --- a/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp @@ -16,6 +16,7 @@ #include <commonFuzzHelpers.h> #include <fuzzer/FuzzedDataProvider.h> +#include <functional> #include <string> #include <vector> #include "BufferedTextOutput.h" diff --git a/libs/bufferstreams/examples/app/Android.bp b/libs/bufferstreams/examples/app/Android.bp new file mode 100644 index 0000000000..0ecf94c48d --- /dev/null +++ b/libs/bufferstreams/examples/app/Android.bp @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_app { + name: "BufferStreamsDemoApp", + srcs: ["java/**/*.java"], + sdk_version: "current", + + jni_uses_platform_apis: true, + jni_libs: ["libbufferstreamdemoapp"], + use_embedded_native_libs: true, + + static_libs: [ + "androidx.appcompat_appcompat", + ], +} diff --git a/libs/bufferstreams/examples/app/AndroidManifest.xml b/libs/bufferstreams/examples/app/AndroidManifest.xml new file mode 100644 index 0000000000..872193c4e6 --- /dev/null +++ b/libs/bufferstreams/examples/app/AndroidManifest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.graphics.bufferstreamsdemoapp" + xmlns:tools="http://schemas.android.com/tools"> + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/Theme.AppCompat.Light" + tools:targetApi="34"> + <activity + android:name=".MainActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java new file mode 100644 index 0000000000..67b95a5349 --- /dev/null +++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.android.graphics.bufferstreamsdemoapp; + +import android.os.Bundle; +import android.widget.TextView; +import androidx.appcompat.app.AppCompatActivity; + +public class MainActivity extends AppCompatActivity { + // Used to load the 'bufferstreamsdemoapp' library on application startup. + static { System.loadLibrary("bufferstreamdemoapp"); } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + RunBufferQueue(); + System.out.println("stringFromJNI: " + stringFromJNI()); + } + + /** + * A native method that is implemented by the 'bufferstreamsdemoapp' native + * library, which is packaged with this application. + */ + public native String stringFromJNI(); + public native void RunBufferQueue(); +}
\ No newline at end of file diff --git a/libs/bufferstreams/examples/app/jni/Android.bp b/libs/bufferstreams/examples/app/jni/Android.bp new file mode 100644 index 0000000000..67910a1c4d --- /dev/null +++ b/libs/bufferstreams/examples/app/jni/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_shared { + name: "libbufferstreamdemoapp", + cflags: [ + "-Werror", + "-Wno-error=unused-parameter", + ], + shared_libs: [ + "libgui", + "libbase", + "libutils", + ], + header_libs: ["jni_headers"], + srcs: ["*.cpp"], +} diff --git a/libs/bufferstreams/examples/app/jni/main.cpp b/libs/bufferstreams/examples/app/jni/main.cpp new file mode 100644 index 0000000000..34e0eb451c --- /dev/null +++ b/libs/bufferstreams/examples/app/jni/main.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <jni.h> + +#include <gui/BufferQueue.h> + +extern "C" +{ + JNIEXPORT jstring JNICALL + Java_com_android_graphics_bufferstreamsdemoapp_MainActivity_stringFromJNI( + JNIEnv *env, + jobject /* this */) { + const char* hello = "Hello from C++"; + return env->NewStringUTF(hello); + } + + JNIEXPORT void JNICALL + Java_com_android_graphics_bufferstreamsdemoapp_MainActivity_RunBufferQueue( + JNIEnv *env, + jobject /* this */) { + android::sp<android::IGraphicBufferProducer> producer; + android::sp<android::IGraphicBufferConsumer> consumer; + android::BufferQueue::createBufferQueue(&producer, &consumer); + } +}
\ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/drawable/ic_launcher_background.xml b/libs/bufferstreams/examples/app/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..07d5da9cbf --- /dev/null +++ b/libs/bufferstreams/examples/app/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:fillColor="#3DDC84" + android:pathData="M0,0h108v108h-108z" /> + <path + android:fillColor="#00000000" + android:pathData="M9,0L9,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,0L19,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M29,0L29,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M39,0L39,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M49,0L49,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M59,0L59,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M69,0L69,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M79,0L79,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M89,0L89,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M99,0L99,108" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,9L108,9" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,19L108,19" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,29L108,29" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,39L108,39" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,49L108,49" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,59L108,59" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,69L108,69" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,79L108,79" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,89L108,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M0,99L108,99" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,29L89,29" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,39L89,39" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,49L89,49" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,59L89,59" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,69L89,69" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M19,79L89,79" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M29,19L29,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M39,19L39,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M49,19L49,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M59,19L59,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M69,19L69,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> + <path + android:fillColor="#00000000" + android:pathData="M79,19L79,89" + android:strokeWidth="0.8" + android:strokeColor="#33FFFFFF" /> +</vector> diff --git a/libs/bufferstreams/examples/app/res/drawable/ic_launcher_foreground.xml b/libs/bufferstreams/examples/app/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000000..2b068d1146 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="85.84757" + android:endY="92.4963" + android:startX="42.9492" + android:startY="49.59793" + android:type="linear"> + <item + android:color="#44000000" + android:offset="0.0" /> + <item + android:color="#00000000" + android:offset="1.0" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillColor="#FFFFFF" + android:fillType="nonZero" + android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" + android:strokeWidth="1" + android:strokeColor="#00000000" /> +</vector>
\ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/layout/activity_main.xml b/libs/bufferstreams/examples/app/res/layout/activity_main.xml new file mode 100644 index 0000000000..79fb331f09 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/layout/activity_main.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MainActivity"> + + <TextView + android:id="@+id/sample_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Hello World!" + tools:layout_editor_absoluteX="100dp" + tools:layout_editor_absoluteY="100dp" /> + +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher.xml b/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000000..6f3b755bf5 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> + <monochrome android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon>
\ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher_round.xml b/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000000..6f3b755bf5 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> + <monochrome android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon>
\ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000000..c209e78ecd --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000000..b2dfe3d1ba --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher_round.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000000..4f0f1d64e5 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000000..62b611da08 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher_round.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000000..948a3070fe --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000000..1b9a6956b3 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher_round.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000000..28d4b77f9f --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000000..9287f50836 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher_round.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp Binary files differnew file mode 100644 index 0000000000..aa7d6427e6 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher_round.webp b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher_round.webp Binary files differnew file mode 100644 index 0000000000..9126ae37cb --- /dev/null +++ b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/libs/bufferstreams/examples/app/res/values/colors.xml b/libs/bufferstreams/examples/app/res/values/colors.xml new file mode 100644 index 0000000000..f8c6127d32 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/values/colors.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="purple_200">#FFBB86FC</color> + <color name="purple_500">#FF6200EE</color> + <color name="purple_700">#FF3700B3</color> + <color name="teal_200">#FF03DAC5</color> + <color name="teal_700">#FF018786</color> + <color name="black">#FF000000</color> + <color name="white">#FFFFFFFF</color> +</resources>
\ No newline at end of file diff --git a/libs/bufferstreams/examples/app/res/values/strings.xml b/libs/bufferstreams/examples/app/res/values/strings.xml new file mode 100644 index 0000000000..e652102cb3 --- /dev/null +++ b/libs/bufferstreams/examples/app/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">Buffer Demos</string> +</resources>
\ No newline at end of file diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp index ff951487bc..7fcb222085 100644 --- a/libs/bufferstreams/rust/Android.bp +++ b/libs/bufferstreams/rust/Android.bp @@ -12,13 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -rust_library { - name: "libbufferstreams", - crate_name: "bufferstreams", +rust_defaults { + name: "libbufferstreams_defaults", srcs: ["src/lib.rs"], - edition: "2021", - rlibs: [ + rustlibs: [ + "libanyhow", "libnativewindow_rs", ], + edition: "2021", +} + +rust_library { + name: "libbufferstreams", + crate_name: "bufferstreams", + defaults: ["libbufferstreams_defaults"], min_sdk_version: "30", } + +rust_test { + name: "libbufferstreams-internal_test", + crate_name: "bufferstreams", + defaults: ["libbufferstreams_defaults"], + test_suites: ["general-tests"], +} diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs index 1d321c833d..87f3104915 100644 --- a/libs/bufferstreams/rust/src/lib.rs +++ b/libs/bufferstreams/rust/src/lib.rs @@ -14,8 +14,14 @@ //! libbufferstreams: Reactive Streams for Graphics Buffers +pub mod publishers; +mod stream_config; +pub mod subscribers; +pub mod subscriptions; + +pub use stream_config::*; + use nativewindow::*; -use std::sync::{Arc, Weak}; use std::time::Instant; /// This function will print Hello World. @@ -31,28 +37,30 @@ pub extern "C" fn hello() -> bool { /// /// BufferPublishers are required to adhere to the following, based on the /// reactive streams specification: -/// * The total number of on_next´s signalled by a Publisher to a Subscriber +/// * The total number of on_next´s signalled by a Publisher to a Subscriber /// MUST be less than or equal to the total number of elements requested by that /// Subscriber´s Subscription at all times. -/// * A Publisher MAY signal fewer on_next than requested and terminate the +/// * A Publisher MAY signal fewer on_next than requested and terminate the /// Subscription by calling on_complete or on_error. -/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber +/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber /// MUST be signaled serially. -/// * If a Publisher fails it MUST signal an on_error. -/// * If a Publisher terminates successfully (finite stream) it MUST signal an +/// * If a Publisher fails it MUST signal an on_error. +/// * If a Publisher terminates successfully (finite stream) it MUST signal an /// on_complete. -/// * If a Publisher signals either on_error or on_complete on a Subscriber, +/// * If a Publisher signals either on_error or on_complete on a Subscriber, /// that Subscriber’s Subscription MUST be considered cancelled. -/// * Once a terminal state has been signaled (on_error, on_complete) it is +/// * Once a terminal state has been signaled (on_error, on_complete) it is /// REQUIRED that no further signals occur. -/// * If a Subscription is cancelled its Subscriber MUST eventually stop being +/// * If a Subscription is cancelled its Subscriber MUST eventually stop being /// signaled. -/// * A Publisher MAY support multiple Subscribers and decides whether each +/// * A Publisher MAY support multiple Subscribers and decides whether each /// Subscription is unicast or multicast. pub trait BufferPublisher { + /// Returns the StreamConfig of buffers that publisher creates. + fn get_publisher_stream_config(&self) -> StreamConfig; /// This function will create the subscription between the publisher and /// the subscriber. - fn subscribe(&self, subscriber: Weak<dyn BufferSubscriber>); + fn subscribe(&mut self, subscriber: impl BufferSubscriber + 'static); } /// BufferSubscribers can subscribe to BufferPublishers. They can request Frames @@ -61,35 +69,37 @@ pub trait BufferPublisher { /// /// BufferSubcribers are required to adhere to the following, based on the /// reactive streams specification: -/// * The total number of on_next´s signalled by a Publisher to a Subscriber +/// * The total number of on_next´s signalled by a Publisher to a Subscriber /// MUST be less than or equal to the total number of elements requested by that /// Subscriber´s Subscription at all times. -/// * A Publisher MAY signal fewer on_next than requested and terminate the +/// * A Publisher MAY signal fewer on_next than requested and terminate the /// Subscription by calling on_complete or on_error. -/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber +/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber /// MUST be signaled serially. -/// * If a Publisher fails it MUST signal an on_error. -/// * If a Publisher terminates successfully (finite stream) it MUST signal an +/// * If a Publisher fails it MUST signal an on_error. +/// * If a Publisher terminates successfully (finite stream) it MUST signal an /// on_complete. -/// * If a Publisher signals either on_error or on_complete on a Subscriber, +/// * If a Publisher signals either on_error or on_complete on a Subscriber, /// that Subscriber’s Subscription MUST be considered cancelled. -/// * Once a terminal state has been signaled (on_error, on_complete) it is +/// * Once a terminal state has been signaled (on_error, on_complete) it is /// REQUIRED that no further signals occur. -/// * If a Subscription is cancelled its Subscriber MUST eventually stop being +/// * If a Subscription is cancelled its Subscriber MUST eventually stop being /// signaled. -/// * Publisher.subscribe MAY be called as many times as wanted but MUST be +/// * Publisher.subscribe MAY be called as many times as wanted but MUST be /// with a different Subscriber each time. -/// * A Publisher MAY support multiple Subscribers and decides whether each +/// * A Publisher MAY support multiple Subscribers and decides whether each /// Subscription is unicast or multicast. pub trait BufferSubscriber { + /// The StreamConfig of buffers that this subscriber expects. + fn get_subscriber_stream_config(&self) -> StreamConfig; /// This function will be called at the beginning of the subscription. - fn on_subscribe(&self, subscription: Arc<dyn BufferSubscription>); + fn on_subscribe(&mut self, subscription: Box<dyn BufferSubscription>); /// This function will be called for buffer that comes in. - fn on_next(&self, frame: Frame); + fn on_next(&mut self, frame: Frame); /// This function will be called in case of an error. - fn on_error(&self, error: BufferError); + fn on_error(&mut self, error: BufferError); /// This function will be called on finite streams when done. - fn on_complete(&self); + fn on_complete(&mut self); } /// BufferSubscriptions serve as the bridge between BufferPublishers and @@ -100,50 +110,51 @@ pub trait BufferSubscriber { /// /// BufferSubcriptions are required to adhere to the following, based on the /// reactive streams specification: -/// * Subscription.request and Subscription.cancel MUST only be called inside +/// * Subscription.request and Subscription.cancel MUST only be called inside /// of its Subscriber context. -/// * The Subscription MUST allow the Subscriber to call Subscription.request +/// * The Subscription MUST allow the Subscriber to call Subscription.request /// synchronously from within on_next or on_subscribe. -/// * Subscription.request MUST place an upper bound on possible synchronous +/// * Subscription.request MUST place an upper bound on possible synchronous /// recursion between Publisher and Subscriber. -/// * Subscription.request SHOULD respect the responsivity of its caller by +/// * Subscription.request SHOULD respect the responsivity of its caller by /// returning in a timely manner. -/// * Subscription.cancel MUST respect the responsivity of its caller by +/// * Subscription.cancel MUST respect the responsivity of its caller by /// returning in a timely manner, MUST be idempotent and MUST be thread-safe. -/// * After the Subscription is cancelled, additional +/// * After the Subscription is cancelled, additional /// Subscription.request(n: u64) MUST be NOPs. -/// * After the Subscription is cancelled, additional Subscription.cancel() +/// * After the Subscription is cancelled, additional Subscription.cancel() /// MUST be NOPs. -/// * While the Subscription is not cancelled, Subscription.request(n: u64) +/// * While the Subscription is not cancelled, Subscription.request(n: u64) /// MUST register the given number of additional elements to be produced to the /// respective subscriber. -/// * While the Subscription is not cancelled, Subscription.request(n: u64) +/// * While the Subscription is not cancelled, Subscription.request(n: u64) /// MUST signal on_error if the argument is <= 0. The cause message SHOULD /// explain that non-positive request signals are illegal. -/// * While the Subscription is not cancelled, Subscription.request(n: u64) +/// * While the Subscription is not cancelled, Subscription.request(n: u64) /// MAY synchronously call on_next on this (or other) subscriber(s). -/// * While the Subscription is not cancelled, Subscription.request(n: u64) +/// * While the Subscription is not cancelled, Subscription.request(n: u64) /// MAY synchronously call on_complete or on_error on this (or other) /// subscriber(s). -/// * While the Subscription is not cancelled, Subscription.cancel() MUST +/// * While the Subscription is not cancelled, Subscription.cancel() MUST /// request the Publisher to eventually stop signaling its Subscriber. The /// operation is NOT REQUIRED to affect the Subscription immediately. -/// * While the Subscription is not cancelled, Subscription.cancel() MUST +/// * While the Subscription is not cancelled, Subscription.cancel() MUST /// request the Publisher to eventually drop any references to the corresponding /// subscriber. -/// * While the Subscription is not cancelled, calling Subscription.cancel MAY +/// * While the Subscription is not cancelled, calling Subscription.cancel MAY /// cause the Publisher, if stateful, to transition into the shut-down state if /// no other Subscription exists at this point. -/// * Calling Subscription.cancel MUST return normally. -/// * Calling Subscription.request MUST return normally. +/// * Calling Subscription.cancel MUST return normally. +/// * Calling Subscription.request MUST return normally. pub trait BufferSubscription { /// request fn request(&self, n: u64); /// cancel fn cancel(&self); } + /// Type used to describe errors produced by subscriptions. -type BufferError = Box<dyn std::error::Error + Send + Sync + 'static>; +pub type BufferError = anyhow::Error; /// Struct used to contain the buffer. pub struct Frame { @@ -154,3 +165,88 @@ pub struct Frame { /// A fence used for reading/writing safely. pub fence: i32, } + +#[cfg(test)] +mod test { + #![allow(warnings, unused)] + use super::*; + + use anyhow::anyhow; + use std::borrow::BorrowMut; + use std::error::Error; + use std::ops::Add; + use std::sync::Arc; + use std::time::Duration; + + use crate::publishers::testing::*; + use crate::subscribers::{testing::*, SharedSubscriber}; + + const STREAM_CONFIG: StreamConfig = StreamConfig { + width: 1, + height: 1, + layers: 1, + format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + stride: 0, + }; + + fn make_frame() -> Frame { + Frame { + buffer: STREAM_CONFIG + .create_hardware_buffer() + .expect("Unable to create hardware buffer for test"), + present_time: Instant::now() + Duration::from_secs(1), + fence: 0, + } + } + + #[test] + fn test_test_implementations_next() { + let subscriber = SharedSubscriber::new(TestSubscriber::new(STREAM_CONFIG)); + let mut publisher = TestPublisher::new(STREAM_CONFIG); + + publisher.subscribe(subscriber.clone()); + assert!(subscriber.map_inner(|s| s.has_subscription())); + assert!(publisher.has_subscriber()); + + publisher.send_frame(make_frame()); + let events = subscriber.map_inner_mut(|s| s.take_events()); + assert!(!matches!(events.last().unwrap(), TestingSubscriberEvent::Next(_))); + + subscriber.map_inner(|s| s.request(1)); + assert_eq!(publisher.pending_requests(), 1); + + publisher.send_frame(make_frame()); + let events = subscriber.map_inner_mut(|s| s.take_events()); + assert!(matches!(events.last().unwrap(), TestingSubscriberEvent::Next(_))); + assert_eq!(publisher.pending_requests(), 0); + } + + #[test] + fn test_test_implementations_complete() { + let subscriber = SharedSubscriber::new(TestSubscriber::new(STREAM_CONFIG)); + let mut publisher = TestPublisher::new(STREAM_CONFIG); + + publisher.subscribe(subscriber.clone()); + assert!(subscriber.map_inner(|s| s.has_subscription())); + assert!(publisher.has_subscriber()); + + publisher.send_complete(); + let events = subscriber.map_inner_mut(|s| s.take_events()); + assert!(matches!(events.last().unwrap(), TestingSubscriberEvent::Complete)); + } + + #[test] + fn test_test_implementations_error() { + let subscriber = SharedSubscriber::new(TestSubscriber::new(STREAM_CONFIG)); + let mut publisher = TestPublisher::new(STREAM_CONFIG); + + publisher.subscribe(subscriber.clone()); + assert!(subscriber.map_inner(|s| s.has_subscription())); + assert!(publisher.has_subscriber()); + + publisher.send_error(anyhow!("error")); + let events = subscriber.map_inner_mut(|s| s.take_events()); + assert!(matches!(events.last().unwrap(), TestingSubscriberEvent::Error(_))); + } +} diff --git a/libs/bufferstreams/rust/src/publishers/mod.rs b/libs/bufferstreams/rust/src/publishers/mod.rs new file mode 100644 index 0000000000..2fd518efee --- /dev/null +++ b/libs/bufferstreams/rust/src/publishers/mod.rs @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module provides [BufferSubscriber] implementations and helpers. + +pub mod testing; diff --git a/libs/bufferstreams/rust/src/publishers/testing.rs b/libs/bufferstreams/rust/src/publishers/testing.rs new file mode 100644 index 0000000000..1593b18d7f --- /dev/null +++ b/libs/bufferstreams/rust/src/publishers/testing.rs @@ -0,0 +1,103 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provides useful publishers for testing specifically. These should not be used in normal code. + +use crate::{subscriptions::SharedBufferSubscription, *}; + +/// A [BufferPublisher] specifically for testing. +/// +/// Provides users the ability to send events and read the state of the subscription. +pub struct TestPublisher { + config: StreamConfig, + subscriber: Option<Box<dyn BufferSubscriber>>, + subscription: SharedBufferSubscription, +} + +impl TestPublisher { + /// Create a new [TestPublisher]. + pub fn new(config: StreamConfig) -> Self { + Self { config, subscriber: None, subscription: SharedBufferSubscription::new() } + } + + /// Send a [BufferSubscriber::on_next] event to an owned [BufferSubscriber] if it has any + /// requested and returns true. Drops the frame and returns false otherwise. + /// + /// # Panics + /// + /// This will panic if there is no owned subscriber. + pub fn send_frame(&mut self, frame: Frame) -> bool { + let subscriber = + self.subscriber.as_deref_mut().expect("Tried to send_frame with no subscriber"); + + if self.subscription.take_request() { + subscriber.on_next(frame); + true + } else { + false + } + } + + /// Send a [BufferSubscriber::on_complete] event to an owned [BufferSubscriber]. + /// + /// # Panics + /// + /// This will panic if there is no owned subscriber. + pub fn send_complete(&mut self) { + let subscriber = + self.subscriber.as_deref_mut().expect("Tried to send_complete with no subscriber"); + subscriber.on_complete(); + } + + /// Send a [BufferSubscriber::on_error] event to an owned [BufferSubscriber]. + /// + /// # Panics + /// + /// This will panic if there is no owned subscriber. + pub fn send_error(&mut self, error: BufferError) { + let subscriber = + self.subscriber.as_deref_mut().expect("Tried to send_error with no subscriber"); + subscriber.on_error(error); + } + + /// Returns whether this [BufferPublisher] owns a subscriber. + pub fn has_subscriber(&self) -> bool { + self.subscriber.is_some() + } + + /// Returns the nummber of frames requested by the [BufferSubscriber]. + pub fn pending_requests(&self) -> u64 { + self.subscription.pending_requests() + } + + /// Returns whether the [BufferSubscriber] has cancelled the subscription. + pub fn is_cancelled(&self) -> bool { + self.subscription.is_cancelled() + } +} + +impl BufferPublisher for TestPublisher { + fn get_publisher_stream_config(&self) -> crate::StreamConfig { + self.config + } + + fn subscribe(&mut self, subscriber: impl BufferSubscriber + 'static) { + assert!(self.subscriber.is_none(), "TestingPublishers can only take one subscriber"); + self.subscriber = Some(Box::new(subscriber)); + + if let Some(ref mut subscriber) = self.subscriber { + subscriber.on_subscribe(self.subscription.clone_for_subscriber()); + } + } +} diff --git a/libs/bufferstreams/rust/src/stream_config.rs b/libs/bufferstreams/rust/src/stream_config.rs new file mode 100644 index 0000000000..d0c621b0c4 --- /dev/null +++ b/libs/bufferstreams/rust/src/stream_config.rs @@ -0,0 +1,67 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use nativewindow::*; + +/// The configuration of the buffers published by a [BufferPublisher] or +/// expected by a [BufferSubscriber]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct StreamConfig { + /// Width in pixels of streaming buffers. + pub width: u32, + /// Height in pixels of streaming buffers. + pub height: u32, + /// Number of layers of streaming buffers. + pub layers: u32, + /// Format of streaming buffers. + pub format: AHardwareBuffer_Format::Type, + /// Usage of streaming buffers. + pub usage: AHardwareBuffer_UsageFlags, + /// Stride of streaming buffers. + pub stride: u32, +} + +impl StreamConfig { + /// Tries to create a new AHardwareBuffer from settings in a [StreamConfig]. + pub fn create_hardware_buffer(&self) -> Option<AHardwareBuffer> { + AHardwareBuffer::new(self.width, self.height, self.layers, self.format, self.usage) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_create_hardware_buffer() { + let config = StreamConfig { + width: 123, + height: 456, + layers: 1, + format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + usage: AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN + | AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + stride: 0, + }; + + let maybe_buffer = config.create_hardware_buffer(); + assert!(maybe_buffer.is_some()); + + let buffer = maybe_buffer.unwrap(); + assert_eq!(config.width, buffer.width()); + assert_eq!(config.height, buffer.height()); + assert_eq!(config.format, buffer.format()); + assert_eq!(config.usage, buffer.usage()); + } +} diff --git a/libs/bufferstreams/rust/src/subscribers/mod.rs b/libs/bufferstreams/rust/src/subscribers/mod.rs new file mode 100644 index 0000000000..dd038c6c32 --- /dev/null +++ b/libs/bufferstreams/rust/src/subscribers/mod.rs @@ -0,0 +1,20 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module provides [BufferSubscriber] implementations and helpers. + +mod shared; +pub mod testing; + +pub use shared::*; diff --git a/libs/bufferstreams/rust/src/subscribers/shared.rs b/libs/bufferstreams/rust/src/subscribers/shared.rs new file mode 100644 index 0000000000..46c58dc04a --- /dev/null +++ b/libs/bufferstreams/rust/src/subscribers/shared.rs @@ -0,0 +1,94 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module provides [BufferSubscriber] implementations and helpers. + +use std::sync::{Arc, Mutex}; + +use crate::*; + +/// A [BufferSubscriber] wrapper that provides shared access. +/// +/// Normally, [BufferSubscriber]s are fully owned by the publisher that they are attached to. With +/// [SharedSubscriber], a +/// +/// # Panics +/// +/// [BufferSubscriber::on_subscribe] on a [SharedSubscriber] can only be called once, otherwise it +/// will panic. This is to prevent accidental and unsupported sharing between multiple publishers to +/// reflect the usual behavior where a publisher takes full ownership of a subscriber. +pub struct SharedSubscriber<S: BufferSubscriber>(Arc<Mutex<SharedSubscriberInner<S>>>); + +struct SharedSubscriberInner<S: BufferSubscriber> { + subscriber: S, + is_subscribed: bool, +} + +impl<S: BufferSubscriber> SharedSubscriber<S> { + /// Create a new wrapper around a [BufferSubscriber]. + pub fn new(subscriber: S) -> Self { + Self(Arc::new(Mutex::new(SharedSubscriberInner { subscriber, is_subscribed: false }))) + } + + /// Provides access to an immutable reference to the wrapped [BufferSubscriber]. + pub fn map_inner<R, F: FnOnce(&S) -> R>(&self, f: F) -> R { + let inner = self.0.lock().unwrap(); + f(&inner.subscriber) + } + + /// Provides access to a mutable reference to the wrapped [BufferSubscriber]. + pub fn map_inner_mut<R, F: FnOnce(&mut S) -> R>(&self, f: F) -> R { + let mut inner = self.0.lock().unwrap(); + f(&mut inner.subscriber) + } +} + +impl<S: BufferSubscriber> Clone for SharedSubscriber<S> { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} + +impl<S: BufferSubscriber> BufferSubscriber for SharedSubscriber<S> { + fn get_subscriber_stream_config(&self) -> StreamConfig { + let inner = self.0.lock().unwrap(); + inner.subscriber.get_subscriber_stream_config() + } + + fn on_subscribe(&mut self, subscription: Box<dyn BufferSubscription>) { + let mut inner = self.0.lock().unwrap(); + assert!( + !inner.is_subscribed, + "A SharedSubscriber can not be shared between two BufferPublishers" + ); + inner.is_subscribed = true; + + inner.subscriber.on_subscribe(subscription); + } + + fn on_next(&mut self, frame: Frame) { + let mut inner = self.0.lock().unwrap(); + inner.subscriber.on_next(frame); + } + + fn on_error(&mut self, error: BufferError) { + let mut inner = self.0.lock().unwrap(); + inner.subscriber.on_error(error); + } + + fn on_complete(&mut self) { + let mut inner = self.0.lock().unwrap(); + inner.subscriber.on_complete(); + } +} diff --git a/libs/bufferstreams/rust/src/subscribers/testing.rs b/libs/bufferstreams/rust/src/subscribers/testing.rs new file mode 100644 index 0000000000..b7e970579e --- /dev/null +++ b/libs/bufferstreams/rust/src/subscribers/testing.rs @@ -0,0 +1,106 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provides useful subscribers for testing specifically. These should not be used in normal code. + +use crate::*; + +/// Represents a callback called by a [BufferPublisher] on a [BufferSubscriber]. +pub enum TestingSubscriberEvent { + /// Represents a call to [BufferSubscriber::on_subscribe]. + Subscribe, + /// Represents a call to [BufferSubscriber::on_next]. + Next(Frame), + /// Represents a call to [BufferSubscriber::on_error]. + Error(BufferError), + /// Represents a call to [BufferSubscriber::on_complete]. + Complete, +} + +/// A [BufferSubscriber] specifically for testing. Logs events as they happen which can be retrieved +/// by the test to ensure appropriate behavior. +pub struct TestSubscriber { + config: StreamConfig, + subscription: Option<Box<dyn BufferSubscription>>, + events: Vec<TestingSubscriberEvent>, +} + +impl TestSubscriber { + /// Create a new [TestSubscriber]. + pub fn new(config: StreamConfig) -> Self { + Self { config, subscription: None, events: Vec::new() } + } + + /// Returns true if this [BufferSubscriber] has an active subscription. + pub fn has_subscription(&self) -> bool { + self.subscription.is_some() + } + + /// Make a request on behalf of this test subscriber. + /// + /// This will panic if there is no owned subscription. + pub fn request(&self, n: u64) { + let subscription = self + .subscription + .as_deref() + .expect("Tried to request on a TestSubscriber with no subscription"); + subscription.request(n); + } + + /// Cancel on behalf of this test subscriber. + /// + /// # Panics + /// + /// This will panic if there is no owned subscription. + pub fn cancel(&self) { + let subscription = self + .subscription + .as_deref() + .expect("Tried to cancel a TestSubscriber with no subscription"); + subscription.cancel(); + } + + /// Gets all of the events that have happened to this [BufferSubscriber] since the last call + /// to this function or it was created. + pub fn take_events(&mut self) -> Vec<TestingSubscriberEvent> { + let mut out = Vec::new(); + out.append(&mut self.events); + out + } +} + +impl BufferSubscriber for TestSubscriber { + fn get_subscriber_stream_config(&self) -> StreamConfig { + self.config + } + + fn on_subscribe(&mut self, subscription: Box<dyn BufferSubscription>) { + assert!(self.subscription.is_none(), "TestSubscriber must only be subscribed to once"); + self.subscription = Some(subscription); + + self.events.push(TestingSubscriberEvent::Subscribe); + } + + fn on_next(&mut self, frame: Frame) { + self.events.push(TestingSubscriberEvent::Next(frame)); + } + + fn on_error(&mut self, error: BufferError) { + self.events.push(TestingSubscriberEvent::Error(error)); + } + + fn on_complete(&mut self) { + self.events.push(TestingSubscriberEvent::Complete); + } +} diff --git a/libs/bufferstreams/rust/src/subscriptions/mod.rs b/libs/bufferstreams/rust/src/subscriptions/mod.rs new file mode 100644 index 0000000000..e046dbbda3 --- /dev/null +++ b/libs/bufferstreams/rust/src/subscriptions/mod.rs @@ -0,0 +1,19 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module provides [BufferSubscription] implementations and helpers. + +mod shared_buffer_subscription; + +pub use shared_buffer_subscription::*; diff --git a/libs/bufferstreams/rust/src/subscriptions/shared_buffer_subscription.rs b/libs/bufferstreams/rust/src/subscriptions/shared_buffer_subscription.rs new file mode 100644 index 0000000000..90275c7320 --- /dev/null +++ b/libs/bufferstreams/rust/src/subscriptions/shared_buffer_subscription.rs @@ -0,0 +1,84 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::{Arc, Mutex}; + +use crate::*; + +/// A simple sharable helper that can be used as a [BufferSubscription] by a [BufferSubscriber] and +/// as a state tracker by a [BufferPublisher]. +#[derive(Clone, Debug)] +pub struct SharedBufferSubscription(Arc<Mutex<BufferSubscriptionData>>); + +#[derive(Debug, Default)] +struct BufferSubscriptionData { + requests: u64, + is_cancelled: bool, +} + +impl SharedBufferSubscription { + /// Create a new [SharedBufferSubscription]. + pub fn new() -> Self { + SharedBufferSubscription::default() + } + + /// Clone this [SharedBufferSubscription] so it can be passed into + /// [BufferSubscriber::on_subscribe]. + pub fn clone_for_subscriber(&self) -> Box<dyn BufferSubscription> { + Box::new(self.clone()) as Box<dyn BufferSubscription> + } + + /// If possible (not cancelled and with requests pending), take + pub fn take_request(&self) -> bool { + let mut data = self.0.lock().unwrap(); + + if data.is_cancelled || data.requests == 0 { + false + } else { + data.requests -= 1; + true + } + } + + /// Get the number of pending requests made by the [BufferSubscriber] via + /// [BufferSubscription::request]. + pub fn pending_requests(&self) -> u64 { + self.0.lock().unwrap().requests + } + + /// Get get whether the [BufferSubscriber] has called [BufferSubscription::cancel]. + pub fn is_cancelled(&self) -> bool { + self.0.lock().unwrap().is_cancelled + } +} + +impl Default for SharedBufferSubscription { + fn default() -> Self { + Self(Arc::new(Mutex::new(BufferSubscriptionData::default()))) + } +} + +impl BufferSubscription for SharedBufferSubscription { + fn request(&self, n: u64) { + let mut data = self.0.lock().unwrap(); + if !data.is_cancelled { + data.requests = data.requests.saturating_add(n); + } + } + + fn cancel(&self) { + let mut data = self.0.lock().unwrap(); + data.is_cancelled = true; + } +} diff --git a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp index f835997187..9df5632e97 100644 --- a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp +++ b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp @@ -20,6 +20,7 @@ #include <fuzzer/FuzzedDataProvider.h> #include <android-base/unique_fd.h> #include <cputimeinstate.h> +#include <functional> using namespace android::bpf; diff --git a/libs/ftl/OWNERS b/libs/ftl/OWNERS new file mode 100644 index 0000000000..3f6129226a --- /dev/null +++ b/libs/ftl/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/native:/services/surfaceflinger/OWNERS
\ No newline at end of file diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp index 5592a01fde..b68c2c3d02 100644 --- a/libs/ftl/enum_test.cpp +++ b/libs/ftl/enum_test.cpp @@ -33,6 +33,11 @@ static_assert(ftl::enum_name<E::ftl_last>() == "F"); static_assert(ftl::enum_name(E::C).value_or("?") == "C"); static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); +static_assert(ftl::enum_name_full<E::B>() == "E::B"); +static_assert(ftl::enum_name_full<E::ftl_last>() == "E::F"); +static_assert(ftl::enum_name_full(E::C).value_or("?") == "E::C"); +static_assert(ftl::enum_name_full(E{3}).value_or("?") == "?"); + enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; static_assert(ftl::enum_begin_v<F> == F{0}); @@ -60,6 +65,10 @@ static_assert(ftl::enum_name<Flags::kNone>() == "kNone"); static_assert(ftl::enum_name<Flags::kFlag4>() == "kFlag4"); static_assert(ftl::enum_name<Flags::kFlag7>() == "kFlag7"); +static_assert(ftl::enum_name_full<Flags::kNone>() == "Flags::kNone"); +static_assert(ftl::enum_name_full<Flags::kFlag4>() == "Flags::kFlag4"); +static_assert(ftl::enum_name_full<Flags::kFlag7>() == "Flags::kFlag7"); + // Though not flags, the enumerators are within the implicit range of bit indices. enum class Planet : std::uint8_t { kMercury, @@ -81,6 +90,9 @@ static_assert(ftl::enum_size_v<Planet> == 8); static_assert(ftl::enum_name<Planet::kMercury>() == "kMercury"); static_assert(ftl::enum_name<Planet::kSaturn>() == "kSaturn"); +static_assert(ftl::enum_name_full<Planet::kMercury>() == "Planet::kMercury"); +static_assert(ftl::enum_name_full<Planet::kSaturn>() == "Planet::kSaturn"); + // Unscoped enum must define explicit range, even if the underlying type is fixed. enum Temperature : int { kRoom = 20, @@ -122,16 +134,28 @@ TEST(Enum, Name) { EXPECT_EQ(ftl::enum_name(Planet::kEarth), "kEarth"); EXPECT_EQ(ftl::enum_name(Planet::kNeptune), "kNeptune"); + EXPECT_EQ(ftl::enum_name_full(Planet::kEarth), "Planet::kEarth"); + EXPECT_EQ(ftl::enum_name_full(Planet::kNeptune), "Planet::kNeptune"); + EXPECT_EQ(ftl::enum_name(kPluto), std::nullopt); + EXPECT_EQ(ftl::enum_name_full(kPluto), std::nullopt); } { EXPECT_EQ(ftl::enum_name(kRoom), "kRoom"); EXPECT_EQ(ftl::enum_name(kFridge), "kFridge"); EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer"); + EXPECT_EQ(ftl::enum_name(kRoom), "kRoom"); + EXPECT_EQ(ftl::enum_name(kFridge), "kFridge"); + EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer"); + EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(-30)), std::nullopt); EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(0)), std::nullopt); EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(100)), std::nullopt); + + EXPECT_EQ(ftl::enum_name_full(static_cast<Temperature>(-30)), std::nullopt); + EXPECT_EQ(ftl::enum_name_full(static_cast<Temperature>(0)), std::nullopt); + EXPECT_EQ(ftl::enum_name_full(static_cast<Temperature>(100)), std::nullopt); } } @@ -158,16 +182,30 @@ TEST(Enum, String) { EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth"); EXPECT_EQ(ftl::enum_string(Planet::kNeptune), "kNeptune"); + EXPECT_EQ(ftl::enum_string_full(Planet::kEarth), "Planet::kEarth"); + EXPECT_EQ(ftl::enum_string_full(Planet::kNeptune), "Planet::kNeptune"); + EXPECT_EQ(ftl::enum_string(kPluto), "8"); + + EXPECT_EQ(ftl::enum_string_full(kPluto), "8"); + } { EXPECT_EQ(ftl::enum_string(kRoom), "kRoom"); EXPECT_EQ(ftl::enum_string(kFridge), "kFridge"); EXPECT_EQ(ftl::enum_string(kFreezer), "kFreezer"); + EXPECT_EQ(ftl::enum_string_full(kRoom), "20"); + EXPECT_EQ(ftl::enum_string_full(kFridge), "4"); + EXPECT_EQ(ftl::enum_string_full(kFreezer), "-18"); + EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(-30)), "-30"); EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(0)), "0"); EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(100)), "100"); + + EXPECT_EQ(ftl::enum_string_full(static_cast<Temperature>(-30)), "-30"); + EXPECT_EQ(ftl::enum_string_full(static_cast<Temperature>(0)), "0"); + EXPECT_EQ(ftl::enum_string_full(static_cast<Temperature>(100)), "100"); } } diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 2ea4d16260..9a27d2321b 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -204,27 +204,8 @@ cc_aconfig_library { aconfig_declarations: "libgui_flags", } -cc_library_shared { - name: "libgui", - vendor_available: true, - vndk: { - enabled: true, - private: true, - }, - double_loadable: true, - - defaults: ["libgui_bufferqueue-defaults"], - - static_libs: [ - "libgui_aidl_static", - "libgui_window_info_static", - "libguiflags", - ], - export_static_lib_headers: [ - "libgui_aidl_static", - "libgui_window_info_static", - ], - +filegroup { + name: "libgui-sources", srcs: [ ":framework_native_aidl_binder", ":framework_native_aidl_gui", @@ -268,11 +249,40 @@ cc_library_shared { "bufferqueue/2.0/B2HProducerListener.cpp", "bufferqueue/2.0/H2BGraphicBufferProducer.cpp", ], +} +cc_defaults { + name: "libgui-defaults", + defaults: ["libgui_bufferqueue-defaults"], + srcs: [":libgui-sources"], + static_libs: [ + "libgui_aidl_static", + "libgui_window_info_static", + "libguiflags", + ], shared_libs: [ "libbinder", "libGLESv2", ], +} + +cc_library_shared { + name: "libgui", + vendor_available: true, + vndk: { + enabled: true, + private: true, + }, + double_loadable: true, + + defaults: [ + "libgui-defaults", + ], + + export_static_lib_headers: [ + "libgui_aidl_static", + "libgui_window_info_static", + ], export_shared_lib_headers: [ "libbinder", @@ -346,6 +356,7 @@ filegroup { "BufferQueueProducer.cpp", "BufferQueueThreadState.cpp", "BufferSlot.cpp", + "FrameRateUtils.cpp", "FrameTimestamps.cpp", "GLConsumerUtils.cpp", "HdrMetadata.cpp", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 207fa4fd31..dd0a028865 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -26,6 +26,8 @@ #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> +#include <gui/Flags.h> +#include <gui/FrameRateUtils.h> #include <gui/GLConsumer.h> #include <gui/IProducerListener.h> #include <gui/Surface.h> @@ -39,6 +41,9 @@ #include <android-base/thread_annotations.h> #include <chrono> +#include <com_android_graphics_libgui_flags.h> + +using namespace com::android::graphics::libgui; using namespace std::chrono_literals; namespace { @@ -139,6 +144,16 @@ void BLASTBufferItemConsumer::onSidebandStreamChanged() { } } +#if FLAG_BQ_SET_FRAME_RATE +void BLASTBufferItemConsumer::onSetFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) { + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (bbq != nullptr) { + bbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy); + } +} +#endif + void BLASTBufferItemConsumer::resizeFrameEventHistory(size_t newSize) { Mutex::Autolock lock(mMutex); mFrameEventHistory.resize(newSize); @@ -890,6 +905,10 @@ public: status_t setFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override { + if (flags::bq_setframerate()) { + return Surface::setFrameRate(frameRate, compatibility, changeFrameRateStrategy); + } + std::lock_guard _lock{mMutex}; if (mDestroyed) { return DEAD_OBJECT; diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index 66cad03fec..ab0f6d213f 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -22,6 +22,7 @@ #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> +#include <gui/Flags.h> namespace android { @@ -98,6 +99,16 @@ void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps( } } +#if FLAG_BQ_SET_FRAME_RATE +void BufferQueue::ProxyConsumerListener::onSetFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) { + sp<ConsumerListener> listener(mConsumerListener.promote()); + if (listener != nullptr) { + listener->onSetFrameRate(frameRate, compatibility, changeFrameRateStrategy); + } +} +#endif + void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) { diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 920b83dba9..67dff6dec6 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -32,6 +32,8 @@ #include <gui/BufferItem.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> +#include <gui/Flags.h> +#include <gui/FrameRateUtils.h> #include <gui/GLConsumer.h> #include <gui/IConsumerListener.h> #include <gui/IProducerListener.h> @@ -1751,4 +1753,27 @@ status_t BufferQueueProducer::setAutoPrerotation(bool autoPrerotation) { return NO_ERROR; } +#if FLAG_BQ_SET_FRAME_RATE +status_t BufferQueueProducer::setFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) { + ATRACE_CALL(); + BQ_LOGV("setFrameRate: %.2f", frameRate); + + if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy, + "BufferQueueProducer::setFrameRate")) { + return BAD_VALUE; + } + + sp<IConsumerListener> listener; + { + std::lock_guard<std::mutex> lock(mCore->mMutex); + listener = mCore->mConsumerListener; + } + if (listener != nullptr) { + listener->onSetFrameRate(frameRate, compatibility, changeFrameRateStrategy); + } + return NO_ERROR; +} +#endif + } // namespace android diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 46fb068dee..93df12471d 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -324,6 +324,12 @@ void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool c to_string(displayId).c_str(), toString(connected)); } +void Choreographer::dispatchHotplugConnectionError(nsecs_t, int32_t connectionError) { + ALOGV("choreographer %p ~ received hotplug connection error event (connectionError=%d), " + "ignoring.", + this, connectionError); +} + void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) { LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered"); } @@ -394,4 +400,4 @@ int64_t Choreographer::getStartTimeNanosForVsyncId(AVsyncId vsyncId) { return iter->second; } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 8a883770d8..5dd058cf9f 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -173,7 +173,13 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, *outVsyncEventData = ev.vsync.vsyncData; break; case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); + if (ev.hotplug.connectionError == 0) { + dispatchHotplug(ev.header.timestamp, ev.header.displayId, + ev.hotplug.connected); + } else { + dispatchHotplugConnectionError(ev.header.timestamp, + ev.hotplug.connectionError); + } break; case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: dispatchModeChanged(ev.header.timestamp, ev.header.displayId, diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp new file mode 100644 index 0000000000..6993bfab45 --- /dev/null +++ b/libs/gui/FrameRateUtils.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/Flags.h> +#include <gui/FrameRateUtils.h> +#include <system/window.h> +#include <utils/Log.h> + +#include <cmath> + +namespace android { +// Returns true if the frameRate is valid. +// +// @param frameRate the frame rate in Hz +// @param compatibility a ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* +// @param changeFrameRateStrategy a ANATIVEWINDOW_CHANGE_FRAME_RATE_* +// @param functionName calling function or nullptr. Used for logging +// @param privileged whether caller has unscoped surfaceflinger access +bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy, + const char* inFunctionName, bool privileged) { + const char* functionName = inFunctionName != nullptr ? inFunctionName : "call"; + int floatClassification = std::fpclassify(frameRate); + if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) { + ALOGE("%s failed - invalid frame rate %f", functionName, frameRate); + return false; + } + + if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && + compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE && + (!privileged || + (compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT && + compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) { + ALOGE("%s failed - invalid compatibility value %d privileged: %s", functionName, + compatibility, privileged ? "yes" : "no"); + return false; + } + + if (__builtin_available(android 31, *)) { + if (changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS && + changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) { + ALOGE("%s failed - invalid change frame rate strategy value %d", functionName, + changeFrameRateStrategy); + if (FLAG_BQ_SET_FRAME_RATE) { + return false; + } + } + } + + return true; +} + +} // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 918ff2dd25..d0c09e481d 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -27,11 +27,12 @@ #include <binder/Parcel.h> #include <binder/IInterface.h> -#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> -#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> #include <gui/BufferQueueDefs.h> +#include <gui/Flags.h> #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> +#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> namespace android { // ---------------------------------------------------------------------------- @@ -78,6 +79,7 @@ enum { CANCEL_BUFFERS, QUERY_MULTIPLE, GET_LAST_QUEUED_BUFFER2, + SET_FRAME_RATE, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -761,6 +763,21 @@ public: } return result; } +#if FLAG_BQ_SET_FRAME_RATE + virtual status_t setFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) override { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeFloat(frameRate); + data.writeInt32(compatibility); + data.writeInt32(changeFrameRateStrategy); + status_t result = remote()->transact(SET_FRAME_RATE, data, &reply); + if (result == NO_ERROR) { + result = reply.readInt32(); + } + return result; + } +#endif }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -956,6 +973,14 @@ status_t IGraphicBufferProducer::setAutoPrerotation(bool autoPrerotation) { return INVALID_OPERATION; } +#if FLAG_BQ_SET_FRAME_RATE +status_t IGraphicBufferProducer::setFrameRate(float /*frameRate*/, int8_t /*compatibility*/, + int8_t /*changeFrameRateStrategy*/) { + // No-op for IGBP other than BufferQueue. + return INVALID_OPERATION; +} +#endif + status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) { status_t res = OK; res = parcel->writeUint32(USE_BUFFER_QUEUE); @@ -1497,6 +1522,17 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } +#if FLAG_BQ_SET_FRAME_RATE + case SET_FRAME_RATE: { + CHECK_INTERFACE(IGraphicBuffer, data, reply); + float frameRate = data.readFloat(); + int8_t compatibility = data.readInt32(); + int8_t changeFrameRateStrategy = data.readInt32(); + status_t result = setFrameRate(frameRate, compatibility, changeFrameRateStrategy); + reply->writeInt32(result); + return NO_ERROR; + } +#endif } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 27b1d8b09b..613721e103 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -22,6 +22,7 @@ #include <android/gui/ISurfaceComposerClient.h> #include <android/native_window.h> #include <binder/Parcel.h> +#include <gui/FrameRateUtils.h> #include <gui/IGraphicBufferProducer.h> #include <gui/LayerState.h> #include <gui/SurfaceControl.h> @@ -871,34 +872,6 @@ status_t InputWindowCommands::read(const Parcel& input) { return NO_ERROR; } -bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy, - const char* inFunctionName, bool privileged) { - const char* functionName = inFunctionName != nullptr ? inFunctionName : "call"; - int floatClassification = std::fpclassify(frameRate); - if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) { - ALOGE("%s failed - invalid frame rate %f", functionName, frameRate); - return false; - } - - if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && - compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE && - (!privileged || - (compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT && - compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) { - ALOGE("%s failed - invalid compatibility value %d privileged: %s", functionName, - compatibility, privileged ? "yes" : "no"); - return false; - } - - if (changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS && - changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) { - ALOGE("%s failed - invalid change frame rate strategy value %d", functionName, - changeFrameRateStrategy); - } - - return true; -} - // ---------------------------------------------------------------------------- namespace gui { diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 53a2f64d11..a87f05357f 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -43,6 +43,7 @@ #include <gui/AidlStatusUtil.h> #include <gui/BufferItem.h> +#include <gui/Flags.h> #include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> @@ -50,8 +51,11 @@ #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> +#include <com_android_graphics_libgui_flags.h> + namespace android { +using namespace com::android::graphics::libgui; using gui::aidl_utils::statusTFromBinderStatus; using ui::Dataspace; @@ -2565,8 +2569,22 @@ void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_ mSurfaceListener->onBuffersDiscarded(discardedBufs); } -[[deprecated]] status_t Surface::setFrameRate(float /*frameRate*/, int8_t /*compatibility*/, - int8_t /*changeFrameRateStrategy*/) { +status_t Surface::setFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) { +#if FLAG_BQ_SET_FRAME_RATE + if (flags::bq_setframerate()) { + status_t err = mGraphicBufferProducer->setFrameRate(frameRate, compatibility, + changeFrameRateStrategy); + ALOGE_IF(err, "IGraphicBufferProducer::setFrameRate(%.2f) returned %s", frameRate, + strerror(-err)); + return err; + } +#else + static_cast<void>(frameRate); + static_cast<void>(compatibility); + static_cast<void>(changeFrameRateStrategy); +#endif + ALOGI("Surface::setFrameRate is deprecated, setFrameRate hint is dropped as destination is not " "SurfaceFlinger"); // ISurfaceComposer no longer supports setFrameRate, we will return NO_ERROR when the api is diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index c2543d182b..038764b800 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -26,6 +26,7 @@ #include <android/gui/IWindowInfosListener.h> #include <android/gui/TrustedPresentationThresholds.h> #include <android/os/IInputConstants.h> +#include <gui/FrameRateUtils.h> #include <gui/TraceUtils.h> #include <utils/Errors.h> #include <utils/Log.h> diff --git a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp index 6e4f074825..0d2a52b576 100644 --- a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp +++ b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp @@ -62,7 +62,10 @@ DisplayEventReceiver::Event buildDisplayEvent(FuzzedDataProvider* fdp, uint32_t } case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: { - event.hotplug = DisplayEventReceiver::Event::Hotplug{fdp->ConsumeBool() /*connected*/}; + event.hotplug = + DisplayEventReceiver::Event::Hotplug{fdp->ConsumeBool() /*connected*/, + fdp->ConsumeIntegral< + int32_t>() /*connectionError*/}; break; } case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: { diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h index 177d5f89c9..bdf8856a75 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -197,6 +197,7 @@ public: 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, diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index a49a85984f..02d7c4d2ac 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -17,9 +17,10 @@ #ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H #define ANDROID_GUI_BLAST_BUFFER_QUEUE_H -#include <gui/IGraphicBufferProducer.h> -#include <gui/BufferItemConsumer.h> #include <gui/BufferItem.h> +#include <gui/BufferItemConsumer.h> +#include <gui/Flags.h> +#include <gui/IGraphicBufferProducer.h> #include <gui/SurfaceComposerClient.h> #include <utils/Condition.h> @@ -58,6 +59,10 @@ public: protected: void onSidebandStreamChanged() override EXCLUDES(mMutex); +#if FLAG_BQ_SET_FRAME_RATE + void onSetFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) override; +#endif private: const wp<BLASTBufferQueue> mBLASTBufferQueue; diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h index 690587f0e6..2756277f2c 100644 --- a/libs/gui/include/gui/BufferQueue.h +++ b/libs/gui/include/gui/BufferQueue.h @@ -19,9 +19,10 @@ #include <gui/BufferItem.h> #include <gui/BufferQueueDefs.h> +#include <gui/Flags.h> +#include <gui/IConsumerListener.h> #include <gui/IGraphicBufferConsumer.h> #include <gui/IGraphicBufferProducer.h> -#include <gui/IConsumerListener.h> namespace android { @@ -69,6 +70,10 @@ public: void addAndGetFrameTimestamps( const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) override; +#if FLAG_BQ_SET_FRAME_RATE + void onSetFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) override; +#endif private: // mConsumerListener is a weak reference to the IConsumerListener. This is // the raison d'etre of ProxyConsumerListener. diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index 1d13dab623..38805d0221 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -18,6 +18,7 @@ #define ANDROID_GUI_BUFFERQUEUEPRODUCER_H #include <gui/BufferQueueDefs.h> +#include <gui/Flags.h> #include <gui/IGraphicBufferProducer.h> namespace android { @@ -201,6 +202,11 @@ public: // See IGraphicBufferProducer::setAutoPrerotation virtual status_t setAutoPrerotation(bool autoPrerotation); +#if FLAG_BQ_SET_FRAME_RATE + // See IGraphicBufferProducer::setFrameRate + status_t setFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) override; +#endif protected: // see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index 1df9b11432..9fef512b64 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -110,6 +110,7 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) override; 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, nsecs_t vsyncPeriod) override; void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override; @@ -137,4 +138,4 @@ private: static constexpr size_t kMaxStartTimes = 250; }; -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index 140efa6d97..fe2dd206ed 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -53,6 +53,9 @@ private: VsyncEventData vsyncEventData) = 0; virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) = 0; + + virtual void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) = 0; + virtual void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, nsecs_t vsyncPeriod) = 0; // AChoreographer-specific hook for processing null-events so that looper diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 7fd6c35c5e..79582ce685 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -88,6 +88,7 @@ public: struct Hotplug { bool connected; + int32_t connectionError __attribute__((aligned(4))); }; struct ModeChange { diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h new file mode 100644 index 0000000000..a2cff56e97 --- /dev/null +++ b/libs/gui/include/gui/Flags.h @@ -0,0 +1,22 @@ +/* + * 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 + +// TODO(281695725): replace this with build time flags, whenever they are available +#ifndef FLAG_BQ_SET_FRAME_RATE +#define FLAG_BQ_SET_FRAME_RATE false +#endif
\ No newline at end of file diff --git a/libs/gui/include/gui/FrameRateUtils.h b/libs/gui/include/gui/FrameRateUtils.h new file mode 100644 index 0000000000..16896efe1f --- /dev/null +++ b/libs/gui/include/gui/FrameRateUtils.h @@ -0,0 +1,26 @@ +/* + * 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 <stdint.h> + +namespace android { + +bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy, + const char* inFunctionName, bool privileged = false); + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h index 0ab2399eb2..e183bf2668 100644 --- a/libs/gui/include/gui/IConsumerListener.h +++ b/libs/gui/include/gui/IConsumerListener.h @@ -19,6 +19,8 @@ #include <binder/IInterface.h> #include <binder/SafeInterface.h> +#include <gui/Flags.h> + #include <utils/Errors.h> #include <utils/RefBase.h> @@ -90,6 +92,12 @@ public: // WARNING: This method can only be called when the BufferQueue is in the consumer's process. virtual void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/, FrameEventHistoryDelta* /*outDelta*/) {} + +#if FLAG_BQ_SET_FRAME_RATE + // Notifies the consumer of a setFrameRate call from the producer side. + virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/, + int8_t /*changeFrameRateStrategy*/) {} +#endif }; #ifndef NO_BINDER diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 98df83453d..3562906870 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -31,6 +31,7 @@ #include <ui/Rect.h> #include <ui/Region.h> +#include <gui/Flags.h> #include <gui/FrameTimestamps.h> #include <gui/HdrMetadata.h> @@ -676,6 +677,12 @@ public: // the width and height used for dequeueBuffer will be additionally swapped. virtual status_t setAutoPrerotation(bool autoPrerotation); +#if FLAG_BQ_SET_FRAME_RATE + // Sets the apps intended frame rate. + virtual status_t setFrameRate(float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy); +#endif + struct RequestBufferOutput : public Flattenable<RequestBufferOutput> { RequestBufferOutput() = default; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 35fcccdf78..4371007778 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -480,16 +480,6 @@ static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) return compare_type(lhs.token, rhs.token); } -// Returns true if the frameRate is valid. -// -// @param frameRate the frame rate in Hz -// @param compatibility a ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* -// @param changeFrameRateStrategy a ANATIVEWINDOW_CHANGE_FRAME_RATE_* -// @param functionName calling function or nullptr. Used for logging -// @param privileged whether caller has unscoped surfaceflinger access -bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy, - const char* functionName, bool privileged = false); - }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 462ce6e14f..38c0eed474 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -15,9 +15,13 @@ cc_test { name: "libgui_test", test_suites: ["device-tests"], - cflags: [ + defaults: ["libgui-defaults"], + + cppflags: [ "-Wall", "-Werror", + "-Wno-extra", + "-DFLAG_BQ_SET_FRAME_RATE=true", ], srcs: [ @@ -28,6 +32,7 @@ cc_test { "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", + "FrameRateUtilsTest.cpp", "DisplayInfo_test.cpp", "DisplayedContentSampling_test.cpp", "FillBuffer.cpp", @@ -53,19 +58,12 @@ cc_test { "android.hardware.configstore@1.0", "android.hardware.configstore-utils", "libSurfaceFlingerProp", - "libbase", - "liblog", - "libEGL", "libGLESv1_CM", - "libGLESv2", - "libbinder", - "libcutils", - "libgui", - "libhidlbase", "libinput", - "libui", - "libutils", - "libnativewindow", + ], + + static_libs: [ + "libgmock", ], header_libs: ["libsurfaceflinger_headers"], diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 0168877478..17aa5f1350 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -37,14 +37,18 @@ #include <system/window.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <future> #include <thread> +#include <com_android_graphics_libgui_flags.h> + using namespace std::chrono_literals; namespace android { +using namespace com::android::graphics::libgui; class BufferQueueTest : public ::testing::Test { @@ -1261,6 +1265,31 @@ TEST_F(BufferQueueTest, TestProducerConnectDisconnect) { ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); } +TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) { + if (flags::bq_setframerate()) { + ASSERT_EQ(true, FLAG_BQ_SET_FRAME_RATE); + } +} + +struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer { + BufferItemConsumerSetFrameRateListener(const sp<IGraphicBufferConsumer>& consumer) + : BufferItemConsumer(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 1) {} + + MOCK_METHOD(void, onSetFrameRate, (float, int8_t, int8_t), (override)); +}; + +TEST_F(BufferQueueTest, TestSetFrameRate) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<BufferItemConsumerSetFrameRateListener> bufferConsumer = + sp<BufferItemConsumerSetFrameRateListener>::make(consumer); + + EXPECT_CALL(*bufferConsumer, onSetFrameRate(12.34f, 1, 0)).Times(1); + producer->setFrameRate(12.34f, 1, 0); +} + class Latch { public: explicit Latch(int expected) : mExpected(expected) {} diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp index 3949d70aac..29eeaa806f 100644 --- a/libs/gui/tests/DisplayEventStructLayout_test.cpp +++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp @@ -59,6 +59,7 @@ TEST(DisplayEventStructLayoutTest, TestEventAlignment) { lastFrameTimelineOffset + 16); CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0); + CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connectionError, 4); CHECK_OFFSET(DisplayEventReceiver::Event::ModeChange, modeId, 0); CHECK_OFFSET(DisplayEventReceiver::Event::ModeChange, vsyncPeriod, 8); diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp new file mode 100644 index 0000000000..5fe22b05f9 --- /dev/null +++ b/libs/gui/tests/FrameRateUtilsTest.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <gui/FrameRateUtils.h> +#include <inttypes.h> +#include <system/window.h> + +#include <com_android_graphics_libgui_flags.h> + +namespace android { +using namespace com::android::graphics::libgui; + +TEST(FrameRateUtilsTest, ValidateFrameRate) { + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, "")); + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + + // Privileged APIs. + EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_FALSE(ValidateFrameRate(0.0f, ANATIVEWINDOW_FRAME_RATE_NO_VOTE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + + constexpr bool kPrivileged = true; + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "", + kPrivileged)); + EXPECT_TRUE(ValidateFrameRate(0.0f, ANATIVEWINDOW_FRAME_RATE_NO_VOTE, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "", + kPrivileged)); + + // Invalid frame rate. + EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_FALSE(ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_FALSE(ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, + ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + + // Invalid compatibility. + EXPECT_FALSE( + ValidateFrameRate(60.0f, -1, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + EXPECT_FALSE(ValidateFrameRate(60.0f, 2, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); + + // Invalid change frame rate strategy. + if (flags::bq_setframerate()) { + EXPECT_FALSE( + ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, -1, "")); + EXPECT_FALSE( + ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, 2, "")); + } +} + +} // namespace android diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 1f1439608b..16000139f7 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -476,8 +476,6 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { } status_t InputChannel::receiveMessage(InputMessage* msg) { - ATRACE_NAME_IF(ATRACE_ENABLED(), - StringPrintf("receiveMessage(inputChannel=%s)", mName.c_str())); ssize_t nRead; do { nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp index c8d1da7b1f..412931bc41 100644 --- a/libs/input/MotionPredictor.cpp +++ b/libs/input/MotionPredictor.cpp @@ -205,6 +205,13 @@ std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) { coords.setAxisValue(AMOTION_EVENT_AXIS_X, predictedPoint.x); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, predictedPoint.y); coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]); + // Copy forward tilt and orientation from the last event until they are predicted + // (b/291789258). + coords.setAxisValue(AMOTION_EVENT_AXIS_TILT, + event.getAxisValue(AMOTION_EVENT_AXIS_TILT, 0)); + coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + event.getRawPointerCoords(0)->getAxisValue( + AMOTION_EVENT_AXIS_ORIENTATION)); predictionTime += mModel->config().predictionInterval; if (i == 0) { diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index ee961f0ffc..a0ec6adc65 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -20,6 +20,7 @@ #include <input/InputDevice.h> #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> +#include <linux/uinput.h> #include "android-base/file.h" namespace android { @@ -97,7 +98,7 @@ TEST_F(InputDeviceKeyMapTest, keyCharacterMapWithOverlayParcelingTest) { ASSERT_EQ(*map, *mKeyMap.keyCharacterMap); } -TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { +TEST_F(InputDeviceKeyMapTest, keyCharacterMapApplyMultipleOverlaysTest) { std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm"; std::string englishOverlayPath = base::GetExecutableDirectory() + "/data/english_us.kcm"; std::string germanOverlayPath = base::GetExecutableDirectory() + "/data/german.kcm"; @@ -133,14 +134,33 @@ TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); } -TEST_F(InputDeviceKeyMapTest, keyCharacteMapBadAxisLabel) { +TEST_F(InputDeviceKeyMapTest, keyCharacterMapApplyOverlayTest) { + std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm"; + base::Result<std::shared_ptr<KeyCharacterMap>> frenchOverlay = + KeyCharacterMap::load(frenchOverlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(frenchOverlay.ok()) << "Cannot load KeyCharacterMap at " << frenchOverlayPath; + + // Apply the French overlay + mKeyMap.keyCharacterMap->combine(*frenchOverlay->get()); + + // Check if mapping for key_Q is correct + int32_t outKeyCode; + status_t mapKeyResult = mKeyMap.keyCharacterMap->mapKey(KEY_Q, /*usageCode=*/0, &outKeyCode); + ASSERT_EQ(mapKeyResult, OK) << "No mapping for KEY_Q for " << frenchOverlayPath; + ASSERT_EQ(outKeyCode, AKEYCODE_A); + + mapKeyResult = mKeyMap.keyCharacterMap->mapKey(KEY_E, /*usageCode=*/0, &outKeyCode); + ASSERT_NE(mapKeyResult, OK) << "Mapping exists for KEY_E for " << frenchOverlayPath; +} + +TEST_F(InputDeviceKeyMapTest, keyCharacterMapBadAxisLabel) { std::string klPath = base::GetExecutableDirectory() + "/data/bad_axis_label.kl"; base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath; } -TEST_F(InputDeviceKeyMapTest, keyCharacteMapBadLedLabel) { +TEST_F(InputDeviceKeyMapTest, keyCharacterMapBadLedLabel) { std::string klPath = base::GetExecutableDirectory() + "/data/bad_led_label.kl"; base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index e253ad596e..cc1d12bc5c 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -24,6 +24,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> #include <GrContextOptions.h> +#include <GrTypes.h> #include <android-base/stringprintf.h> #include <gl/GrGLInterface.h> #include <gui/TraceUtils.h> @@ -338,7 +339,8 @@ base::unique_fd SkiaGLRenderEngine::flushAndSubmit(GrDirectContext* grContext) { } else { ATRACE_BEGIN("Submit(sync=false)"); } - bool success = grContext->submit(requireSync); + bool success = grContext->submit(requireSync ? GrSyncCpu::kYes : + GrSyncCpu::kNo); ATRACE_END(); if (!success) { ALOGE("Failed to flush RenderEngine commands"); diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 6ecc6ab362..17f263d2ce 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -681,7 +681,7 @@ base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { flushInfo.fFinishedContext = destroySemaphoreInfo; } GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); - grContext->submit(false /* no cpu sync */); + grContext->submit(GrSyncCpu::kNo); int drawFenceFd = -1; if (semaphore != VK_NULL_HANDLE) { if (GrSemaphoresSubmitted::kYes == submitted) { diff --git a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h index 30149c11f0..b86ce5f450 100644 --- a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h +++ b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h @@ -136,7 +136,7 @@ private: size_t mHeight; // Position of EXIF package, default value is -1 which means no EXIF package appears. - size_t mExifPos; + ssize_t mExifPos = -1; }; } /* namespace android::ultrahdr */ diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp index 74760d9b32..3d70fcea71 100644 --- a/libs/ultrahdr/jpegr.cpp +++ b/libs/ultrahdr/jpegr.cpp @@ -85,13 +85,11 @@ static void copyJpegWithoutExif(jr_compressed_ptr pDest, jr_compressed_ptr pSource, size_t exif_pos, size_t exif_size) { - memcpy(pDest, pSource, sizeof(jpegr_compressed_struct)); - const size_t exif_offset = 4; //exif_pos has 4 bytes offset to the FF sign pDest->length = pSource->length - exif_size - exif_offset; pDest->data = new uint8_t[pDest->length]; - std::unique_ptr<uint8_t[]> dest_data; - dest_data.reset(reinterpret_cast<uint8_t*>(pDest->data)); + pDest->maxLength = pDest->length; + pDest->colorGamut = pSource->colorGamut; memcpy(pDest->data, pSource->data, exif_pos - exif_offset); memcpy((uint8_t*)pDest->data + exif_pos - exif_offset, (uint8_t*)pSource->data + exif_pos + exif_size, @@ -1262,13 +1260,13 @@ status_t JpegR::appendGainMap(jr_compressed_ptr primary_jpg_image_ptr, if (!decoder.extractEXIF(primary_jpg_image_ptr->data, primary_jpg_image_ptr->length)) { return ERROR_JPEGR_DECODE_ERROR; } - jpegr_exif_struct exif_from_jpg; - exif_from_jpg.data = nullptr; - exif_from_jpg.length = 0; - jpegr_compressed_struct new_jpg_image; - new_jpg_image.data = nullptr; - new_jpg_image.length = 0; - if (decoder.getEXIFPos() != 0) { + jpegr_exif_struct exif_from_jpg = {.data = nullptr, .length = 0}; + jpegr_compressed_struct new_jpg_image = {.data = nullptr, + .length = 0, + .maxLength = 0, + .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED}; + std::unique_ptr<uint8_t[]> dest_data; + if (decoder.getEXIFPos() >= 0) { if (pExif != nullptr) { ALOGE("received EXIF from outside while the primary image already contains EXIF"); return ERROR_JPEGR_INVALID_INPUT_TYPE; @@ -1277,6 +1275,7 @@ status_t JpegR::appendGainMap(jr_compressed_ptr primary_jpg_image_ptr, primary_jpg_image_ptr, decoder.getEXIFPos(), decoder.getEXIFSize()); + dest_data.reset(reinterpret_cast<uint8_t*>(new_jpg_image.data)); exif_from_jpg.data = decoder.getEXIFPtr(); exif_from_jpg.length = decoder.getEXIFSize(); pExif = &exif_from_jpg; |