Merge "SF: Propagate display hotplug errors" into main
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index fc0801c..3e6d2e0 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -93,6 +93,10 @@
chmod 0666 /sys/kernel/tracing/events/binder/binder_unlock/enable
chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_set_priority/enable
chmod 0666 /sys/kernel/tracing/events/binder/binder_set_priority/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_command/enable
+ chmod 0666 /sys/kernel/tracing/events/binder/binder_command/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_return/enable
+ chmod 0666 /sys/kernel/tracing/events/binder/binder_return/enable
chmod 0666 /sys/kernel/debug/tracing/events/i2c/enable
chmod 0666 /sys/kernel/tracing/events/i2c/enable
chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index a447cda..822ab7f 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -437,6 +437,9 @@
maybe_open_reference_profile(parameters_.pkgName, parameters_.apk_path,
parameters_.profile_name, profile_guided,
is_public, parameters_.uid, is_secondary_dex);
+ // `maybe_open_reference_profile` installs a hook that clears the profile on
+ // destruction. Disable it.
+ reference_profile.DisableCleanup();
struct stat sbuf;
if (reference_profile.fd() == -1 ||
(fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) {
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 35f87f9..87a14c0 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -196,7 +196,7 @@
*
* @param userContext Pointer to user-defined data passed to
* {@link AndroidBitmap_compress}.
- * @param data Compressed data of |size| bytes to write.
+ * @param data Compressed data of `size` bytes to write.
* @param size Length in bytes of data to write.
* @return Whether the operation succeeded.
*/
@@ -205,7 +205,7 @@
size_t size) __INTRODUCED_IN(30);
/**
- * Compress |pixels| as described by |info|.
+ * Compress `pixels` as described by `info`.
*
* Available since API level 30.
*
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index e0a8045..645fa8a 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -53,7 +53,7 @@
/**
* Create a shared memory region.
*
- * Create shared memory region and returns an file descriptor. The resulting file descriptor can be
+ * Create shared memory region and returns a file descriptor. The resulting file descriptor can be
* mmap'ed to process memory space with PROT_READ | PROT_WRITE | PROT_EXEC. Access to shared memory
* region can be restricted with {@link ASharedMemory_setProt}.
*
@@ -65,7 +65,7 @@
* cmsg(3) man pages for more information.
*
* If you intend to share this file descriptor with a child process after
- * calling exec(3), note that you will need to use fcntl(2) with FD_SETFD
+ * calling exec(3), note that you will need to use fcntl(2) with F_SETFD
* to clear the FD_CLOEXEC flag for this to work on all versions of Android.
*
* Available since API level 26.
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 32580ba..1f477f8 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -188,13 +188,13 @@
* Note that this only attempts to track the headroom of slow-moving sensors, such as
* the skin temperature sensor. This means that there is no benefit to calling this function
* more frequently than about once per second, and attempted to call significantly
- * more frequently may result in the function returning {@code NaN}.
+ * more frequently may result in the function returning `NaN`.
*
* In addition, in order to be able to provide an accurate forecast, the system does
* not attempt to forecast until it has multiple temperature samples from which to
* extrapolate. This should only take a few seconds from the time of the first call,
* but during this time, no forecasting will occur, and the current headroom will be
- * returned regardless of the value of {@code forecastSeconds}.
+ * returned regardless of the value of `forecastSeconds`.
*
* The value returned is a non-negative float that represents how much of the thermal envelope
* is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is
diff --git a/include/ftl/OWNERS b/include/ftl/OWNERS
new file mode 100644
index 0000000..3f61292
--- /dev/null
+++ b/include/ftl/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
\ No newline at end of file
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
index 075d12b..2c86e2e 100644
--- a/include/ftl/enum.h
+++ b/include/ftl/enum.h
@@ -25,12 +25,12 @@
#include <ftl/string.h>
-// Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> by parsing the
-// compiler-generated string literal for the signature of this function. The function is defined in
-// the global namespace with a short name and inferred return type to reduce bloat in the read-only
-// data segment.
-template <typename E, E V>
-constexpr auto ftl_enum() {
+// Returns the name of enumerator E::V and optionally the class (i.e. "E::V" or "V") as
+// std::optional<std::string_view> by parsing the compiler-generated string literal for the
+// signature of this function. The function is defined in the global namespace with a short name
+// and inferred return type to reduce bloat in the read-only data segment.
+template <bool S, typename E, E V>
+constexpr auto ftl_enum_builder() {
static_assert(std::is_enum_v<E>);
using R = std::optional<std::string_view>;
@@ -58,7 +58,9 @@
// V = android::test::Enum::kValue
//
view = view.substr(value_begin);
- const auto name_begin = view.rfind("::"sv);
+ const auto pos = S ? view.rfind("::"sv) - 2 : view.npos;
+
+ const auto name_begin = view.rfind("::"sv, pos);
if (name_begin == view.npos) return R{};
// Chop off the leading "::".
@@ -68,6 +70,18 @@
return name.find(')') == view.npos ? R{name} : R{};
}
+// Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view>
+template <typename E, E V>
+constexpr auto ftl_enum() {
+ return ftl_enum_builder<false, E, V>();
+}
+
+// Returns the name of enumerator and class E::V (i.e. "E::V") as std::optional<std::string_view>
+template <typename E, E V>
+constexpr auto ftl_enum_full() {
+ return ftl_enum_builder<true, E, V>();
+}
+
namespace android::ftl {
// Trait for determining whether a type is specifically a scoped enum or not. By definition, a
@@ -191,6 +205,11 @@
static constexpr auto value = ftl_enum<decltype(V), V>();
};
+template <auto V>
+struct EnumNameFull {
+ static constexpr auto value = ftl_enum_full<decltype(V), V>();
+};
+
template <auto I>
struct FlagName {
using E = decltype(I);
@@ -230,6 +249,18 @@
return *kName;
}
+// Returns a stringified enumerator with class at compile time.
+//
+// enum class E { A, B, C };
+// static_assert(ftl::enum_name<E::B>() == "E::B");
+//
+template <auto V>
+constexpr std::string_view enum_name_full() {
+ constexpr auto kName = ftl_enum_full<decltype(V), V>();
+ static_assert(kName, "Unknown enumerator");
+ return *kName;
+}
+
// Returns a stringified enumerator, possibly at compile time.
//
// enum class E { A, B, C, F = 5, ftl_last = F };
@@ -249,6 +280,25 @@
return kRange.values[value - kBegin];
}
+// Returns a stringified enumerator with class, possibly at compile time.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// static_assert(ftl::enum_name(E::C).value_or("?") == "E::C");
+// static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
+//
+template <typename E>
+constexpr std::optional<std::string_view> enum_name_full(E v) {
+ const auto value = to_underlying(v);
+
+ constexpr auto kBegin = to_underlying(enum_begin_v<E>);
+ constexpr auto kLast = to_underlying(enum_last_v<E>);
+ if (value < kBegin || value > kLast) return {};
+
+ constexpr auto kRange = details::EnumRange<E, details::EnumNameFull>{};
+ return kRange.values[value - kBegin];
+}
+
// Returns a stringified flag enumerator, possibly at compile time.
//
// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
@@ -282,6 +332,21 @@
return to_string(to_underlying(v));
}
+// Returns a stringified enumerator with class, or its integral value if not named.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// assert(ftl::enum_string(E::C) == "E::C");
+// assert(ftl::enum_string(E{3}) == "3");
+//
+template <typename E>
+inline std::string enum_string_full(E v) {
+ if (const auto name = enum_name_full(v)) {
+ return std::string(*name);
+ }
+ return to_string(to_underlying(v));
+}
+
// Returns a stringified flag enumerator, or its integral value if not named.
//
// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 589df9a..ef0ae4f 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -54,6 +54,11 @@
// 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 @@
}
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 @@
}
}
}
+ --sBinderProxyCount;
if (ipc) {
ipc->expungeHandle(binderHandle(), this);
@@ -688,6 +706,11 @@
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 5496d61..a5c6094 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -87,6 +87,7 @@
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 @@
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 76c7aac..4786c89 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 8ee396e..8fb755c 100644
--- a/libs/binder/ndk/tests/Android.bp
+++ b/libs/binder/ndk/tests/Android.bp
@@ -80,6 +80,28 @@
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 25b8e97..15708ca 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 @@
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 0000000..0a3021d
--- /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/IBinderRpcBenchmark.aidl b/libs/binder/tests/IBinderRpcBenchmark.aidl
index 2baf680..1008778 100644
--- a/libs/binder/tests/IBinderRpcBenchmark.aidl
+++ b/libs/binder/tests/IBinderRpcBenchmark.aidl
@@ -18,4 +18,7 @@
@utf8InCpp String repeatString(@utf8InCpp String str);
IBinder repeatBinder(IBinder binder);
byte[] repeatBytes(in byte[] bytes);
+
+ IBinder gimmeBinder();
+ void waitGimmesDestroyed();
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index e021af0..0396869 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1441,6 +1441,36 @@
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/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
index 9c96c41..4f10d74 100644
--- a/libs/binder/tests/binderRpcBenchmark.cpp
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -74,6 +74,44 @@
*out = bytes;
return Status::ok();
}
+
+ class CountedBinder : public BBinder {
+ public:
+ CountedBinder(const sp<MyBinderRpcBenchmark>& parent) : mParent(parent) {
+ std::lock_guard<std::mutex> l(mParent->mCountMutex);
+ mParent->mBinderCount++;
+ // std::cout << "Count + is now " << mParent->mBinderCount << std::endl;
+ }
+ ~CountedBinder() {
+ {
+ std::lock_guard<std::mutex> l(mParent->mCountMutex);
+ mParent->mBinderCount--;
+ // std::cout << "Count - is now " << mParent->mBinderCount << std::endl;
+
+ // skip notify
+ if (mParent->mBinderCount != 0) return;
+ }
+ mParent->mCountCv.notify_one();
+ }
+
+ private:
+ sp<MyBinderRpcBenchmark> mParent;
+ };
+
+ Status gimmeBinder(sp<IBinder>* out) override {
+ *out = sp<CountedBinder>::make(sp<MyBinderRpcBenchmark>::fromExisting(this));
+ return Status::ok();
+ }
+ Status waitGimmesDestroyed() override {
+ std::unique_lock<std::mutex> l(mCountMutex);
+ mCountCv.wait(l, [&] { return mBinderCount == 0; });
+ return Status::ok();
+ }
+
+ friend class CountedBinder;
+ std::mutex mCountMutex;
+ std::condition_variable mCountCv;
+ size_t mBinderCount;
};
enum Transport {
@@ -212,6 +250,38 @@
->ArgsProduct({kTransportList,
{64, 1024, 2048, 4096, 8182, 16364, 32728, 65535, 65536, 65537}});
+void BM_collectProxies(benchmark::State& state) {
+ sp<IBinder> binder = getBinderForOptions(state);
+ sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
+ CHECK(iface != nullptr);
+
+ const size_t kNumIters = state.range(1);
+
+ while (state.KeepRunning()) {
+ std::vector<sp<IBinder>> out;
+ out.resize(kNumIters);
+
+ for (size_t i = 0; i < kNumIters; i++) {
+ Status ret = iface->gimmeBinder(&out[i]);
+ CHECK(ret.isOk()) << ret;
+ }
+
+ out.clear();
+
+ // we are using a thread up to wait, so make a call to
+ // force all refcounts to be updated first - current
+ // binder behavior means we really don't need to wait,
+ // so code which is waiting is really there to protect
+ // against any future changes that could delay destruction
+ android::IInterface::asBinder(iface)->pingBinder();
+
+ iface->waitGimmesDestroyed();
+ }
+
+ SetLabel(state);
+}
+BENCHMARK(BM_collectProxies)->ArgsProduct({kTransportList, {10, 100, 1000, 5000, 10000, 20000}});
+
void BM_repeatBinder(benchmark::State& state) {
sp<IBinder> binder = getBinderForOptions(state);
CHECK(binder != nullptr);
diff --git a/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
index 09cb216..b80ac53 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 0000000..0ecf94c
--- /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 0000000..872193c
--- /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 0000000..67b95a5
--- /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 0000000..67910a1
--- /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 0000000..34e0eb4
--- /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 0000000..07d5da9
--- /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 0000000..2b068d1
--- /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 0000000..79fb331
--- /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 0000000..6f3b755
--- /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 0000000..6f3b755
--- /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
new file mode 100644
index 0000000..c209e78
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
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
new file mode 100644
index 0000000..b2dfe3d
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
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
new file mode 100644
index 0000000..62b611d
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
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
new file mode 100644
index 0000000..1b9a695
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
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
new file mode 100644
index 0000000..9287f50
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
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
new file mode 100644
index 0000000..9126ae3
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
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 0000000..f8c6127
--- /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 0000000..e652102
--- /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 ff95148..7fcb222 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_defaults {
+ name: "libbufferstreams_defaults",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "libanyhow",
+ "libnativewindow_rs",
+ ],
+ edition: "2021",
+}
+
rust_library {
name: "libbufferstreams",
crate_name: "bufferstreams",
- srcs: ["src/lib.rs"],
- edition: "2021",
- rlibs: [
- "libnativewindow_rs",
- ],
+ 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 1d321c8..87f3104 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 @@
///
/// 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 @@
///
/// 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 @@
///
/// 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 @@
/// 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 0000000..2fd518e
--- /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 0000000..1593b18
--- /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 0000000..d0c621b
--- /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 0000000..dd038c6
--- /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 0000000..46c58dc
--- /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 0000000..b7e9705
--- /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 0000000..e046dbb
--- /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 0000000..90275c7
--- /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 f835997..9df5632 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 0000000..3f61292
--- /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 5592a01..b68c2c3 100644
--- a/libs/ftl/enum_test.cpp
+++ b/libs/ftl/enum_test.cpp
@@ -33,6 +33,11 @@
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::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_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 @@
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 @@
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 2ea4d16..9a27d23 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -204,27 +204,8 @@
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 @@
"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 @@
"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 207fa4f..dd0a028 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 @@
}
}
+#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 @@
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 66cad03..ab0f6d2 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 @@
}
}
+#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 920b83d..67dff6d 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 @@
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/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp
new file mode 100644
index 0000000..6993bfa
--- /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 918ff2d..d0c09e4 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 @@
CANCEL_BUFFERS,
QUERY_MULTIPLE,
GET_LAST_QUEUED_BUFFER2,
+ SET_FRAME_RATE,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -761,6 +763,21 @@
}
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 @@
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 @@
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 e1afb52..613721e 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>
@@ -84,6 +85,7 @@
changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
defaultFrameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
frameRateCategory(ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT),
+ frameRateSelectionStrategy(ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF),
fixedTransformHint(ui::Transform::ROT_INVALID),
autoRefresh(false),
isTrustedOverlay(false),
@@ -160,6 +162,7 @@
SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
SAFE_PARCEL(output.writeByte, defaultFrameRateCompatibility);
SAFE_PARCEL(output.writeByte, frameRateCategory);
+ SAFE_PARCEL(output.writeByte, frameRateSelectionStrategy);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeBool, autoRefresh);
SAFE_PARCEL(output.writeBool, dimmingEnabled);
@@ -293,6 +296,7 @@
SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
SAFE_PARCEL(input.readByte, &defaultFrameRateCompatibility);
SAFE_PARCEL(input.readByte, &frameRateCategory);
+ SAFE_PARCEL(input.readByte, &frameRateSelectionStrategy);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readBool, &autoRefresh);
@@ -666,6 +670,10 @@
what |= eFrameRateCategoryChanged;
frameRateCategory = other.frameRateCategory;
}
+ if (other.what & eFrameRateSelectionStrategyChanged) {
+ what |= eFrameRateSelectionStrategyChanged;
+ frameRateSelectionStrategy = other.frameRateSelectionStrategy;
+ }
if (other.what & eFixedTransformHintChanged) {
what |= eFixedTransformHintChanged;
fixedTransformHint = other.fixedTransformHint;
@@ -777,6 +785,7 @@
CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
changeFrameRateStrategy);
CHECK_DIFF(diff, eFrameRateCategoryChanged, other, frameRateCategory);
+ CHECK_DIFF(diff, eFrameRateSelectionStrategyChanged, other, frameRateSelectionStrategy);
CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint);
CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay);
@@ -863,34 +872,6 @@
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 53a2f64..a87f053 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 @@
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 4db960e..038764b 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>
@@ -2104,6 +2105,19 @@
return *this;
}
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setFrameRateSelectionStrategy(const sp<SurfaceControl>& sc,
+ int8_t strategy) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eFrameRateSelectionStrategyChanged;
+ s->frameRateSelectionStrategy = strategy;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixedTransformHint(
const sp<SurfaceControl>& sc, int32_t fixedTransformHint) {
layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index a49a859..02d7c4d 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 @@
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 690587f..2756277 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 @@
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 1d13dab..38805d0 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 @@
// 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/services/inputflinger/host/main.cpp b/libs/gui/include/gui/Flags.h
similarity index 64%
rename from services/inputflinger/host/main.cpp
rename to libs/gui/include/gui/Flags.h
index 0a517cc..a2cff56 100644
--- a/services/inputflinger/host/main.cpp
+++ b/libs/gui/include/gui/Flags.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-#include <binder/BinderService.h>
-#include "InputFlinger.h"
+#pragma once
-using namespace android;
-
-int main(int, char**) {
- ProcessState::self()->setThreadPoolMaxThreadCount(4);
- BinderService<InputFlinger>::publishAndJoinThreadPool(true);
- return 0;
-}
+// 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/services/inputflinger/host/main.cpp b/libs/gui/include/gui/FrameRateUtils.h
similarity index 64%
copy from services/inputflinger/host/main.cpp
copy to libs/gui/include/gui/FrameRateUtils.h
index 0a517cc..16896ef 100644
--- a/services/inputflinger/host/main.cpp
+++ b/libs/gui/include/gui/FrameRateUtils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#include <binder/BinderService.h>
-#include "InputFlinger.h"
+#pragma once
-using namespace android;
+#include <stdint.h>
-int main(int, char**) {
- ProcessState::self()->setThreadPoolMaxThreadCount(4);
- BinderService<InputFlinger>::publishAndJoinThreadPool(true);
- return 0;
-}
+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 0ab2399..e183bf2 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 @@
// 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 98df834..3562906 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 @@
// 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 8c36058..4371007 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -197,7 +197,7 @@
eInputInfoChanged = 0x40000000,
eCornerRadiusChanged = 0x80000000,
eDestinationFrameChanged = 0x1'00000000,
- /* unused = 0x2'00000000, */
+ eFrameRateSelectionStrategyChanged = 0x2'00000000,
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
eColorSpaceAgnosticChanged = 0x10'00000000,
@@ -268,6 +268,7 @@
layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged |
layer_state_t::eFrameRateChanged | layer_state_t::eFrameRateCategoryChanged |
+ layer_state_t::eFrameRateSelectionStrategyChanged |
layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFixedTransformHintChanged;
// Changes affecting data sent to input.
@@ -361,6 +362,9 @@
// Frame rate category to suggest what frame rate range a surface should run.
int8_t frameRateCategory;
+ // Strategy of the layer for frame rate selection.
+ int8_t frameRateSelectionStrategy;
+
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
// the graphic producers should receive a transform hint as if the
@@ -476,16 +480,6 @@
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/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6fef5d2..42e3c16 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -687,6 +687,8 @@
Transaction& setFrameRateCategory(const sp<SurfaceControl>& sc, int8_t category);
+ Transaction& setFrameRateSelectionStrategy(const sp<SurfaceControl>& sc, int8_t strategy);
+
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
// the graphic producers should receive a transform hint as if the
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 462ce6e..38c0eed 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -15,9 +15,13 @@
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 @@
"CompositorTiming_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
+ "FrameRateUtilsTest.cpp",
"DisplayInfo_test.cpp",
"DisplayedContentSampling_test.cpp",
"FillBuffer.cpp",
@@ -53,19 +58,12 @@
"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 0168877..17aa5f1 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 @@
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/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp
new file mode 100644
index 0000000..5fe22b0
--- /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 1f14396..1600013 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -476,8 +476,6 @@
}
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 c8d1da7..412931b 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -205,6 +205,13 @@
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 ee961f0..a0ec6ad 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 @@
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 @@
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/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index e158f01..b068f48 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1096,6 +1096,26 @@
ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 4
};
+/*
+ * Frame rate selection strategy values that can be used in
+ * Transaction::setFrameRateSelectionStrategy.
+ */
+enum {
+ /**
+ * Default value. The layer uses its own frame rate specifications, assuming it has any
+ * specifications, instead of its parent's.
+ */
+ ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF = 0,
+
+ /**
+ * The layer's frame rate specifications will propagate to and override those of its descendant
+ * layers.
+ * The layer with this strategy has the ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF
+ * behavior for itself.
+ */
+ ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN = 1,
+};
+
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy) {
return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index e253ad5..cc1d12b 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 @@
} 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/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 82ee95c..7ec98ea 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -400,7 +400,10 @@
// simply match the existing behavior for protected buffers.) We also never cache any
// buffers while in a protected context.
const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
- if (isProtectedBuffer || isProtected()) {
+ // Don't attempt to map buffers if we're not gpu sampleable. Callers shouldn't send a buffer
+ // over to RenderEngine.
+ const bool isGpuSampleable = buffer->getUsage() & GRALLOC_USAGE_HW_TEXTURE;
+ if (isProtectedBuffer || isProtected() || !isGpuSampleable) {
return;
}
ATRACE_CALL();
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 6ecc6ab..17f263d 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -681,7 +681,7 @@
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 30149c1..b86ce5f4 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
@@ -136,7 +136,7 @@
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 74760d9..3d70fce 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -85,13 +85,11 @@
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 @@
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 @@
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;
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 052efb6b..e5b1e44 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,7 +21,13 @@
cc_defaults {
name: "libgpuservice_defaults",
- defaults: ["gpuservice_defaults"],
+ defaults: [
+ "gpuservice_defaults",
+ "libvkjson_deps",
+ "libgfxstats_deps",
+ "libgpumem_deps",
+ "libgpumemtracer_deps",
+ ],
cflags: [
"-DLOG_TAG=\"GpuService\"",
],
@@ -29,17 +35,17 @@
"libbase",
"libbinder",
"libcutils",
- "libgfxstats",
- "libgpumem",
"libgpuwork",
- "libgpumemtracer",
"libgraphicsenv",
"liblog",
"libutils",
- "libvkjson",
],
static_libs: [
+ "libgfxstats",
+ "libgpumem",
+ "libgpumemtracer",
"libserviceutils",
+ "libvkjson",
],
export_static_lib_headers: [
"libserviceutils",
@@ -68,7 +74,7 @@
],
}
-cc_library_shared {
+cc_library_static {
name: "libgpuservice",
defaults: ["libgpuservice_production_defaults"],
export_include_dirs: ["include"],
@@ -96,14 +102,17 @@
cc_binary {
name: "gpuservice",
- defaults: ["libgpuservice_binary"],
+ defaults: [
+ "libgpuservice_binary",
+ "libgpuservice_production_defaults",
+ ],
init_rc: ["gpuservice.rc"],
required: [
"bpfloader",
"gpuMem.o",
],
srcs: [":gpuservice_binary_sources"],
- shared_libs: [
+ static_libs: [
"libgpuservice",
],
}
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
index d0ea856..66a3059 100644
--- a/services/gpuservice/gpumem/Android.bp
+++ b/services/gpuservice/gpumem/Android.bp
@@ -21,12 +21,8 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
- name: "libgpumem",
- srcs: [
- "GpuMem.cpp",
- ],
- header_libs: ["bpf_headers"],
+cc_defaults {
+ name: "libgpumem_deps",
shared_libs: [
"libbase",
"libbpf_bcc",
@@ -34,6 +30,17 @@
"liblog",
"libutils",
],
+}
+
+cc_library_static {
+ name: "libgpumem",
+ defaults: [
+ "libgpumem_deps",
+ ],
+ srcs: [
+ "GpuMem.cpp",
+ ],
+ header_libs: ["bpf_headers"],
export_include_dirs: ["include"],
export_header_lib_headers: ["bpf_headers"],
export_shared_lib_headers: ["libbase"],
diff --git a/services/gpuservice/gpustats/Android.bp b/services/gpuservice/gpustats/Android.bp
index 54291ad..0e64716 100644
--- a/services/gpuservice/gpustats/Android.bp
+++ b/services/gpuservice/gpustats/Android.bp
@@ -7,11 +7,8 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
- name: "libgfxstats",
- srcs: [
- "GpuStats.cpp",
- ],
+cc_defaults {
+ name: "libgfxstats_deps",
shared_libs: [
"libcutils",
"libgraphicsenv",
@@ -22,6 +19,16 @@
"libstatssocket",
"libutils",
],
+}
+
+cc_library_static {
+ name: "libgfxstats",
+ defaults: [
+ "libgfxstats_deps",
+ ],
+ srcs: [
+ "GpuStats.cpp",
+ ],
export_include_dirs: ["include"],
export_shared_lib_headers: [
"libstatspull",
diff --git a/services/gpuservice/tests/fuzzers/Android.bp b/services/gpuservice/tests/fuzzers/Android.bp
index 6bcc5e8..d4d48c4 100644
--- a/services/gpuservice/tests/fuzzers/Android.bp
+++ b/services/gpuservice/tests/fuzzers/Android.bp
@@ -5,10 +5,12 @@
cc_fuzz {
name: "gpu_service_fuzzer",
defaults: [
+ "libgpuservice_defaults",
"service_fuzzer_defaults",
"fuzzer_disable_leaks",
],
static_libs: [
+ "libgpuservice",
"liblog",
],
fuzz_config: {
@@ -20,7 +22,4 @@
},
include_dirs: ["frameworks/native/services/gpuservice/"],
srcs: ["GpuServiceFuzzer.cpp"],
- shared_libs: [
- "libgpuservice",
- ],
}
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index c870b17..8056a2c 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -24,6 +24,9 @@
cc_test {
name: "gpuservice_unittest",
test_suites: ["device-tests"],
+ defaults: [
+ "libgpuservice_defaults",
+ ],
srcs: [
"GpuMemTest.cpp",
"GpuMemTracerTest.cpp",
@@ -36,9 +39,6 @@
"libbinder",
"libbpf_bcc",
"libcutils",
- "libgfxstats",
- "libgpumem",
- "libgpumemtracer",
"libgraphicsenv",
"liblog",
"libprotobuf-cpp-lite",
@@ -46,10 +46,10 @@
"libstatslog",
"libstatspull",
"libutils",
- "libgpuservice",
],
static_libs: [
"libgmock",
+ "libgpuservice",
"libperfetto_client_experimental",
"perfetto_trace_protos",
],
diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp
index a1bc1ed..d636b7d 100644
--- a/services/gpuservice/tracing/Android.bp
+++ b/services/gpuservice/tracing/Android.bp
@@ -21,20 +21,28 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
- name: "libgpumemtracer",
- srcs: [
- "GpuMemTracer.cpp",
- ],
+cc_defaults {
+ name: "libgpumemtracer_deps",
shared_libs: [
- "libgpumem",
"libbase",
"liblog",
"libutils",
],
static_libs: [
+ "libgpumem",
"libperfetto_client_experimental",
],
+}
+
+cc_library_static {
+ name: "libgpumemtracer",
+ defaults: [
+ "libgpumemtracer_deps",
+ "libgpumem_deps",
+ ],
+ srcs: [
+ "GpuMemTracer.cpp",
+ ],
export_include_dirs: ["include"],
export_static_lib_headers: [
"libperfetto_client_experimental",
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 7451037..5d1d4af 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -240,9 +240,6 @@
"libinputservice_test",
"Bug-115739809",
"StructLayout_test",
- // currently unused, but still must build correctly
- "inputflinger",
- "libinputflingerhost",
// rust targets
"libinput_rust_test",
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index cb369a8..fa8f548 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -269,6 +269,11 @@
return msg;
}
+std::ostream& operator<<(std::ostream& out, const MotionEntry& motionEntry) {
+ out << motionEntry.getDescription();
+ return out;
+}
+
// --- SensorEntry ---
SensorEntry::SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 8dc2a2a..dd4aab8 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -24,6 +24,7 @@
#include <stdint.h>
#include <utils/Timers.h>
#include <functional>
+#include <ostream>
#include <string>
namespace android::inputdispatcher {
@@ -189,6 +190,8 @@
~MotionEntry() override;
};
+std::ostream& operator<<(std::ostream& out, const MotionEntry& motionEntry);
+
struct SensorEntry : EventEntry {
int32_t deviceId;
uint32_t source;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d98641e..b3be89e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -668,7 +668,15 @@
} else {
// This pointer was already sent to the window. Use ACTION_HOVER_MOVE.
if (CC_UNLIKELY(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE)) {
- LOG(FATAL) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
+ android::base::LogSeverity severity = android::base::LogSeverity::FATAL;
+ if (entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) {
+ // The Accessibility injected touch exploration event stream
+ // has known inconsistencies, so log ERROR instead of
+ // crashing the device with FATAL.
+ // TODO(b/299977100): Move a11y severity back to FATAL.
+ severity = android::base::LogSeverity::ERROR;
+ }
+ LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
}
touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
}
@@ -706,6 +714,40 @@
});
}
+/**
+ * In general, touch should be always split between windows. Some exceptions:
+ * 1. Don't split touch if all of the below is true:
+ * (a) we have an active pointer down *and*
+ * (b) a new pointer is going down that's from the same device *and*
+ * (c) the window that's receiving the current pointer does not support split touch.
+ * 2. Don't split mouse events
+ */
+bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) {
+ if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
+ // We should never split mouse events
+ return false;
+ }
+ for (const TouchedWindow& touchedWindow : touchState.windows) {
+ if (touchedWindow.windowHandle->getInfo()->isSpy()) {
+ // Spy windows should not affect whether or not touch is split.
+ continue;
+ }
+ if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
+ continue;
+ }
+ if (touchedWindow.windowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
+ // Wallpaper window should not affect whether or not touch is split
+ continue;
+ }
+
+ if (touchedWindow.hasTouchingPointers(entry.deviceId)) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -2210,40 +2252,6 @@
return responsiveMonitors;
}
-/**
- * In general, touch should be always split between windows. Some exceptions:
- * 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's
- * from the same device, *and* the window that's receiving the current pointer does not support
- * split touch.
- * 2. Don't split mouse events
- */
-bool InputDispatcher::shouldSplitTouch(const TouchState& touchState,
- const MotionEntry& entry) const {
- if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
- // We should never split mouse events
- return false;
- }
- for (const TouchedWindow& touchedWindow : touchState.windows) {
- if (touchedWindow.windowHandle->getInfo()->isSpy()) {
- // Spy windows should not affect whether or not touch is split.
- continue;
- }
- if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
- continue;
- }
- if (touchedWindow.windowHandle->getInfo()->inputConfig.test(
- gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
- // Wallpaper window should not affect whether or not touch is split
- continue;
- }
-
- if (touchedWindow.hasTouchingPointers(entry.deviceId)) {
- return false;
- }
- }
- return true;
-}
-
std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
InputEventInjectionResult& outInjectionResult) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 512bc4f..4d145c6 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -522,7 +522,6 @@
// shade is pulled down while we are counting down the timeout).
void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
- bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) const;
int32_t getTargetDisplayId(const EventEntry& entry);
sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 4221e42..9dcf615 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -288,4 +288,9 @@
return out;
}
+std::ostream& operator<<(std::ostream& out, const TouchState& state) {
+ out << state.dump();
+ return out;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 39e63e5..f016936 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -17,6 +17,7 @@
#pragma once
#include <bitset>
+#include <ostream>
#include <set>
#include "TouchedWindow.h"
@@ -79,5 +80,7 @@
std::string dump() const;
};
+std::ostream& operator<<(std::ostream& out, const TouchState& state);
+
} // namespace inputdispatcher
} // namespace android
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 9807a6d..ff4b425 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -256,5 +256,10 @@
return out;
}
+std::ostream& operator<<(std::ostream& out, const TouchedWindow& window) {
+ out << window.dump();
+ return out;
+}
+
} // namespace inputdispatcher
} // namespace android
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 0a38f9f..3f760c0 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -20,6 +20,7 @@
#include <input/Input.h>
#include <utils/BitSet.h>
#include <bitset>
+#include <ostream>
#include <set>
#include "InputTarget.h"
@@ -92,5 +93,7 @@
static std::string deviceStateToString(const TouchedWindow::DeviceState& state);
};
+std::ostream& operator<<(std::ostream& out, const TouchedWindow& window);
+
} // namespace inputdispatcher
} // namespace android
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
deleted file mode 100644
index 4d2839f..0000000
--- a/services/inputflinger/host/Android.bp
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_library_shared {
- name: "libinputflingerhost",
- cpp_std: "c++20",
- srcs: [
- "InputFlinger.cpp",
- "InputDriver.cpp",
- "InputHost.cpp",
- ],
-
- header_libs: ["jni_headers"],
- shared_libs: [
- "libbase",
- "libbinder",
- "libcrypto",
- "libcutils",
- "libinput",
- "liblog",
- "libutils",
- "libhardware",
- ],
- static_libs: [
- "libarect",
- ],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- // TODO: Move inputflinger to its own process and mark it hidden
- //-fvisibility=hidden
- ],
-
- export_header_lib_headers: ["jni_headers"],
- export_include_dirs: ["."],
-}
-
-//#######################################################################
-// build input flinger executable
-cc_binary {
- name: "inputflinger",
-
- srcs: ["main.cpp"],
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- shared_libs: [
- "libbase",
- "libbinder",
- "libinputflingerhost",
- "libutils",
- "libinput",
- ],
- static_libs: [
- "libarect",
- "libui-types",
- ],
-}
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
deleted file mode 100644
index de99fc7..0000000
--- a/services/inputflinger/host/InputDriver.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <functional>
-#include <stdint.h>
-#include <sys/types.h>
-#include <unordered_map>
-#include <vector>
-
-#define LOG_TAG "InputDriver"
-
-#define LOG_NDEBUG 0
-
-#include "InputDriver.h"
-#include "InputHost.h"
-
-#include <hardware/input.h>
-#include <input/InputDevice.h>
-#include <input/PropertyMap.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#define INDENT2 " "
-
-struct input_property_map {
- std::unique_ptr<android::PropertyMap> propertyMap;
-};
-
-struct input_property {
- android::String8 key;
- android::String8 value;
-};
-
-struct input_device_identifier {
- const char* name;
- const char* uniqueId;
- input_bus_t bus;
- int32_t vendorId;
- int32_t productId;
- int32_t version;
-};
-
-struct input_device_definition {
- std::vector<input_report_definition*> reportDefs;
-};
-
-struct input_device_handle {
- input_device_identifier_t* id;
- input_device_definition_t* def;
-};
-
-struct input_int_usage {
- input_usage_t usage;
- int32_t min;
- int32_t max;
- float resolution;
-};
-
-struct input_collection {
- int32_t arity;
- std::vector<input_int_usage> intUsages;
- std::vector<input_usage_t> boolUsages;
-};
-
-struct InputCollectionIdHasher {
- std::size_t operator()(const input_collection_id& id) const {
- return std::hash<int>()(static_cast<int>(id));
- }
-};
-
-struct input_report_definition {
- std::unordered_map<input_collection_id_t, input_collection, InputCollectionIdHasher> collections;
-};
-
-
-namespace android {
-
-static input_host_callbacks_t kCallbacks = {
- .create_device_identifier = create_device_identifier,
- .create_device_definition = create_device_definition,
- .create_input_report_definition = create_input_report_definition,
- .create_output_report_definition = create_output_report_definition,
- .free_report_definition = free_report_definition,
- .input_device_definition_add_report = input_device_definition_add_report,
- .input_report_definition_add_collection = input_report_definition_add_collection,
- .input_report_definition_declare_usage_int = input_report_definition_declare_usage_int,
- .input_report_definition_declare_usages_bool = input_report_definition_declare_usages_bool,
- .register_device = register_device,
- .input_allocate_report = input_allocate_report,
- .input_report_set_usage_int = input_report_set_usage_int,
- .input_report_set_usage_bool = input_report_set_usage_bool,
- .report_event = report_event,
- .input_get_device_property_map = input_get_device_property_map,
- .input_get_device_property = input_get_device_property,
- .input_get_property_key = input_get_property_key,
- .input_get_property_value = input_get_property_value,
- .input_free_device_property = input_free_device_property,
- .input_free_device_property_map = input_free_device_property_map,
-};
-
-InputDriver::InputDriver(const char* name) : mName(String8(name)) {
- const hw_module_t* module;
- int err = input_open(&module, name);
- LOG_ALWAYS_FATAL_IF(err != 0, "Input module %s not found", name);
- mHal = reinterpret_cast<const input_module_t*>(module);
-}
-
-void InputDriver::init() {
- mHal->init(mHal, static_cast<input_host_t*>(this), kCallbacks);
-}
-
-input_device_identifier_t* InputDriver::createDeviceIdentifier(
- const char* name, int32_t productId, int32_t vendorId,
- input_bus_t bus, const char* uniqueId) {
- auto identifier = new ::input_device_identifier {
- .name = name,
- .uniqueId = uniqueId,
- .bus = bus,
- .vendorId = vendorId,
- .productId = productId,
- };
- // TODO: store this identifier somewhere
- return identifier;
-}
-
-input_device_definition_t* InputDriver::createDeviceDefinition() {
- return new ::input_device_definition;
-}
-
-input_report_definition_t* InputDriver::createInputReportDefinition() {
- return new ::input_report_definition;
-}
-
-input_report_definition_t* InputDriver::createOutputReportDefinition() {
- return new ::input_report_definition;
-}
-
-void InputDriver::freeReportDefinition(input_report_definition_t* reportDef) {
- delete reportDef;
-}
-
-void InputDriver::inputDeviceDefinitionAddReport(input_device_definition_t* d,
- input_report_definition_t* r) {
- d->reportDefs.push_back(r);
-}
-
-void InputDriver::inputReportDefinitionAddCollection(input_report_definition_t* report,
- input_collection_id_t id, int32_t arity) {
- report->collections[id] = {.arity = arity};
-}
-
-void InputDriver::inputReportDefinitionDeclareUsageInt(input_report_definition_t* report,
- input_collection_id_t id, input_usage_t usage, int32_t min, int32_t max,
- float resolution) {
- if (report->collections.find(id) != report->collections.end()) {
- report->collections[id].intUsages.push_back({
- .usage = usage, .min = min, .max = max, .resolution = resolution});
- }
-}
-
-void InputDriver::inputReportDefinitionDeclareUsagesBool(input_report_definition_t* report,
- input_collection_id_t id, input_usage_t* usage, size_t usageCount) {
- if (report->collections.find(id) != report->collections.end()) {
- for (size_t i = 0; i < usageCount; ++i) {
- report->collections[id].boolUsages.push_back(usage[i]);
- }
- }
-}
-
-input_device_handle_t* InputDriver::registerDevice(input_device_identifier_t* id,
- input_device_definition_t* d) {
- ALOGD("Registering device %s with %zu input reports", id->name, d->reportDefs.size());
- // TODO: save this device handle
- return new input_device_handle{ .id = id, .def = d };
-}
-
-void InputDriver::unregisterDevice(input_device_handle_t* handle) {
- delete handle;
-}
-
-input_report_t* InputDriver::inputAllocateReport(input_report_definition_t* r) {
- ALOGD("Allocating input report for definition %p", r);
- return nullptr;
-}
-
-void InputDriver::inputReportSetUsageInt(input_report_t* r, input_collection_id_t id,
- input_usage_t usage, int32_t value, int32_t arity_index) {
-}
-
-void InputDriver::inputReportSetUsageBool(input_report_t* r, input_collection_id_t id,
- input_usage_t usage, bool value, int32_t arity_index) {
-}
-
-void InputDriver::reportEvent(input_device_handle_t* d, input_report_t* report) {
- ALOGD("report_event %p for handle %p", report, d);
-}
-
-input_property_map_t* InputDriver::inputGetDevicePropertyMap(input_device_identifier_t* id) {
- InputDeviceIdentifier idi;
- idi.name = id->name;
- idi.uniqueId = id->uniqueId;
- idi.bus = id->bus;
- idi.vendor = id->vendorId;
- idi.product = id->productId;
- idi.version = id->version;
-
- std::string configFile =
- getInputDeviceConfigurationFilePathByDeviceIdentifier(idi,
- InputDeviceConfigurationFileType::
- CONFIGURATION);
- if (configFile.empty()) {
- ALOGD("No input device configuration file found for device '%s'.",
- idi.name.c_str());
- } else {
- std::unique_ptr<input_property_map_t> propMap = std::make_unique<input_property_map_t>();
- android::base::Result<std::unique_ptr<PropertyMap>> result =
- PropertyMap::load(configFile.c_str());
- if (!result.ok()) {
- ALOGE("Error loading input device configuration file for device '%s'. "
- "Using default configuration.",
- idi.name.c_str());
- return nullptr;
- }
- propMap->propertyMap = std::move(*result);
- return propMap.release();
- }
- return nullptr;
-}
-
-input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* key) {
- if (map != nullptr) {
- std::optional<std::string> value = map->propertyMap->getString(key);
- if (!value.has_value()) {
- return nullptr;
- }
- auto prop = std::make_unique<input_property_t>();
- prop->key = key;
- prop->value = value->c_str();
- return prop.release();
- }
- return nullptr;
-}
-
-const char* InputDriver::inputGetPropertyKey(input_property_t* property) {
- if (property != nullptr) {
- return property->key.c_str();
- }
- return nullptr;
-}
-
-const char* InputDriver::inputGetPropertyValue(input_property_t* property) {
- if (property != nullptr) {
- return property->value.c_str();
- }
- return nullptr;
-}
-
-void InputDriver::inputFreeDeviceProperty(input_property_t* property) {
- if (property != nullptr) {
- delete property;
- }
-}
-
-void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) {
- if (map != nullptr) {
- delete map;
- }
-}
-
-void InputDriver::dump(String8& result) {
- result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.c_str());
-}
-
-} // namespace android
-
-// HAL wrapper functions
-
-namespace android {
-
-::input_device_identifier_t* create_device_identifier(input_host_t* host,
- const char* name, int32_t product_id, int32_t vendor_id,
- input_bus_t bus, const char* unique_id) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->createDeviceIdentifier(name, product_id, vendor_id, bus, unique_id);
-}
-
-input_device_definition_t* create_device_definition(input_host_t* host) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->createDeviceDefinition();
-}
-
-input_report_definition_t* create_input_report_definition(input_host_t* host) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->createInputReportDefinition();
-}
-
-input_report_definition_t* create_output_report_definition(input_host_t* host) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->createOutputReportDefinition();
-}
-
-void free_report_definition(input_host_t* host, input_report_definition_t* report_def) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->freeReportDefinition(report_def);
-}
-
-void input_device_definition_add_report(input_host_t* host,
- input_device_definition_t* d, input_report_definition_t* r) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->inputDeviceDefinitionAddReport(d, r);
-}
-
-void input_report_definition_add_collection(input_host_t* host,
- input_report_definition_t* report, input_collection_id_t id, int32_t arity) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->inputReportDefinitionAddCollection(report, id, arity);
-}
-
-void input_report_definition_declare_usage_int(input_host_t* host,
- input_report_definition_t* report, input_collection_id_t id,
- input_usage_t usage, int32_t min, int32_t max, float resolution) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->inputReportDefinitionDeclareUsageInt(report, id, usage, min, max, resolution);
-}
-
-void input_report_definition_declare_usages_bool(input_host_t* host,
- input_report_definition_t* report, input_collection_id_t id,
- input_usage_t* usage, size_t usage_count) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->inputReportDefinitionDeclareUsagesBool(report, id, usage, usage_count);
-}
-
-input_device_handle_t* register_device(input_host_t* host,
- input_device_identifier_t* id, input_device_definition_t* d) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->registerDevice(id, d);
-}
-
-void unregister_device(input_host_t* host, input_device_handle_t* handle) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->unregisterDevice(handle);
-}
-
-input_report_t* input_allocate_report(input_host_t* host, input_report_definition_t* r) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->inputAllocateReport(r);
-}
-
-void input_report_set_usage_int(input_host_t* host, input_report_t* r,
- input_collection_id_t id, input_usage_t usage, int32_t value, int32_t arity_index) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->inputReportSetUsageInt(r, id, usage, value, arity_index);
-}
-
-void input_report_set_usage_bool(input_host_t* host, input_report_t* r,
- input_collection_id_t id, input_usage_t usage, bool value, int32_t arity_index) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->inputReportSetUsageBool(r, id, usage, value, arity_index);
-}
-
-void report_event(input_host_t* host, input_device_handle_t* d, input_report_t* report) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->reportEvent(d, report);
-}
-
-input_property_map_t* input_get_device_property_map(input_host_t* host,
- input_device_identifier_t* id) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->inputGetDevicePropertyMap(id);
-}
-
-input_property_t* input_get_device_property(input_host_t* host, input_property_map_t* map,
- const char* key) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->inputGetDeviceProperty(map, key);
-}
-
-const char* input_get_property_key(input_host_t* host, input_property_t* property) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->inputGetPropertyKey(property);
-}
-
-const char* input_get_property_value(input_host_t* host, input_property_t* property) {
- auto driver = static_cast<InputDriverInterface*>(host);
- return driver->inputGetPropertyValue(property);
-}
-
-void input_free_device_property(input_host_t* host, input_property_t* property) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->inputFreeDeviceProperty(property);
-}
-
-void input_free_device_property_map(input_host_t* host, input_property_map_t* map) {
- auto driver = static_cast<InputDriverInterface*>(host);
- driver->inputFreeDevicePropertyMap(map);
-}
-
-} // namespace android
diff --git a/services/inputflinger/host/InputDriver.h b/services/inputflinger/host/InputDriver.h
deleted file mode 100644
index b4c9094..0000000
--- a/services/inputflinger/host/InputDriver.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "InputHost.h"
-
-#include <hardware/input.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-
-// Declare a concrete type for the HAL
-struct input_host {
-};
-
-namespace android {
-
-class InputDriverInterface : public input_host_t, public virtual RefBase {
-protected:
- InputDriverInterface() = default;
- virtual ~InputDriverInterface() = default;
-
-public:
- virtual void init() = 0;
-
- virtual input_device_identifier_t* createDeviceIdentifier(
- const char* name, int32_t productId, int32_t vendorId,
- input_bus_t bus, const char* uniqueId) = 0;
- virtual input_device_definition_t* createDeviceDefinition() = 0;
- virtual input_report_definition_t* createInputReportDefinition() = 0;
- virtual input_report_definition_t* createOutputReportDefinition() = 0;
- virtual void freeReportDefinition(input_report_definition_t* reportDef) = 0;
-
- virtual void inputDeviceDefinitionAddReport(input_device_definition_t* d,
- input_report_definition_t* r) = 0;
- virtual void inputReportDefinitionAddCollection(input_report_definition_t* report,
- input_collection_id_t id, int32_t arity) = 0;
- virtual void inputReportDefinitionDeclareUsageInt(input_report_definition_t* report,
- input_collection_id_t id, input_usage_t usage, int32_t min, int32_t max,
- float resolution) = 0;
- virtual void inputReportDefinitionDeclareUsagesBool(input_report_definition_t* report,
- input_collection_id_t id, input_usage_t* usage, size_t usageCount) = 0;
-
- virtual input_device_handle_t* registerDevice(input_device_identifier_t* id,
- input_device_definition_t* d) = 0;
- virtual void unregisterDevice(input_device_handle_t* handle) = 0;
-
- virtual input_report_t* inputAllocateReport(input_report_definition_t* r) = 0;
- virtual void inputReportSetUsageInt(input_report_t* r, input_collection_id_t id,
- input_usage_t usage, int32_t value, int32_t arity_index) = 0;
- virtual void inputReportSetUsageBool(input_report_t* r, input_collection_id_t id,
- input_usage_t usage, bool value, int32_t arity_index) = 0;
- virtual void reportEvent(input_device_handle_t* d, input_report_t* report) = 0;
-
- virtual input_property_map_t* inputGetDevicePropertyMap(input_device_identifier_t* id) = 0;
- virtual input_property_t* inputGetDeviceProperty(input_property_map_t* map,
- const char* key) = 0;
- virtual const char* inputGetPropertyKey(input_property_t* property) = 0;
- virtual const char* inputGetPropertyValue(input_property_t* property) = 0;
- virtual void inputFreeDeviceProperty(input_property_t* property) = 0;
- virtual void inputFreeDevicePropertyMap(input_property_map_t* map) = 0;
-
- virtual void dump(String8& result) = 0;
-};
-
-class InputDriver : public InputDriverInterface {
-public:
- explicit InputDriver(const char* name);
- virtual ~InputDriver() = default;
-
- virtual void init() override;
-
- virtual input_device_identifier_t* createDeviceIdentifier(
- const char* name, int32_t productId, int32_t vendorId,
- input_bus_t bus, const char* uniqueId) override;
- virtual input_device_definition_t* createDeviceDefinition() override;
- virtual input_report_definition_t* createInputReportDefinition() override;
- virtual input_report_definition_t* createOutputReportDefinition() override;
- virtual void freeReportDefinition(input_report_definition_t* reportDef) override;
-
- virtual void inputDeviceDefinitionAddReport(input_device_definition_t* d,
- input_report_definition_t* r) override;
- virtual void inputReportDefinitionAddCollection(input_report_definition_t* report,
- input_collection_id_t id, int32_t arity) override;
- virtual void inputReportDefinitionDeclareUsageInt(input_report_definition_t* report,
- input_collection_id_t id, input_usage_t usage, int32_t min, int32_t max,
- float resolution) override;
- virtual void inputReportDefinitionDeclareUsagesBool(input_report_definition_t* report,
- input_collection_id_t id, input_usage_t* usage, size_t usageCount) override;
-
- virtual input_device_handle_t* registerDevice(input_device_identifier_t* id,
- input_device_definition_t* d) override;
- virtual void unregisterDevice(input_device_handle_t* handle) override;
-
- virtual input_report_t* inputAllocateReport(input_report_definition_t* r) override;
- virtual void inputReportSetUsageInt(input_report_t* r, input_collection_id_t id,
- input_usage_t usage, int32_t value, int32_t arity_index) override;
- virtual void inputReportSetUsageBool(input_report_t* r, input_collection_id_t id,
- input_usage_t usage, bool value, int32_t arity_index) override;
- virtual void reportEvent(input_device_handle_t* d, input_report_t* report) override;
-
- virtual input_property_map_t* inputGetDevicePropertyMap(input_device_identifier_t* id) override;
- virtual input_property_t* inputGetDeviceProperty(input_property_map_t* map,
- const char* key) override;
- virtual const char* inputGetPropertyKey(input_property_t* property) override;
- virtual const char* inputGetPropertyValue(input_property_t* property) override;
- virtual void inputFreeDeviceProperty(input_property_t* property) override;
- virtual void inputFreeDevicePropertyMap(input_property_map_t* map) override;
-
- virtual void dump(String8& result) override;
-
-private:
- String8 mName;
- const input_module_t* mHal;
-};
-
-
-extern "C" {
-
-input_device_identifier_t* create_device_identifier(input_host_t* host,
- const char* name, int32_t product_id, int32_t vendor_id,
- input_bus_t bus, const char* unique_id);
-
-input_device_definition_t* create_device_definition(input_host_t* host);
-
-input_report_definition_t* create_input_report_definition(input_host_t* host);
-
-input_report_definition_t* create_output_report_definition(input_host_t* host);
-
-void free_report_definition(input_host_t* host, input_report_definition_t* report_def);
-
-void input_device_definition_add_report(input_host_t* host,
- input_device_definition_t* d, input_report_definition_t* r);
-
-void input_report_definition_add_collection(input_host_t* host,
- input_report_definition_t* report, input_collection_id_t id, int32_t arity);
-
-void input_report_definition_declare_usage_int(input_host_t* host,
- input_report_definition_t* report, input_collection_id_t id,
- input_usage_t usage, int32_t min, int32_t max, float resolution);
-
-void input_report_definition_declare_usages_bool(input_host_t* host,
- input_report_definition_t* report, input_collection_id_t id,
- input_usage_t* usage, size_t usage_count);
-
-
-input_device_handle_t* register_device(input_host_t* host,
- input_device_identifier_t* id, input_device_definition_t* d);
-
-void unregister_device(input_host_t* host, input_device_handle_t* handle);
-
-input_report_t* input_allocate_report(input_host_t* host, input_report_definition_t* r);
-
-void input_report_set_usage_int(input_host_t* host, input_report_t* r,
- input_collection_id_t id, input_usage_t usage, int32_t value, int32_t arity_index);
-
-void input_report_set_usage_bool(input_host_t* host, input_report_t* r,
- input_collection_id_t id, input_usage_t usage, bool value, int32_t arity_index);
-
-void report_event(input_host_t* host, input_device_handle_t* d, input_report_t* report);
-
-input_property_map_t* input_get_device_property_map(input_host_t* host,
- input_device_identifier_t* id);
-
-input_property_t* input_get_device_property(input_host_t* host, input_property_map_t* map,
- const char* key);
-
-const char* input_get_property_key(input_host_t* host, input_property_t* property);
-
-const char* input_get_property_value(input_host_t* host, input_property_t* property);
-
-void input_free_device_property(input_host_t* host, input_property_t* property);
-
-void input_free_device_property_map(input_host_t* host, input_property_map_t* map);
-}
-
-} // namespace android
diff --git a/services/inputflinger/host/InputFlinger.cpp b/services/inputflinger/host/InputFlinger.cpp
deleted file mode 100644
index d974c43..0000000
--- a/services/inputflinger/host/InputFlinger.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "InputFlinger"
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/PermissionCache.h>
-#include <hardware/input.h>
-#include <log/log.h>
-#include <private/android_filesystem_config.h>
-
-#include "InputFlinger.h"
-#include "InputDriver.h"
-
-namespace android {
-
-const String16 sAccessInputFlingerPermission("android.permission.ACCESS_INPUT_FLINGER");
-const String16 sDumpPermission("android.permission.DUMP");
-
-
-InputFlinger::InputFlinger() :
- BnInputFlinger() {
- ALOGI("InputFlinger is starting");
- mHost = new InputHost();
- mHost->registerInputDriver(new InputDriver(INPUT_INSTANCE_EVDEV));
-}
-
-InputFlinger::~InputFlinger() {
-}
-
-status_t InputFlinger::dump(int fd, const Vector<String16>& args) {
- String8 result;
- const IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- if ((uid != AID_SHELL)
- && !PermissionCache::checkPermission(sDumpPermission, pid, uid)) {
- result.appendFormat("Permission Denial: "
- "can't dump InputFlinger from pid=%d, uid=%d\n", pid, uid);
- } else {
- dumpInternal(result);
- }
- write(fd, result.c_str(), result.size());
- return OK;
-}
-
-void InputFlinger::dumpInternal(String8& result) {
- result.append("INPUT FLINGER (dumpsys inputflinger)\n");
- mHost->dump(result);
-}
-
-}; // namespace android
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
deleted file mode 100644
index 388988b..0000000
--- a/services/inputflinger/host/InputFlinger.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "InputHost.h"
-
-#include <android/os/BnInputFlinger.h>
-#include <binder/Binder.h>
-#include <cutils/compiler.h>
-#include <utils/String16.h>
-#include <utils/String8.h>
-#include <utils/StrongPointer.h>
-
-using android::gui::FocusRequest;
-using android::os::BnInputFlinger;
-
-namespace android {
-
-class InputFlinger : public BnInputFlinger {
-public:
- static char const* getServiceName() ANDROID_API {
- return "inputflinger";
- }
-
- InputFlinger() ANDROID_API;
-
- status_t dump(int fd, const Vector<String16>& args) override;
- binder::Status createInputChannel(const std::string&, InputChannel*) override {
- return binder::Status::ok();
- }
- binder::Status removeInputChannel(const sp<IBinder>&) override { return binder::Status::ok(); }
- binder::Status setFocusedWindow(const FocusRequest&) override { return binder::Status::ok(); }
-
-private:
- ~InputFlinger() override;
-
- void dumpInternal(String8& result);
-
- sp<InputHostInterface> mHost;
-};
-
-} // namespace android
diff --git a/services/inputflinger/host/InputHost.cpp b/services/inputflinger/host/InputHost.cpp
deleted file mode 100644
index 094200a..0000000
--- a/services/inputflinger/host/InputHost.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <vector>
-
-#include "InputDriver.h"
-#include "InputHost.h"
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#define INDENT " "
-
-namespace android {
-
-void InputHost::registerInputDriver(InputDriverInterface* driver) {
- LOG_ALWAYS_FATAL_IF(driver == nullptr, "Cannot register a nullptr as an InputDriver!");
- driver->init();
- mDrivers.push_back(driver);
-}
-
-void InputHost::dump(String8& result) {
- result.append(INDENT "Input Drivers:\n");
- for (size_t i = 0; i < mDrivers.size(); i++) {
- mDrivers[i]->dump(result);
- }
-}
-
-} // namespace android
diff --git a/services/inputflinger/host/InputHost.h b/services/inputflinger/host/InputHost.h
deleted file mode 100644
index bdc4225..0000000
--- a/services/inputflinger/host/InputHost.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <vector>
-
-#include <hardware/input.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/StrongPointer.h>
-
-#include "InputDriver.h"
-
-namespace android {
-
-class InputDriverInterface;
-
-class InputHostInterface : public virtual RefBase {
-protected:
- InputHostInterface() = default;
- virtual ~InputHostInterface() = default;
-
-public:
-
- virtual void registerInputDriver(InputDriverInterface* driver) = 0;
-
- virtual void dump(String8& result) = 0;
-};
-
-class InputHost : public InputHostInterface {
-public:
- InputHost() = default;
-
- virtual void registerInputDriver(InputDriverInterface* driver) override;
-
- virtual void dump(String8& result) override;
-
-private:
- std::vector<sp<InputDriverInterface>> mDrivers;
-};
-
-} // namespace android
diff --git a/services/inputflinger/host/inputflinger.rc b/services/inputflinger/host/inputflinger.rc
deleted file mode 100644
index 4130ddc..0000000
--- a/services/inputflinger/host/inputflinger.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service inputflinger /system/bin/inputflinger
- class main
- user system
- group input wakelock
-# onrestart restart zygote
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index b565454..90bd7c9 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -130,7 +130,10 @@
TouchInputMapper::~TouchInputMapper() {}
uint32_t TouchInputMapper::getSources() const {
- return mSource;
+ // The SOURCE_BLUETOOTH_STYLUS is added to events dynamically if the current stream is modified
+ // by the external stylus state. That's why we don't add it directly to mSource during
+ // configuration.
+ return mSource | (hasExternalStylus() ? AINPUT_SOURCE_BLUETOOTH_STYLUS : 0);
}
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
@@ -932,9 +935,6 @@
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
}
- if (hasExternalStylus()) {
- mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
- }
} else if (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION) {
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
mDeviceMode = DeviceMode::NAVIGATION;
@@ -1664,6 +1664,10 @@
mSource, mViewport.displayId, policyFlags,
mLastCookedState.buttonState, mCurrentCookedState.buttonState);
+ if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
+ mCurrentStreamModifiedByExternalStylus = false;
+ }
+
// Clear some transient state.
mCurrentRawState.rawVScroll = 0;
mCurrentRawState.rawHScroll = 0;
@@ -1715,6 +1719,10 @@
mExternalStylusButtonsApplied |= pressedButtons;
mExternalStylusButtonsApplied &= ~releasedButtons;
+
+ if (mExternalStylusButtonsApplied != 0 || releasedButtons != 0) {
+ mCurrentStreamModifiedByExternalStylus = true;
+ }
}
}
@@ -1725,6 +1733,8 @@
return;
}
+ mCurrentStreamModifiedByExternalStylus = true;
+
float pressure = lastPointerData.isTouching(*mFusedStylusPointerId)
? lastPointerData.pointerCoordsForId(*mFusedStylusPointerId)
.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)
@@ -3821,6 +3831,9 @@
ALOG_ASSERT(false);
}
}
+ if (mCurrentStreamModifiedByExternalStylus) {
+ source |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
+ }
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
const bool showDirectStylusPointer = mConfig.stylusPointerIconEnabled &&
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index c5dfb00..bd9371d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -357,6 +357,8 @@
bool mExternalStylusDataPending;
// A subset of the buttons in mCurrentRawState that came from an external stylus.
int32_t mExternalStylusButtonsApplied{0};
+ // True if the current cooked pointer data was modified due to the state of an external stylus.
+ bool mCurrentStreamModifiedByExternalStylus{false};
// True if we sent a HOVER_ENTER event.
bool mSentHoverEnter{false};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index dc281a3..dd003a6 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3349,6 +3349,31 @@
}
/**
+ * Test that invalid HOVER events sent by accessibility do not cause a fatal crash.
+ */
+TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 1200, 800));
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ MotionEventBuilder hoverEnterBuilder =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
+ .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, hoverEnterBuilder.build()));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, hoverEnterBuilder.build()));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+}
+
+/**
* If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT.
*/
TEST_F(InputDispatcherTest, TouchDownAfterMouseHover) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bce0937..6539593 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -91,6 +91,9 @@
static constexpr int32_t ACTION_POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr uint32_t STYLUS_FUSION_SOURCE =
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_BLUETOOTH_STYLUS;
+
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
// Maximum smoothing time delta so that we don't generate events too far into the future.
@@ -2308,6 +2311,22 @@
// ongoing stylus gesture that is being emitted by the touchscreen.
using ExternalStylusIntegrationTest = BaseTouchIntegrationTest;
+TEST_F(ExternalStylusIntegrationTest, ExternalStylusConnectionChangesTouchscreenSource) {
+ // Create an external stylus capable of reporting pressure data that
+ // should be fused with a touch pointer.
+ std::unique_ptr<UinputExternalStylusWithPressure> stylus =
+ createUinputDevice<UinputExternalStylusWithPressure>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto stylusInfo = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(stylusInfo);
+
+ // Connecting an external stylus changes the source of the touchscreen.
+ const auto deviceInfo = findDeviceByName(mDevice->getName());
+ ASSERT_TRUE(deviceInfo);
+ ASSERT_TRUE(isFromSource(deviceInfo->getSources(), STYLUS_FUSION_SOURCE));
+}
+
TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
const Point centerPoint = mDevice->getCenterPoint();
@@ -2337,17 +2356,17 @@
mDevice->sendDown(centerPoint);
mDevice->sendSync();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithToolType(ToolType::STYLUS), WithButtonState(0),
- WithDeviceId(touchscreenId), WithPressure(100.f / RAW_PRESSURE_MAX))));
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithToolType(ToolType::STYLUS),
+ WithButtonState(0), WithSource(STYLUS_FUSION_SOURCE), WithDeviceId(touchscreenId),
+ WithPressure(100.f / RAW_PRESSURE_MAX))));
// Change the pressure on the external stylus, and ensure the touchscreen generates a MOVE
// event with the updated pressure.
stylus->setPressure(200);
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithToolType(ToolType::STYLUS), WithButtonState(0),
- WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX))));
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithToolType(ToolType::STYLUS),
+ WithButtonState(0), WithSource(STYLUS_FUSION_SOURCE), WithDeviceId(touchscreenId),
+ WithPressure(200.f / RAW_PRESSURE_MAX))));
// The external stylus did not generate any events.
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
@@ -2392,8 +2411,8 @@
// it shows up as a finger pointer.
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithToolType(ToolType::FINGER), WithDeviceId(touchscreenId),
- WithPressure(1.f))));
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
+ WithToolType(ToolType::FINGER), WithDeviceId(touchscreenId), WithPressure(1.f))));
// Change the pressure on the external stylus. Since the pressure was not present at the start
// of the gesture, it is ignored for now.
@@ -2405,6 +2424,7 @@
mDevice->sendSync();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
WithToolType(ToolType::FINGER))));
// Start a new gesture. Since we have a valid pressure value, it shows up as a stylus.
@@ -2413,9 +2433,9 @@
mDevice->sendDown(centerPoint);
mDevice->sendSync();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithToolType(ToolType::STYLUS), WithButtonState(0),
- WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX))));
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithSource(STYLUS_FUSION_SOURCE),
+ WithToolType(ToolType::STYLUS), WithButtonState(0), WithDeviceId(touchscreenId),
+ WithPressure(200.f / RAW_PRESSURE_MAX))));
// The external stylus did not generate any events.
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
@@ -2447,14 +2467,15 @@
std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT));
mDevice->sendSync();
ASSERT_NO_FATAL_FAILURE(
- mTestListener
- ->assertNotifyMotionWasCalled(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithToolType(
- ToolType::FINGER),
- WithButtonState(0),
- WithDeviceId(touchscreenId),
- WithPressure(1.f)),
- waitUntil));
+ mTestListener->assertNotifyMotionWasCalled(AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(ToolType::FINGER),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN |
+ AINPUT_SOURCE_STYLUS),
+ WithButtonState(0),
+ WithDeviceId(touchscreenId),
+ WithPressure(1.f)),
+ waitUntil));
// The external stylus did not generate any events.
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
@@ -7567,12 +7588,10 @@
protected:
StylusState mStylusState{};
- static constexpr uint32_t EXPECTED_SOURCE =
- AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_BLUETOOTH_STYLUS;
void testStartFusedStylusGesture(SingleTouchInputMapper& mapper) {
auto toolTypeSource =
- AllOf(WithSource(EXPECTED_SOURCE), WithToolType(ToolType::STYLUS));
+ AllOf(WithSource(STYLUS_FUSION_SOURCE), WithToolType(ToolType::STYLUS));
// The first pointer is withheld.
processDown(mapper, 100, 200);
@@ -7606,7 +7625,7 @@
processUp(mapper);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(STYLUS_FUSION_SOURCE),
WithToolType(ToolType::STYLUS))));
mStylusState.pressure = 0.f;
@@ -7616,8 +7635,10 @@
}
void testUnsuccessfulFusionGesture(SingleTouchInputMapper& mapper) {
+ // When stylus fusion is not successful, events should be reported with the original source.
+ // In this case, it is from a touchscreen.
auto toolTypeSource =
- AllOf(WithSource(EXPECTED_SOURCE), WithToolType(ToolType::FINGER));
+ AllOf(WithSource(AINPUT_SOURCE_TOUCHSCREEN), WithToolType(ToolType::FINGER));
// The first pointer is withheld when an external stylus is connected,
// and a timeout is requested.
@@ -7657,7 +7678,7 @@
TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
- ASSERT_EQ(EXPECTED_SOURCE, mapper.getSources());
+ ASSERT_EQ(STYLUS_FUSION_SOURCE, mapper.getSources());
}
TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) {
@@ -7674,8 +7695,7 @@
// before the touch is reported by the touchscreen.
TEST_F(ExternalStylusFusionTest, SuccessfulFusion_PressureFirst) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
- auto toolTypeSource =
- AllOf(WithSource(EXPECTED_SOURCE), WithToolType(ToolType::STYLUS));
+ auto toolTypeSource = AllOf(WithSource(STYLUS_FUSION_SOURCE), WithToolType(ToolType::STYLUS));
// The external stylus reports pressure first. It is ignored for now.
mStylusState.pressure = 1.f;
@@ -7717,8 +7737,7 @@
TEST_F(ExternalStylusFusionTest, FusedPointerReportsPressureChanges) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
- auto toolTypeSource =
- AllOf(WithSource(EXPECTED_SOURCE), WithToolType(ToolType::STYLUS));
+ auto toolTypeSource = AllOf(WithSource(STYLUS_FUSION_SOURCE), WithToolType(ToolType::STYLUS));
mStylusState.pressure = 0.8f;
processExternalStylusState(mapper);
@@ -7779,7 +7798,7 @@
processUp(mapper);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(STYLUS_FUSION_SOURCE),
WithToolType(ToolType::STYLUS))));
ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
@@ -7788,7 +7807,7 @@
TEST_F(ExternalStylusFusionTest, FusedPointerReportsToolTypeChanges) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
- auto source = WithSource(EXPECTED_SOURCE);
+ auto source = WithSource(STYLUS_FUSION_SOURCE);
mStylusState.pressure = 1.f;
mStylusState.toolType = ToolType::ERASER;
@@ -7841,8 +7860,7 @@
TEST_F(ExternalStylusFusionTest, FusedPointerReportsButtons) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
- auto toolTypeSource =
- AllOf(WithSource(EXPECTED_SOURCE), WithToolType(ToolType::STYLUS));
+ auto toolTypeSource = AllOf(WithSource(STYLUS_FUSION_SOURCE), WithToolType(ToolType::STYLUS));
ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper));
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index ed4829a..6870d4e 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -46,7 +46,6 @@
"unsigned-integer-overflow",
],
},
- address: true,
integer_overflow: true,
},
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 06c5e4c..2dc7332 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -148,18 +148,6 @@
"libvulkan",
],
sanitize: {
- // By using the address sanitizer, we not only uncover any issues
- // with the test, but also any issues with the code under test.
- //
- // Note: If you get an runtime link error like:
- //
- // CANNOT LINK EXECUTABLE "/data/local/tmp/libcompositionengine_test": library "libclang_rt.asan-aarch64-android.so" not found
- //
- // it is because the address sanitizer shared objects are not installed
- // by default in the system image.
- //
- // You can either "make dist tests" before flashing, or set this
- // option to false temporarily.
- address: true,
+ hwaddress: true,
},
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 22db247..fe56969 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -844,10 +844,16 @@
bool OutputLayer::needsFiltering() const {
const auto& state = getState();
- const auto& displayFrame = state.displayFrame;
const auto& sourceCrop = state.sourceCrop;
- return sourceCrop.getHeight() != displayFrame.getHeight() ||
- sourceCrop.getWidth() != displayFrame.getWidth();
+ auto displayFrameWidth = static_cast<float>(state.displayFrame.getWidth());
+ auto displayFrameHeight = static_cast<float>(state.displayFrame.getHeight());
+
+ if (state.bufferTransform & HAL_TRANSFORM_ROT_90) {
+ std::swap(displayFrameWidth, displayFrameHeight);
+ }
+
+ return sourceCrop.getHeight() != displayFrameHeight ||
+ sourceCrop.getWidth() != displayFrameWidth;
}
std::optional<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionSettings() const {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9039d16..630906a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1614,5 +1614,20 @@
EXPECT_TRUE(mOutputLayer.needsFiltering());
}
+TEST_F(OutputLayerTest, needsFilteringReturnsFalseIfRotatedDisplaySizeSameAsSourceSize) {
+ mOutputLayer.editState().displayFrame = Rect(100, 100, 300, 200);
+ mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.f, 200.f};
+ mOutputLayer.editState().bufferTransform = Hwc2::Transform::ROT_90;
+
+ EXPECT_FALSE(mOutputLayer.needsFiltering());
+}
+
+TEST_F(OutputLayerTest, needsFilteringReturnsTrueIfRotatedDisplaySizeDiffersFromSourceSize) {
+ mOutputLayer.editState().displayFrame = Rect(100, 100, 300, 200);
+ mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.f, 200.f};
+
+ EXPECT_TRUE(mOutputLayer.needsFiltering());
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 252ba8e..1faf6a1 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -477,8 +477,12 @@
void DisplayDevice::updateRefreshRateOverlayRate(Fps vsyncRate, Fps renderFps, bool setByHwc) {
ATRACE_CALL();
- if (mRefreshRateOverlay && (!mRefreshRateOverlay->isSetByHwc() || setByHwc)) {
- mRefreshRateOverlay->changeRefreshRate(vsyncRate, renderFps);
+ if (mRefreshRateOverlay) {
+ if (!mRefreshRateOverlay->isSetByHwc() || setByHwc) {
+ mRefreshRateOverlay->changeRefreshRate(vsyncRate, renderFps);
+ } else {
+ mRefreshRateOverlay->changeRenderRate(renderFps);
+ }
}
}
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 422513b..1775a7a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -31,8 +31,7 @@
#include "DisplayHardware/Hal.h"
#include "Scheduler/StrongTyping.h"
-
-#include <com_android_graphics_surfaceflinger_flags.h>
+#include "Utils/FlagUtils.h"
namespace android {
@@ -141,7 +140,7 @@
// Peak refresh rate represents the highest refresh rate that can be used
// for the presentation.
Fps getPeakFps() const {
- return flags::vrr_config() && mVrrConfig
+ return flagutils::vrrConfigEnabled() && mVrrConfig
? Fps::fromPeriodNsecs(mVrrConfig->minFrameIntervalNs)
: mVsyncRate;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 7537a39..a5e9368 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -85,6 +85,7 @@
bool isTrustedOverlay;
gui::GameMode gameMode;
scheduler::LayerInfo::FrameRate frameRate;
+ scheduler::LayerInfo::FrameRateSelectionStrategy frameRateSelectionStrategy;
ui::Transform::RotationFlags fixedTransformHint;
std::optional<ui::Transform::RotationFlags> transformHint;
bool handleSkipScreenshotFlag = false;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index da84e44..4c9fb06 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -813,11 +813,23 @@
RequestedLayerState::Changes::Hierarchy) ||
snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
RequestedLayerState::Changes::Hierarchy)) {
- snapshot.frameRate = requested.requestedFrameRate.isValid() ? requested.requestedFrameRate
- : parentSnapshot.frameRate;
+ bool shouldOverrideChildren = parentSnapshot.frameRateSelectionStrategy ==
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren;
+ snapshot.frameRate = !requested.requestedFrameRate.isValid() || shouldOverrideChildren
+ ? parentSnapshot.frameRate
+ : requested.requestedFrameRate;
snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
+ if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionStrategyChanged) {
+ const auto strategy = scheduler::LayerInfo::convertFrameRateSelectionStrategy(
+ requested.frameRateSelectionStrategy);
+ snapshot.frameRateSelectionStrategy =
+ strategy == scheduler::LayerInfo::FrameRateSelectionStrategy::Self
+ ? parentSnapshot.frameRateSelectionStrategy
+ : strategy;
+ }
+
if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) {
snapshot.frameRateSelectionPriority =
(requested.frameRateSelectionPriority == Layer::PRIORITY_UNSET)
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 57ebee9..dc8694c 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -125,6 +125,8 @@
defaultFrameRateCompatibility =
static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
frameRateCategory = static_cast<int8_t>(FrameRateCategory::Default);
+ frameRateSelectionStrategy =
+ static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
dataspace = ui::Dataspace::V0_SRGB;
gameMode = gui::GameMode::Unsupported;
requestedFrameRate = {};
@@ -403,10 +405,19 @@
return debug.str();
}
+std::ostream& operator<<(std::ostream& out, const scheduler::LayerInfo::FrameRate& obj) {
+ out << obj.vote.rate;
+ out << " " << ftl::enum_string_full(obj.vote.type);
+ out << " " << ftl::enum_string_full(obj.category);
+ return out;
+}
+
std::ostream& operator<<(std::ostream& out, const RequestedLayerState& obj) {
out << obj.debugName;
if (obj.relativeParentId != UNASSIGNED_LAYER_ID) out << " parent=" << obj.parentId;
if (!obj.handleAlive) out << " handleNotAlive";
+ if (obj.requestedFrameRate.isValid())
+ out << " requestedFrameRate: {" << obj.requestedFrameRate << "}";
return out;
}
@@ -466,12 +477,18 @@
Rect RequestedLayerState::getBufferCrop() const {
// this is the crop rectangle that applies to the buffer
// itself (as opposed to the window)
- if (!bufferCrop.isEmpty()) {
- // if the buffer crop is defined, we use that
- return bufferCrop;
+ if (!bufferCrop.isEmpty() && externalTexture != nullptr) {
+ // if the buffer crop is defined and there's a valid buffer, intersect buffer size and crop
+ // since the crop should never exceed the size of the buffer.
+ Rect sizeAndCrop;
+ externalTexture->getBounds().intersect(bufferCrop, &sizeAndCrop);
+ return sizeAndCrop;
} else if (externalTexture != nullptr) {
// otherwise we use the whole buffer
return externalTexture->getBounds();
+ } else if (!bufferCrop.isEmpty()) {
+ // if the buffer crop is defined, we use that
+ return bufferCrop;
} else {
// if we don't have a buffer yet, we use an empty/invalid crop
return Rect();
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2684000..e780a1e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -192,6 +192,7 @@
mDrawingState.dropInputMode = gui::DropInputMode::NONE;
mDrawingState.dimmingEnabled = true;
mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default;
+ mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Self;
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -1251,9 +1252,14 @@
return mBorderColor;
}
-bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded) {
- // The frame rate for layer tree is this layer's frame rate if present, or the parent frame rate
+bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
+ bool* transactionNeeded) {
+ // Gets the frame rate to propagate to children.
const auto frameRate = [&] {
+ if (overrideChildren && parentFrameRate.isValid()) {
+ return parentFrameRate;
+ }
+
if (mDrawingState.frameRate.isValid()) {
return mDrawingState.frameRate;
}
@@ -1267,7 +1273,10 @@
bool childrenHaveFrameRate = false;
for (const sp<Layer>& child : mCurrentChildren) {
childrenHaveFrameRate |=
- child->propagateFrameRateForLayerTree(frameRate, transactionNeeded);
+ child->propagateFrameRateForLayerTree(frameRate,
+ overrideChildren ||
+ shouldOverrideChildrenFrameRate(),
+ transactionNeeded);
}
// If we don't have a valid frame rate specification, but the children do, we set this
@@ -1300,7 +1309,7 @@
}();
bool transactionNeeded = false;
- root->propagateFrameRateForLayerTree({}, &transactionNeeded);
+ root->propagateFrameRateForLayerTree({}, false, &transactionNeeded);
// TODO(b/195668952): we probably don't need eTraversalNeeded here
if (transactionNeeded) {
@@ -1338,6 +1347,15 @@
return true;
}
+bool Layer::setFrameRateSelectionStrategy(FrameRateSelectionStrategy strategy) {
+ if (mDrawingState.frameRateSelectionStrategy == strategy) return false;
+ mDrawingState.frameRateSelectionStrategy = strategy;
+ mDrawingState.sequence++;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
nsecs_t postTime) {
mDrawingState.postTime = postTime;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 0b0cef5..40882f4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -112,6 +112,7 @@
using FrameRate = scheduler::LayerInfo::FrameRate;
using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+ using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
struct State {
int32_t z;
@@ -188,6 +189,8 @@
// The combined frame rate of parents / children of this layer
FrameRate frameRateForLayerTree;
+ FrameRateSelectionStrategy frameRateSelectionStrategy;
+
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
// the graphic producers should receive a transform hint as if the
@@ -782,6 +785,8 @@
bool setFrameRate(FrameRate::FrameRateVote);
bool setFrameRateCategory(FrameRateCategory);
+ bool setFrameRateSelectionStrategy(FrameRateSelectionStrategy);
+
virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {}
void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime);
void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
@@ -1097,7 +1102,8 @@
const std::vector<Layer*>& layersInTree);
void updateTreeHasFrameRateVote();
- bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded);
+ bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
+ bool* transactionNeeded);
void setZOrderRelativeOf(const wp<Layer>& relativeOf);
bool isTrustedOverlay() const;
gui::DropInputMode getDropInputMode() const;
@@ -1159,6 +1165,11 @@
void transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
std::vector<JankData>& jankData);
+ bool shouldOverrideChildrenFrameRate() const {
+ return getDrawingState().frameRateSelectionStrategy ==
+ FrameRateSelectionStrategy::OverrideChildren;
+ }
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
// a transform from the current layer coordinate space to display(screen) coordinate space.
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 3270e4c..0aee7d4 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -4,6 +4,7 @@
alecmouri@google.com
chaviw@google.com
domlaskowski@google.com
+jreck@google.com
lpy@google.com
pdwilliams@google.com
racarr@google.com
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index e918dc9..be04c09 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -19,6 +19,7 @@
#include "Client.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
+#include "Utils/FlagUtils.h"
#include <SkSurface.h>
@@ -249,6 +250,14 @@
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
+void RefreshRateOverlay::changeRenderRate(Fps renderFps) {
+ if (mFeatures.test(Features::RenderRate) && mVsyncRate && flagutils::vrrConfigEnabled()) {
+ mRenderFps = renderFps;
+ const auto buffer = getOrCreateBuffers(*mVsyncRate, renderFps)[mFrame];
+ createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+ }
+}
+
void RefreshRateOverlay::animate() {
if (!mFeatures.test(Features::Spinner) || !mVsyncRate) return;
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index c0fc79b..ae334e5 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -50,6 +50,7 @@
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
void changeRefreshRate(Fps, Fps);
+ void changeRenderRate(Fps);
void animate();
bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 23eb31f..6b5327a 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -41,7 +41,7 @@
bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
// Layers with an explicit frame rate or frame rate category are always kept active,
- // but ignore NoVote/NoPreference.
+ // but ignore NoVote.
if (info.getSetFrameRateVote().isValid() && !info.getSetFrameRateVote().isNoVote()) {
return true;
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 551d744..875bdc8 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -116,12 +116,24 @@
}
}
+ // Vote the small dirty when a layer contains at least HISTORY_SIZE of small dirty updates.
+ bool isSmallDirty = false;
+ if (smallDirtyCount >= kNumSmallDirtyThreshold) {
+ if (mLastSmallDirtyCount >= HISTORY_SIZE) {
+ isSmallDirty = true;
+ } else {
+ mLastSmallDirtyCount++;
+ }
+ } else {
+ mLastSmallDirtyCount = 0;
+ }
+
if (isFrequent || isInfrequent) {
// If the layer was previously inconclusive, we clear
// the history as indeterminate layers changed to frequent,
// and we should not look at the stale data.
return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true,
- /* isSmallDirty */ smallDirtyCount >= kNumSmallDirtyThreshold};
+ isSmallDirty};
}
// If we can't determine whether the layer is frequent or not, we return
@@ -292,6 +304,8 @@
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
if (mLayerVote.category != FrameRateCategory::Default) {
+ ATRACE_FORMAT_INSTANT("ExplicitCategory (%s)",
+ ftl::enum_string(mLayerVote.category).c_str());
ALOGV("%s uses frame rate category: %d", mName.c_str(),
static_cast<int>(mLayerVote.category));
votes.push_back({LayerHistory::LayerVoteType::ExplicitCategory, Fps(),
@@ -300,6 +314,7 @@
if (mLayerVote.fps.isValid() ||
mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) {
+ ATRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str());
ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
votes.push_back(mLayerVote);
}
@@ -321,6 +336,7 @@
ATRACE_FORMAT_INSTANT("infrequent");
ALOGV("%s is infrequent", mName.c_str());
mLastRefreshRate.infrequent = true;
+ mLastSmallDirtyCount = 0;
// Infrequent layers vote for minimal refresh rate for
// battery saving purposes and also to prevent b/135718869.
votes.push_back({LayerHistory::LayerVoteType::Min, Fps()});
@@ -331,7 +347,7 @@
clearHistory(now);
}
- // Return no vote if the latest frames are small dirty.
+ // Return no vote if the recent frames are small dirty.
if (frequent.isSmallDirty && !mLastRefreshRate.reported.isValid()) {
ATRACE_FORMAT_INSTANT("NoVote (small dirty)");
ALOGV("%s is small dirty", mName.c_str());
@@ -341,11 +357,13 @@
auto refreshRate = calculateRefreshRateIfPossible(selector, now);
if (refreshRate.has_value()) {
+ ATRACE_FORMAT_INSTANT("calculated (%s)", to_string(*refreshRate).c_str());
ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
votes.push_back({LayerHistory::LayerVoteType::Heuristic, refreshRate.value()});
return votes;
}
+ ATRACE_FORMAT_INSTANT("Max (can't resolve refresh rate)");
ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
return votes;
@@ -485,9 +503,21 @@
}
}
+LayerInfo::FrameRateSelectionStrategy LayerInfo::convertFrameRateSelectionStrategy(
+ int8_t strategy) {
+ switch (strategy) {
+ case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF:
+ return FrameRateSelectionStrategy::Self;
+ case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN:
+ return FrameRateSelectionStrategy::OverrideChildren;
+ default:
+ LOG_ALWAYS_FATAL("Invalid frame rate selection strategy value %d", strategy);
+ return FrameRateSelectionStrategy::Self;
+ }
+}
+
bool LayerInfo::FrameRate::isNoVote() const {
- return vote.type == FrameRateCompatibility::NoVote ||
- category == FrameRateCategory::NoPreference;
+ return vote.type == FrameRateCompatibility::NoVote;
}
bool LayerInfo::FrameRate::isValid() const {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 1e08ec8..129b4c4 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -73,7 +73,7 @@
FrameRateCategory category = FrameRateCategory::Default;
// Returns true if the layer explicitly should contribute to frame rate scoring.
- bool isNoVote() const { return RefreshRateSelector::isNoVote(type, category); }
+ bool isNoVote() const { return RefreshRateSelector::isNoVote(type); }
};
using RefreshRateVotes = ftl::SmallVector<LayerInfo::LayerVote, 2>;
@@ -96,6 +96,13 @@
ftl_last = NoVote
};
+ enum class FrameRateSelectionStrategy {
+ Self,
+ OverrideChildren,
+
+ ftl_last = OverrideChildren
+ };
+
// Encapsulates the frame rate specifications of the layer. This information will be used
// when the display refresh rate is determined.
struct FrameRate {
@@ -139,11 +146,11 @@
static FrameRateCompatibility convertCompatibility(int8_t compatibility);
// Convert an ANATIVEWINDOW_CHANGE_FRAME_RATE_* value to a scheduler::Seamlessness.
- // Logs fatal if the compatibility value is invalid.
+ // Logs fatal if the strategy value is invalid.
static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
// Convert an ANATIVEWINDOW_FRAME_RATE_CATEGORY_* value to a FrameRateCategory.
- // Logs fatal if the compatibility value is invalid.
+ // Logs fatal if the category value is invalid.
static FrameRateCategory convertCategory(int8_t category);
// True if the FrameRate has explicit frame rate specifications.
@@ -164,6 +171,10 @@
}
};
+ // Convert an ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_* value to FrameRateSelectionStrategy.
+ // Logs fatal if the strategy value is invalid.
+ static FrameRateSelectionStrategy convertFrameRateSelectionStrategy(int8_t strategy);
+
static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
LayerInfo(const std::string& name, uid_t ownerUid, LayerHistory::LayerVoteType defaultVote);
@@ -346,6 +357,10 @@
RefreshRateHistory mRefreshRateHistory;
+ // This will be accessed from only one thread when counting a layer is frequent or infrequent,
+ // and to determine whether a layer is in small dirty updating.
+ mutable int32_t mLastSmallDirtyCount = 0;
+
mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
// Shared for all LayerInfo instances
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 3ee6a4d..b06723d 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -38,6 +38,7 @@
#include "../SurfaceFlingerProperties.h"
#include "RefreshRateSelector.h"
+#include "Utils/FlagUtils.h"
#include <com_android_graphics_surfaceflinger_flags.h>
@@ -114,7 +115,7 @@
using fps_approx_ops::operator/;
// use signed type as `fps / range.max` might be 0
auto start = std::max(1, static_cast<int>(peakFps / range.max) - 1);
- if (flags::vrr_config()) {
+ if (flagutils::vrrConfigEnabled()) {
start = std::max(1,
static_cast<int>(vsyncRate /
std::min(range.max, peakFps, fps_approx_ops::operator<)) -
@@ -599,7 +600,8 @@
layer.name.c_str(), ftl::enum_string(layer.vote).c_str(), layer.weight,
layer.desiredRefreshRate.getValue(),
ftl::enum_string(layer.frameRateCategory).c_str());
- if (layer.isNoVote() || layer.vote == LayerVoteType::Min) {
+ if (layer.isNoVote() || layer.frameRateCategory == FrameRateCategory::NoPreference ||
+ layer.vote == LayerVoteType::Min) {
continue;
}
@@ -904,7 +906,8 @@
}
for (const auto& layer : layersWithSameUid) {
- if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
+ if (layer->isNoVote() || layer->frameRateCategory == FrameRateCategory::NoPreference ||
+ layer->vote == LayerVoteType::Min) {
continue;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 9bcbc0e..5d32414 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -187,14 +187,12 @@
bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
- bool isNoVote() const { return RefreshRateSelector::isNoVote(vote, frameRateCategory); }
+ bool isNoVote() const { return RefreshRateSelector::isNoVote(vote); }
};
// Returns true if the layer explicitly instructs to not contribute to refresh rate selection.
// In other words, true if the layer should be ignored.
- static bool isNoVote(LayerVoteType vote, FrameRateCategory category) {
- return vote == LayerVoteType::NoVote || category == FrameRateCategory::NoPreference;
- }
+ static bool isNoVote(LayerVoteType vote) { return vote == LayerVoteType::NoVote; }
// Global state describing signals that affect refresh rate choice.
struct GlobalSignals {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 312a412..4959e25 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -155,6 +155,7 @@
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
#include "Utils/Dumper.h"
+#include "Utils/FlagUtils.h"
#include "WindowInfosListenerInvoker.h"
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
@@ -498,7 +499,6 @@
mMiscFlagValue = flags::misc1();
mConnectedDisplayFlagValue = flags::connected_display();
mMisc2FlagEarlyBootValue = flags::late_boot_misc2();
- mVrrConfigFlagValue = flags::vrr_config();
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -1355,8 +1355,7 @@
continue;
}
- if (!display->isPoweredOn()) {
- // Display is no longer powered on, so abort the mode change.
+ if (!shouldApplyRefreshRateSelectorPolicy(*display)) {
clearDesiredActiveModeState(display);
continue;
}
@@ -3953,8 +3952,9 @@
if (!display) continue;
- if (!display->isPoweredOn()) {
- ALOGV("%s(%s): Display is powered off", __func__, to_string(displayId).c_str());
+ if (ftl::FakeGuard guard(kMainThreadContext);
+ !shouldApplyRefreshRateSelectorPolicy(*display)) {
+ ALOGV("%s(%s): Skipped applying policy", __func__, to_string(displayId).c_str());
continue;
}
@@ -5206,6 +5206,14 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eFrameRateSelectionStrategyChanged) {
+ const scheduler::LayerInfo::FrameRateSelectionStrategy strategy =
+ scheduler::LayerInfo::convertFrameRateSelectionStrategy(
+ s.frameRateSelectionStrategy);
+ if (layer->setFrameRateSelectionStrategy(strategy)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eFixedTransformHintChanged) {
if (layer->setFixedTransformHint(s.fixedTransformHint)) {
flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
@@ -5820,6 +5828,20 @@
StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
pid, uid);
} else {
+ Dumper hwclayersDump = [this](const DumpArgs&, bool, std::string& result)
+ FTL_FAKE_GUARD(mStateLock) -> void const {
+ if (mLayerLifecycleManagerEnabled) {
+ mScheduler
+ ->schedule([this, &result]() FTL_FAKE_GUARD(kMainThreadContext)
+ FTL_FAKE_GUARD(mStateLock) {
+ dumpHwcLayersMinidump(result);
+ })
+ .get();
+ } else {
+ dumpHwcLayersMinidumpLockedLegacy(result);
+ }
+ };
+
static const std::unordered_map<std::string, Dumper> dumpers = {
{"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)},
{"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
@@ -5828,7 +5850,7 @@
{"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
{"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
{"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
- {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy)},
+ {"--hwclayers"s, std::move(hwclayersDump)},
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
{"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
@@ -6396,7 +6418,8 @@
StringAppendF(&result, "Misc2FlagValue: %s (%s after boot)\n",
mMisc2FlagLateBootValue ? "true" : "false",
mMisc2FlagEarlyBootValue == mMisc2FlagLateBootValue ? "stable" : "modified");
- StringAppendF(&result, "VrrConfigFlagValue: %s\n", mVrrConfigFlagValue ? "true" : "false");
+ StringAppendF(&result, "VrrConfigFlagValue: %s\n",
+ flagutils::vrrConfigEnabled() ? "true" : "false");
getRenderEngine().dump(result);
@@ -7005,7 +7028,10 @@
}
case 1041: { // Transaction tracing
if (mTransactionTracing) {
- if (data.readInt32()) {
+ int arg = data.readInt32();
+ if (arg == -1) {
+ mTransactionTracing.reset();
+ } else if (arg > 0) {
// Transaction tracing is always running but allow the user to temporarily
// increase the buffer when actively debugging.
mTransactionTracing->setBufferSize(
@@ -7743,6 +7769,7 @@
ATRACE_CALL();
auto layers = getLayerSnapshots();
+
for (auto& [_, layerFE] : layers) {
frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
@@ -7794,19 +7821,30 @@
pickBestDataspace(requestedDataspace, display, captureResults.capturedHdrLayers,
renderArea->getHintForSeamlessTransition());
sdrWhitePointNits = state.sdrWhitePointNits;
- displayBrightnessNits = state.displayBrightnessNits;
- // Only clamp the display brightness if this is not a seamless transition. Otherwise
- // for seamless transitions it's important to match the current display state as the
- // buffer will be shown under these same conditions, and we want to avoid any flickers
- if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
- // Restrict the amount of HDR "headroom" in the screenshot to avoid over-dimming
- // the SDR portion. 2.0 chosen by experimentation
- constexpr float kMaxScreenshotHeadroom = 2.0f;
- displayBrightnessNits =
- std::min(sdrWhitePointNits * kMaxScreenshotHeadroom, displayBrightnessNits);
+
+ // TODO(b/298219334): Clean this up once we verify this doesn't break anything
+ static constexpr bool kScreenshotsDontDim = true;
+
+ if (kScreenshotsDontDim && !captureResults.capturedHdrLayers) {
+ displayBrightnessNits = sdrWhitePointNits;
+ } else {
+ displayBrightnessNits = state.displayBrightnessNits;
+ // Only clamp the display brightness if this is not a seamless transition. Otherwise
+ // for seamless transitions it's important to match the current display state as the
+ // buffer will be shown under these same conditions, and we want to avoid any
+ // flickers
+ if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
+ // Restrict the amount of HDR "headroom" in the screenshot to avoid over-dimming
+ // the SDR portion. 2.0 chosen by experimentation
+ constexpr float kMaxScreenshotHeadroom = 2.0f;
+ displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom,
+ displayBrightnessNits);
+ }
}
- if (requestedDataspace == ui::Dataspace::UNKNOWN) {
+ // Screenshots leaving the device should be colorimetric
+ if (requestedDataspace == ui::Dataspace::UNKNOWN &&
+ renderArea->getHintForSeamlessTransition()) {
renderIntent = state.renderIntent;
}
}
@@ -7850,6 +7888,10 @@
}
}
+ // Screenshots leaving the device must not dim in gamma space.
+ const bool dimInGammaSpaceForEnhancedScreenshots = mDimInGammaSpaceForEnhancedScreenshots &&
+ renderArea->getHintForSeamlessTransition();
+
std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput(
ScreenCaptureOutputArgs{.compositionEngine = *compositionEngine,
.colorProfile = colorProfile,
@@ -7862,7 +7904,7 @@
.regionSampling = regionSampling,
.treat170mAsSrgb = mTreat170mAsSrgb,
.dimInGammaSpaceForEnhancedScreenshots =
- mDimInGammaSpaceForEnhancedScreenshots});
+ dimInGammaSpaceForEnhancedScreenshots});
const float colorSaturation = grayscale ? 0 : 1;
compositionengine::CompositionRefreshArgs refreshArgs{
@@ -8008,17 +8050,33 @@
break;
}
- // TODO(b/255635711): Apply the policy once the display is powered on, which is currently only
- // done for the internal display that becomes active on fold/unfold. For now, assume that DM
- // always powers on the secondary (internal or external) display before setting its policy.
- if (!display->isPoweredOn()) {
- ALOGV("%s(%s): Display is powered off", __func__, to_string(displayId).c_str());
+ if (!shouldApplyRefreshRateSelectorPolicy(*display)) {
+ ALOGV("%s(%s): Skipped applying policy", __func__, to_string(displayId).c_str());
return NO_ERROR;
}
return applyRefreshRateSelectorPolicy(displayId, selector);
}
+bool SurfaceFlinger::shouldApplyRefreshRateSelectorPolicy(const DisplayDevice& display) const {
+ if (display.isPoweredOn() || mPhysicalDisplays.size() == 1) return true;
+
+ LOG_ALWAYS_FATAL_IF(display.isVirtual());
+ const auto displayId = display.getPhysicalId();
+
+ // The display is powered off, and this is a multi-display device. If the display is the
+ // inactive internal display of a dual-display foldable, then the policy will be applied
+ // when it becomes active upon powering on.
+ //
+ // TODO(b/255635711): Remove this function (i.e. returning `false` as a special case) once
+ // concurrent mode setting across multiple (potentially powered off) displays is supported.
+ //
+ return displayId == mActiveDisplayId ||
+ !mPhysicalDisplays.get(displayId)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+}
+
status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector, bool force) {
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ef6b815..f9ea2ee 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -705,6 +705,9 @@
const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&)
EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+ bool shouldApplyRefreshRateSelectorPolicy(const DisplayDevice&) const
+ REQUIRES(mStateLock, kMainThreadContext);
+
// TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter.
status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId,
const scheduler::RefreshRateSelector&,
@@ -1457,7 +1460,6 @@
bool mConnectedDisplayFlagValue;
bool mMisc2FlagEarlyBootValue;
bool mMisc2FlagLateBootValue;
- bool mVrrConfigFlagValue;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/Tracing/LayerDataSource.cpp b/services/surfaceflinger/Tracing/LayerDataSource.cpp
index 474fef8..25e768e 100644
--- a/services/surfaceflinger/Tracing/LayerDataSource.cpp
+++ b/services/surfaceflinger/Tracing/LayerDataSource.cpp
@@ -52,7 +52,7 @@
mMode = static_cast<LayerTracing::Mode>(config.mode());
} else {
mMode = LayerTracing::Mode::MODE_GENERATED;
- ALOGV("Received config with unspecified 'mode'. Using 'GENERATED' as default");
+ ALOGD("Received config with unspecified 'mode'. Using 'GENERATED' as default");
}
mFlags = 0;
@@ -62,21 +62,21 @@
}
void LayerDataSource::OnStart(const LayerDataSource::StartArgs&) {
- ALOGV("Received OnStart event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
+ ALOGD("Received OnStart event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
if (auto* p = mLayerTracing.load()) {
p->onStart(mMode, mFlags);
}
}
void LayerDataSource::OnFlush(const LayerDataSource::FlushArgs&) {
- ALOGV("Received OnFlush event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
+ ALOGD("Received OnFlush event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
if (auto* p = mLayerTracing.load()) {
p->onFlush(mMode, mFlags);
}
}
void LayerDataSource::OnStop(const LayerDataSource::StopArgs&) {
- ALOGV("Received OnStop event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
+ ALOGD("Received OnStop event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
if (auto* p = mLayerTracing.load()) {
p->onStop(mMode);
}
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index e55b4c2..403e105 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -36,6 +36,10 @@
LayerDataSource::Initialize(*this);
}
+LayerTracing::LayerTracing(std::ostream& outStream) : LayerTracing() {
+ mOutStream = std::ref(outStream);
+}
+
LayerTracing::~LayerTracing() {
LayerDataSource::UnregisterLayerTracing();
}
@@ -49,10 +53,6 @@
mTransactionTracing = &transactionTracing;
}
-void LayerTracing::setOutputStream(std::ostream& outStream) {
- mOutStream = std::ref(outStream);
-}
-
void LayerTracing::onStart(Mode mode, uint32_t flags) {
switch (mode) {
case Mode::MODE_ACTIVE: {
@@ -63,18 +63,17 @@
// taken. Let's manually take a snapshot, so that the trace's first entry will contain
// the current layers state.
addProtoSnapshotToOstream(mTakeLayersSnapshotProto(flags), Mode::MODE_ACTIVE);
- ALOGV("Started active tracing (traced initial snapshot)");
+ ALOGD("Started active tracing (traced initial snapshot)");
break;
}
case Mode::MODE_GENERATED: {
- ALOGV("Started generated tracing (waiting for OnFlush event to generated layers)");
+ ALOGD("Started generated tracing (waiting for OnFlush event to generated layers)");
break;
}
case Mode::MODE_DUMP: {
- ALOGV("Starting dump tracing (dumping single snapshot)");
auto snapshot = mTakeLayersSnapshotProto(flags);
addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP);
- ALOGV("Started dump tracing (dumped single snapshot)");
+ ALOGD("Started dump tracing (dumped single snapshot)");
break;
}
default: {
@@ -91,19 +90,19 @@
}
if (!mTransactionTracing) {
- ALOGV("Skipping layers trace generation (transactions tracing disabled)");
+ ALOGD("Skipping layers trace generation (transactions tracing disabled)");
return;
}
auto transactionTrace = mTransactionTracing->writeToProto();
- LayerTraceGenerator{}.generate(transactionTrace, flags);
- ALOGV("Flushed generated tracing");
+ LayerTraceGenerator{}.generate(transactionTrace, flags, *this);
+ ALOGD("Flushed generated tracing");
}
void LayerTracing::onStop(Mode mode) {
if (mode == Mode::MODE_ACTIVE) {
mIsActiveTracingStarted.store(false);
- ALOGV("Stopped active tracing");
+ ALOGD("Stopped active tracing");
}
}
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index 349cc40..fe7f06d 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -97,11 +97,11 @@
};
LayerTracing();
+ LayerTracing(std::ostream&);
~LayerTracing();
void setTakeLayersSnapshotProtoFunction(
const std::function<perfetto::protos::LayersSnapshotProto(uint32_t)>&);
void setTransactionTracing(TransactionTracing&);
- void setOutputStream(std::ostream&);
// Start event from perfetto data source
void onStart(Mode mode, uint32_t flags);
diff --git a/services/surfaceflinger/Tracing/TransactionDataSource.cpp b/services/surfaceflinger/Tracing/TransactionDataSource.cpp
index 05b89b8..6c9ed30 100644
--- a/services/surfaceflinger/Tracing/TransactionDataSource.cpp
+++ b/services/surfaceflinger/Tracing/TransactionDataSource.cpp
@@ -49,26 +49,26 @@
mMode = static_cast<TransactionTracing::Mode>(config.mode());
} else {
mMode = TransactionTracing::Mode::MODE_CONTINUOUS;
- ALOGV("Received config with unspecified 'mode'. Using 'CONTINUOUS' as default");
+ ALOGD("Received config with unspecified 'mode'. Using 'CONTINUOUS' as default");
}
}
void TransactionDataSource::OnStart(const StartArgs&) {
- ALOGV("Received OnStart event");
+ ALOGD("Received OnStart event");
if (auto* p = mTransactionTracing.load()) {
p->onStart(mMode);
}
}
void TransactionDataSource::OnFlush(const FlushArgs&) {
- ALOGV("Received OnFlush event");
+ ALOGD("Received OnFlush event");
if (auto* p = mTransactionTracing.load()) {
p->onFlush(mMode);
}
}
void TransactionDataSource::OnStop(const StopArgs&) {
- ALOGV("Received OnStop event");
+ ALOGD("Received OnStop event");
}
TransactionTracing::Mode TransactionDataSource::GetMode() const {
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 0517984..9d6d87e 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -70,7 +70,7 @@
writeRingBufferToPerfetto(TransactionTracing::Mode::MODE_ACTIVE);
- ALOGV("Started active mode tracing (wrote initial transactions ring buffer to perfetto)");
+ ALOGD("Started active mode tracing (wrote initial transactions ring buffer to perfetto)");
}
void TransactionTracing::onFlush(TransactionTracing::Mode mode) {
@@ -83,7 +83,7 @@
writeRingBufferToPerfetto(TransactionTracing::Mode::MODE_CONTINUOUS);
- ALOGV("Flushed continuous mode tracing (wrote transactions ring buffer to perfetto");
+ ALOGD("Flushed continuous mode tracing (wrote transactions ring buffer to perfetto");
}
void TransactionTracing::writeRingBufferToPerfetto(TransactionTracing::Mode mode) {
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 62c362e..23fe8fa 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -41,8 +41,7 @@
using namespace ftl::flag_operators;
bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& traceFile,
- std::uint32_t traceFlags,
- std::optional<std::reference_wrapper<std::ostream>> outStream,
+ std::uint32_t traceFlags, LayerTracing& layerTracing,
bool onlyLastEntry) {
if (traceFile.entry_size() == 0) {
ALOGD("Trace file is empty");
@@ -51,11 +50,6 @@
TransactionProtoParser parser(std::make_unique<TransactionProtoParser::FlingerDataMapper>());
- LayerTracing layerTracing;
- if (outStream) {
- layerTracing.setOutputStream(outStream->get());
- }
-
// frontend
frontend::LayerLifecycleManager lifecycleManager;
frontend::LayerHierarchyBuilder hierarchyBuilder{{}};
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
index 2bb6f51..e4d02ca 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.h
@@ -30,7 +30,6 @@
class LayerTraceGenerator {
public:
bool generate(const perfetto::protos::TransactionTraceFile&, std::uint32_t traceFlags,
- std::optional<std::reference_wrapper<std::ostream>> outStream = std::nullopt,
- bool onlyLastEntry = false);
+ LayerTracing& layerTracing, bool onlyLastEntry = false);
};
} // namespace android
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index a8ac36a..65e8479 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -52,6 +52,8 @@
(argc == 3) ? argv[2] : "/data/misc/wmtrace/layers_trace.winscope";
auto outStream = std::ofstream{outputLayersTracePath, std::ios::binary | std::ios::app};
+ auto layerTracing = LayerTracing{outStream};
+
const bool generateLastEntryOnly =
argc >= 4 && std::string_view(argv[3]) == "--last-entry-only";
@@ -60,7 +62,7 @@
ALOGD("Generating %s...", outputLayersTracePath);
std::cout << "Generating " << outputLayersTracePath << "\n";
- if (!LayerTraceGenerator().generate(transactionTraceFile, traceFlags, outStream,
+ if (!LayerTraceGenerator().generate(transactionTraceFile, traceFlags, layerTracing,
generateLastEntryOnly)) {
std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
return -1;
diff --git a/services/surfaceflinger/Utils/FlagUtils.h b/services/surfaceflinger/Utils/FlagUtils.h
new file mode 100644
index 0000000..8435f04
--- /dev/null
+++ b/services/surfaceflinger/Utils/FlagUtils.h
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/properties.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
+#include <string>
+
+namespace android::flagutils {
+
+using namespace std::literals::string_literals;
+using namespace com::android::graphics::surfaceflinger;
+
+inline bool vrrConfigEnabled() {
+ static const bool enable_vrr_config =
+ base::GetBoolProperty("debug.sf.enable_vrr_config"s, false);
+ return flags::vrr_config() || enable_vrr_config;
+}
+} // namespace android::flagutils
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
index b6cd7b8..aeceadb 100644
--- a/services/surfaceflinger/tests/tracing/Android.bp
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -29,9 +29,6 @@
"skia_renderengine_deps",
],
test_suites: ["device-tests"],
- sanitize: {
- address: true,
- },
srcs: [
":libsurfaceflinger_sources",
":libsurfaceflinger_mock_sources",
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 7a07634..2fcb9e0 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -23,6 +23,7 @@
#include <unordered_map>
#include <LayerProtoHelper.h>
+#include <Tracing/LayerTracing.h>
#include <Tracing/TransactionProtoParser.h>
#include <Tracing/tools/LayerTraceGenerator.h>
#include <layerproto/LayerProtoHeader.h>
@@ -62,7 +63,8 @@
{
auto traceFlags = LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS;
std::ofstream outStream{actualLayersTracePath, std::ios::binary | std::ios::app};
- EXPECT_TRUE(LayerTraceGenerator().generate(mTransactionTrace, traceFlags, outStream,
+ auto layerTracing = LayerTracing{outStream};
+ EXPECT_TRUE(LayerTraceGenerator().generate(mTransactionTrace, traceFlags, layerTracing,
/*onlyLastEntry=*/true))
<< "Failed to generate layers trace from " << transactionTracePath;
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 8deff85..f4516c7 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -87,6 +87,7 @@
"FramebufferSurfaceTest.cpp",
"FrameRateOverrideMappingsTest.cpp",
"FrameRateSelectionPriorityTest.cpp",
+ "FrameRateSelectionStrategyTest.cpp",
"FrameTimelineTest.cpp",
"GameModeTest.cpp",
"HWComposerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
new file mode 100644
index 0000000..ffe6f24
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "Layer.h"
+#include "LayerTestUtils.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+
+namespace android {
+
+using testing::DoAll;
+using testing::Mock;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using scheduler::LayerHistory;
+
+using FrameRate = Layer::FrameRate;
+using FrameRateCompatibility = Layer::FrameRateCompatibility;
+using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
+
+/**
+ * This class tests the behaviour of Layer::setFrameRateSelectionStrategy.
+ */
+class FrameRateSelectionStrategyTest : public BaseLayerTest {
+protected:
+ const FrameRate FRAME_RATE_VOTE1 = FrameRate(11_Hz, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE2 = FrameRate(22_Hz, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE3 = FrameRate(33_Hz, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
+
+ FrameRateSelectionStrategyTest();
+
+ void addChild(sp<Layer> layer, sp<Layer> child);
+ void removeChild(sp<Layer> layer, sp<Layer> child);
+ void commitTransaction();
+
+ std::vector<sp<Layer>> mLayers;
+};
+
+FrameRateSelectionStrategyTest::FrameRateSelectionStrategyTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+}
+
+void FrameRateSelectionStrategyTest::addChild(sp<Layer> layer, sp<Layer> child) {
+ layer->addChild(child);
+}
+
+void FrameRateSelectionStrategyTest::removeChild(sp<Layer> layer, sp<Layer> child) {
+ layer->removeChild(child);
+}
+
+void FrameRateSelectionStrategyTest::commitTransaction() {
+ for (auto layer : mLayers) {
+ layer->commitTransaction();
+ }
+}
+
+namespace {
+
+INSTANTIATE_TEST_SUITE_P(PerLayerType, FrameRateSelectionStrategyTest,
+ testing::Values(std::make_shared<BufferStateLayerFactory>(),
+ std::make_shared<EffectLayerFactory>()),
+ PrintToStringParamName);
+
+TEST_P(FrameRateSelectionStrategyTest, SetAndGet) {
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+ const auto& layerFactory = GetParam();
+ auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ layer->setFrameRate(FRAME_RATE_VOTE1.vote);
+ layer->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ layer->getDrawingState().frameRateSelectionStrategy);
+}
+
+TEST_P(FrameRateSelectionStrategyTest, SetChildAndGetParent) {
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+ const auto& layerFactory = GetParam();
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1.vote);
+ child2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ parent->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ child1->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ child2->getDrawingState().frameRateSelectionStrategy);
+}
+
+TEST_P(FrameRateSelectionStrategyTest, SetParentAndGet) {
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+ const auto& layerFactory = GetParam();
+ auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ addChild(layer1, layer2);
+ addChild(layer2, layer3);
+
+ layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
+ layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
+ layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
+ layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ layer3->setFrameRate(FRAME_RATE_VOTE3.vote);
+ commitTransaction();
+
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ layer1->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ layer2->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ layer3->getDrawingState().frameRateSelectionStrategy);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index ff644ba..d7ac038 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -346,6 +346,18 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what =
+ layer_state_t::eFrameRateSelectionStrategyChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRateSelectionStrategy = strategy;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setRoundedCorners(uint32_t id, float radius) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -372,6 +384,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eBufferCropChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.bufferCrop = bufferCrop;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setDataspace(uint32_t id, ui::Dataspace dataspace) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index b7996ce..7e3e61f 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -494,14 +494,14 @@
time += HI_FPS_PERIOD;
}
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer became inactive
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
@@ -1124,8 +1124,8 @@
LayerHistory::Summary summary;
- // layer1 is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ // layer1 is updating small dirty.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
auto props = layer1->getLayerProps();
props.isSmallDirty = true;
history().record(layer1->getSequence(), props, 0 /*presentTime*/, time,
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 1227b99..1a9233d 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -743,6 +743,50 @@
EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
}
+TEST_F(LayerSnapshotTest, frameRateSelectionStrategy) {
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12 (frame rate set to 244.f with strategy OverrideChildren)
+ // │ │ ├── 121
+ // │ │ └── 122 (frame rate set to 123.f but should be overridden by layer 12)
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+ setFrameRate(12, 244.f, 0, 0);
+ setFrameRate(122, 123.f, 0, 0);
+ setFrameRateSelectionStrategy(12, 1 /* OverrideChildren */);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent 1 gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+}
+
TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) {
setRoundedCorners(1, 42.f);
setRoundedCorners(2, 42.f);
@@ -802,4 +846,33 @@
aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR);
}
+TEST_F(LayerSnapshotTest, setBufferCrop) {
+ // validate no buffer but has crop
+ Rect crop = Rect(0, 0, 50, 50);
+ setBufferCrop(1, crop);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->geomContentCrop, crop);
+
+ setBuffer(1,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(100U /*width*/,
+ 100U /*height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ // validate a buffer crop within the buffer bounds
+ setBufferCrop(1, crop);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->geomContentCrop, crop);
+
+ // validate a buffer crop outside the buffer bounds
+ crop = Rect(0, 0, 150, 150);
+ setBufferCrop(1, crop);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100));
+
+ // validate no buffer crop
+ setBufferCrop(1, Rect());
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100));
+}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 264b172..0b67137 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -1410,11 +1410,6 @@
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_30_60_90_120) {
auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId60);
- std::vector<LayerRequirement> layers = {{.vote = LayerVoteType::ExplicitDefault, .weight = 1.f},
- {.vote = LayerVoteType::ExplicitCategory,
- .weight = 1.f}};
- auto& lr = layers[0];
-
struct Case {
// Params
Fps desiredFrameRate = 0_Hz;
@@ -1431,7 +1426,7 @@
{0_Hz, FrameRateCategory::High, 90_Hz},
{0_Hz, FrameRateCategory::Normal, 60_Hz},
{0_Hz, FrameRateCategory::Low, 30_Hz},
- {0_Hz, FrameRateCategory::NoPreference, 60_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 30_Hz},
// Cases that have both desired frame rate and frame rate category requirements.
{24_Hz, FrameRateCategory::High, 120_Hz},
@@ -1444,34 +1439,41 @@
};
for (auto testCase : testCases) {
+ std::vector<LayerRequirement> layers;
ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
to_string(testCase.desiredFrameRate).c_str(),
ftl::enum_string(testCase.frameRateCategory).c_str());
- lr.desiredRefreshRate = testCase.desiredFrameRate;
-
- std::stringstream ss;
- ss << to_string(testCase.desiredFrameRate)
- << ", category=" << ftl::enum_string(testCase.frameRateCategory);
- lr.name = ss.str();
+ if (testCase.desiredFrameRate.isValid()) {
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate) << "ExplicitDefault";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = testCase.desiredFrameRate,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
if (testCase.frameRateCategory != FrameRateCategory::Default) {
- layers[1].frameRateCategory = testCase.frameRateCategory;
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .weight = 1.f};
+ layers.push_back(layer);
}
EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getPeakFps())
- << "did not get expected frame rate for " << lr.name;
+ << "Did not get expected frame rate for frameRate="
+ << to_string(testCase.desiredFrameRate)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
}
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
- std::vector<LayerRequirement> layers = {{.vote = LayerVoteType::ExplicitDefault, .weight = 1.f},
- {.vote = LayerVoteType::ExplicitCategory,
- .weight = 1.f}};
- auto& lr = layers[0];
-
struct Case {
// Params
Fps desiredFrameRate = 0_Hz;
@@ -1501,23 +1503,35 @@
};
for (auto testCase : testCases) {
+ std::vector<LayerRequirement> layers;
ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
to_string(testCase.desiredFrameRate).c_str(),
ftl::enum_string(testCase.frameRateCategory).c_str());
- lr.desiredRefreshRate = testCase.desiredFrameRate;
-
- std::stringstream ss;
- ss << to_string(testCase.desiredFrameRate)
- << ", category=" << ftl::enum_string(testCase.frameRateCategory);
- lr.name = ss.str();
+ if (testCase.desiredFrameRate.isValid()) {
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate) << "ExplicitDefault";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = testCase.desiredFrameRate,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
if (testCase.frameRateCategory != FrameRateCategory::Default) {
- layers[1].frameRateCategory = testCase.frameRateCategory;
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .weight = 1.f};
+ layers.push_back(layer);
}
EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getPeakFps())
- << "did not get expected frame rate for " << lr.name;
+ << "Did not get expected frame rate for frameRate="
+ << to_string(testCase.desiredFrameRate)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
}
}
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 608fa76..9899d42 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -19,6 +19,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <gui/FrameRateUtils.h>
#include <gui/LayerMetadata.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -325,48 +326,6 @@
std::make_shared<EffectLayerFactory>()),
PrintToStringParamName);
-TEST_F(SetFrameRateTest, 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.
- EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, -1, ""));
- EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, 2, ""));
-}
-
TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
const auto& layerFactory = GetParam();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 24eb318..3a5fdf9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -476,6 +476,37 @@
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) {
+ EXPECT_TRUE(mDisplay->isPoweredOn());
+ EXPECT_THAT(mDisplay, ModeSettledTo(kModeId60));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+
+ // Power off the display before the mode has been set.
+ mDisplay->setPowerMode(hal::PowerMode::OFF);
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // Powering off should not abort the mode set.
+ EXPECT_FALSE(mDisplay->isPoweredOn());
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(mDisplay, ModeSettledTo(kModeId90));
+}
+
+TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -516,6 +547,7 @@
mFlinger.commit();
+ // Powering off the inactive display should abort the mode set.
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
@@ -523,6 +555,28 @@
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+
+ innerDisplay->setPowerMode(hal::PowerMode::OFF);
+ outerDisplay->setPowerMode(hal::PowerMode::ON);
+
+ // Only the outer display is powered on.
+ mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kOuterDisplayHwcId,
+ hal::HWConfigId(kModeId60.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // The mode set should resume once the display becomes active.
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
}
} // namespace
diff --git a/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h b/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
index 8e28a75..11723c7 100644
--- a/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
+++ b/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/properties.h>
#include <gtest/gtest.h>
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
@@ -21,7 +22,8 @@
#include <future>
namespace android {
-using Transaction = SurfaceComposerClient::Transaction;
+
+using base::HwTimeoutMultiplier;
using gui::DisplayInfo;
using gui::WindowInfo;
@@ -36,7 +38,8 @@
auto listener = sp<WindowInfosListener>::make(std::move(predicate), promise);
mClient->addWindowInfosListener(listener);
auto future = promise.get_future();
- bool satisfied = future.wait_for(std::chrono::seconds{1}) == std::future_status::ready;
+ bool satisfied = future.wait_for(std::chrono::seconds{5 * HwTimeoutMultiplier()}) ==
+ std::future_status::ready;
mClient->removeWindowInfosListener(listener);
return satisfied;
}
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index b544245..de4271d 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -7,8 +7,19 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_defaults {
+ name: "libvkjson_deps",
+ shared_libs: [
+ "libjsoncpp",
+ "libvulkan",
+ ],
+}
+
+cc_library_static {
name: "libvkjson",
+ defaults: [
+ "libvkjson_deps",
+ ],
srcs: [
"vkjson.cc",
"vkjson_instance.cc",
@@ -24,10 +35,6 @@
export_include_dirs: [
".",
],
- shared_libs: [
- "libjsoncpp",
- "libvulkan",
- ],
export_shared_lib_headers: [
"libvulkan",
],