Merge "Add GTE compatibility enum to ANativeWindow." into main
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index b8e5ce1..17ae0aa 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -67,6 +67,8 @@
MOCK_METHOD2(unregisterForNotifications, status_t(const String16&,
const sp<LocalRegistrationCallback>&));
MOCK_METHOD0(getServiceDebugInfo, std::vector<ServiceDebugInfo>());
+ MOCK_METHOD1(enableAddServiceCache, void(bool));
+
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
diff --git a/include/android/display_luts.h b/include/android/display_luts.h
index 08dfb12..eae2bfd 100644
--- a/include/android/display_luts.h
+++ b/include/android/display_luts.h
@@ -43,6 +43,7 @@
enum ADisplayLuts_SamplingKey : int32_t {
ADISPLAYLUTS_SAMPLINGKEY_RGB = 0,
ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB = 1,
+ ADISPLAYLUTS_SAMPLINGKEY_CIE_Y = 2,
};
typedef enum ADisplayLuts_SamplingKey ADisplayLuts_SamplingKey;
@@ -74,7 +75,8 @@
* @return a new \a ADisplayLutsEntry instance.
*/
ADisplayLutsEntry* _Nonnull ADisplayLutsEntry_createEntry(float* _Nonnull buffer,
- int32_t length, int32_t dimension, int32_t key) __INTRODUCED_IN(36);
+ int32_t length, ADisplayLuts_Dimension dimension, ADisplayLuts_SamplingKey key)
+ __INTRODUCED_IN(36);
/**
* Destroy the \a ADisplayLutsEntry instance.
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 7f9d2ed..e5d46b5 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -140,45 +140,47 @@
* Available since API level 30.
*
* @param manager The manager instance to use to query the thermal status.
- * Acquired via {@link AThermal_acquireManager}.
+ * Acquired via {@link AThermal_acquireManager}.
*
* @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
*/
AThermalStatus
-AThermal_getCurrentThermalStatus(AThermalManager* _Nonnull manager) __INTRODUCED_IN(30);
+AThermal_getCurrentThermalStatus(AThermalManager *_Nonnull manager) __INTRODUCED_IN(30);
/**
- * Register the thermal status listener for thermal status change.
+ * Register a thermal status listener for thermal status change.
*
* Available since API level 30.
*
* @param manager The manager instance to use to register.
- * Acquired via {@link AThermal_acquireManager}.
- * @param callback The callback function to be called when thermal status updated.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function to be called on system binder thread pool when thermal
+ * status updated.
* @param data The data pointer to be passed when callback is called.
*
* @return 0 on success
* EINVAL if the listener and data pointer were previously added and not removed.
- * EPERM if the required permission is not held.
- * EPIPE if communication with the system service has failed.
+ * EPIPE if communication with the system service has failed, the listener will not get
+ * removed and this call should be retried
*/
-int AThermal_registerThermalStatusListener(AThermalManager* _Nonnull manager,
+int AThermal_registerThermalStatusListener(AThermalManager *_Nonnull manager,
AThermal_StatusCallback _Nullable callback,
void* _Nullable data) __INTRODUCED_IN(30);
/**
- * Unregister the thermal status listener previously resgistered.
+ * Unregister a thermal status listener previously registered.
+ *
+ * No subsequent invocations of the callback will occur after this function returns successfully.
*
* Available since API level 30.
*
* @param manager The manager instance to use to unregister.
- * Acquired via {@link AThermal_acquireManager}.
- * @param callback The callback function to be called when thermal status updated.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function that was previously registered.
* @param data The data pointer to be passed when callback is called.
*
* @return 0 on success
* EINVAL if the listener and data pointer were not previously added.
- * EPERM if the required permission is not held.
* EPIPE if communication with the system service has failed.
*/
int AThermal_unregisterThermalStatusListener(AThermalManager* _Nonnull manager,
@@ -254,7 +256,7 @@
* The headroom threshold is used to interpret the possible thermal throttling status based on
* the headroom prediction. For example, if the headroom threshold for
* {@link ATHERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75
- * (or {@code AThermal_getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system
+ * (or `AThermal_getThermalHeadroom(10)=0.75}`, one can expect that in 10 seconds the system
* could be in lightly throttled state if the workload remains the same. The app can consider
* taking actions according to the nearest throttling status the difference between the headroom and
* the threshold.
@@ -262,24 +264,30 @@
* For new devices it's guaranteed to have a single sensor, but for older devices with multiple
* sensors reporting different threshold values, the minimum threshold is taken to be conservative
* on predictions. Thus, when reading real-time headroom, it's not guaranteed that a real-time value
- * of 0.75 (or {@code AThermal_getThermalHeadroom(0)}=0.75) exceeding the threshold of 0.7 above
+ * of 0.75 (or `AThermal_getThermalHeadroom(0)=0.75`) exceeding the threshold of 0.7 above
* will always come with lightly throttled state
- * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT}) but it can be lower
- * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE}).
+ * (or `AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT`) but it can be lower
+ * (or `AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE`).
* While it's always guaranteed that the device won't be throttled heavier than the unmet
* threshold's state, so a real-time headroom of 0.75 will never come with
* {@link #ATHERMAL_STATUS_MODERATE} but always lower, and 0.65 will never come with
* {@link ATHERMAL_STATUS_LIGHT} but {@link #ATHERMAL_STATUS_NONE}.
* <p>
- * The returned list of thresholds is cached on first successful query and owned by the thermal
- * manager, which will not change between calls to this function. The caller should only need to
- * free the manager with {@link AThermal_releaseManager}.
+ * Starting in Android 16, this polling API may return different results when called depending on
+ * the device. The new headroom listener API {@link #AThermal_HeadroomCallback} can be used to
+ * detect headroom thresholds changes.
+ * <p>
+ * Before API level 36 the returned list of thresholds is cached on first successful query and owned
+ * by the thermal manager, which will not change between calls to this function. The caller should
+ * only need to free the manager with {@link AThermal_releaseManager}.
+ * <p>
*
* @param manager The manager instance to use.
* Acquired via {@link AThermal_acquireManager}.
* @param outThresholds non-null output pointer to null AThermalHeadroomThreshold pointer, which
- * will be set to the cached array of thresholds if thermal thresholds are supported
- * by the system or device, otherwise nullptr or unmodified.
+ * will be set to a new array of thresholds if thermal thresholds are supported
+ * by the system or device, otherwise nullptr or unmodified. The client should
+ * clean up the thresholds by array-deleting the threshold pointer.
* @param size non-null output pointer whose value will be set to the size of the threshold array
* or 0 if it's not supported.
* @return 0 on success
@@ -292,6 +300,71 @@
* _Nullable outThresholds,
size_t* _Nonnull size) __INTRODUCED_IN(35);
+/**
+ * Prototype of the function that is called when thermal headroom or thresholds changes.
+ * It's passed the updated thermal headroom and thresholds as parameters, as well as the
+ * pointer provided by the client that registered a callback.
+ *
+ * @param data The data pointer to be passed when callback is called.
+ * @param headroom The current non-negative normalized headroom value, also see
+ * {@link AThermal_getThermalHeadroom}.
+ * @param forecastHeadroom The forecasted non-negative normalized headroom value, also see
+ * {@link AThermal_getThermalHeadroom}.
+ * @param forecastSeconds The seconds used for the forecast by the system.
+ * @param thresholds The current headroom thresholds. The thresholds pointer will be a constant
+ * shared across all callbacks registered from the same process, and it will be
+ * destroyed after all the callbacks are finished. If the client intents to
+ * persist the values, it should make a copy of it during the callback.
+ * @param thresholdsCount The count of thresholds.
+ */
+typedef void (*AThermal_HeadroomCallback)(void *_Nullable data,
+ float headroom,
+ float forecastHeadroom,
+ int forecastSeconds,
+ const AThermalHeadroomThreshold* _Nullable thresholds,
+ size_t thresholdsCount);
+
+/**
+ * Register a thermal headroom listener for thermal headroom or thresholds change.
+ *
+ * Available since API level 36.
+ *
+ * @param manager The manager instance to use to register.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function to be called on system binder thread pool when thermal
+ * headroom or thresholds update.
+ * @param data The data pointer to be passed when callback is called.
+ *
+ * @return 0 on success
+ * EINVAL if the listener and data pointer were previously added and not removed.
+ * EPIPE if communication with the system service has failed.
+ */
+int AThermal_registerThermalHeadroomListener(AThermalManager* _Nonnull manager,
+ AThermal_HeadroomCallback _Nullable callback,
+ void* _Nullable data) __INTRODUCED_IN(36);
+
+/**
+ * Unregister a thermal headroom listener previously registered.
+ *
+ * No subsequent invocations of the callback will occur after this function returns successfully.
+ *
+ * Available since API level 36.
+ *
+ * @param manager The manager instance to use to unregister.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param callback The callback function that was previously registered.
+ * @param data The data pointer that was previously registered.
+ *
+ * @return 0 on success
+ * EINVAL if the listener and data pointer were not previously added.
+ * EPIPE if communication with the system service has failed, the listener will not get
+ * removed and this call should be retried
+ */
+
+int AThermal_unregisterThermalHeadroomListener(AThermalManager* _Nonnull manager,
+ AThermal_HeadroomCallback _Nullable callback,
+ void* _Nullable data) __INTRODUCED_IN(36);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/private/display_luts_private.h b/include/private/display_luts_private.h
index 240e1f9..c347a0c 100644
--- a/include/private/display_luts_private.h
+++ b/include/private/display_luts_private.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/display_luts.h>
#include <stdint.h>
#include <vector>
#include <utils/RefBase.h>
@@ -29,9 +30,9 @@
};
struct ADisplayLutsEntry_properties {
- int32_t dimension;
+ ADisplayLuts_Dimension dimension;
int32_t size;
- int32_t samplingKey;
+ ADisplayLuts_SamplingKey samplingKey;
};
struct ADisplayLutsEntry: public RefBase {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 0a61178..e10e4dd 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -477,9 +477,34 @@
},
}
+soong_config_module_type {
+ name: "libbinder_addservice_cache_config",
+ module_type: "cc_defaults",
+ config_namespace: "libbinder",
+ bool_variables: ["release_libbinder_addservice_cache"],
+ properties: [
+ "cflags",
+ ],
+}
+
+libbinder_addservice_cache_config {
+ name: "libbinder_addservice_cache_flag",
+ soong_config_variables: {
+ release_libbinder_addservice_cache: {
+ cflags: ["-DLIBBINDER_ADDSERVICE_CACHE"],
+ conditions_default: {
+ cflags: ["-DNO_LIBBINDER_ADDSERVICE_CACHE"],
+ },
+ },
+ },
+}
+
cc_defaults {
name: "libbinder_kernel_defaults",
- defaults: ["libbinder_client_cache_flag"],
+ defaults: [
+ "libbinder_client_cache_flag",
+ "libbinder_addservice_cache_flag",
+ ],
srcs: [
"BufferedTextOutput.cpp",
"BackendUnifiedServiceManager.cpp",
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index d32eecd..123dfd2 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -30,6 +30,12 @@
constexpr bool kUseCache = false;
#endif
+#ifdef LIBBINDER_ADDSERVICE_CACHE
+constexpr bool kUseCacheInAddService = true;
+#else
+constexpr bool kUseCacheInAddService = false;
+#endif
+
using AidlServiceManager = android::os::IServiceManager;
using android::os::IAccessor;
using binder::Status;
@@ -125,31 +131,36 @@
if (!kUseCache) {
return Status::ok();
}
+
+ if (service.getTag() == os::Service::Tag::binder) {
+ return updateCache(serviceName, service.get<os::Service::Tag::binder>());
+ }
+ return Status::ok();
+}
+
+Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
+ const sp<IBinder>& binder) {
std::string traceStr;
if (atrace_is_tag_enabled(ATRACE_TAG_AIDL)) {
traceStr = "BinderCacheWithInvalidation::updateCache : " + serviceName;
}
binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL, traceStr.c_str());
-
- if (service.getTag() == os::Service::Tag::binder) {
- sp<IBinder> binder = service.get<os::Service::Tag::binder>();
- if (!binder) {
- binder::ScopedTrace
- aidlTrace(ATRACE_TAG_AIDL,
- "BinderCacheWithInvalidation::updateCache failed: binder_null");
- } else if (!binder->isBinderAlive()) {
- binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
- "BinderCacheWithInvalidation::updateCache failed: "
- "isBinderAlive_false");
- } else if (mCacheForGetService->isClientSideCachingEnabled(serviceName)) {
- binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
- "BinderCacheWithInvalidation::updateCache successful");
- return mCacheForGetService->setItem(serviceName, binder);
- } else {
- binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
- "BinderCacheWithInvalidation::updateCache failed: "
- "caching_not_enabled");
- }
+ if (!binder) {
+ binder::ScopedTrace
+ aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: binder_null");
+ } else if (!binder->isBinderAlive()) {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: "
+ "isBinderAlive_false");
+ } else if (mCacheForGetService->isClientSideCachingEnabled(serviceName)) {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache successful");
+ return mCacheForGetService->setItem(serviceName, binder);
+ } else {
+ binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
+ "BinderCacheWithInvalidation::updateCache failed: "
+ "caching_not_enabled");
}
return Status::ok();
}
@@ -277,7 +288,12 @@
Status BackendUnifiedServiceManager::addService(const ::std::string& name,
const sp<IBinder>& service, bool allowIsolated,
int32_t dumpPriority) {
- return mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
+ Status status = mTheRealServiceManager->addService(name, service, allowIsolated, dumpPriority);
+ // mEnableAddServiceCache is true by default.
+ if (kUseCacheInAddService && mEnableAddServiceCache && status.isOk()) {
+ return updateCache(name, service);
+ }
+ return status;
}
Status BackendUnifiedServiceManager::listServices(int32_t dumpPriority,
::std::vector<::std::string>* _aidl_return) {
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index abc0eda..b46bf19 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -105,8 +105,7 @@
binder::ScopedTrace aidlTrace(ATRACE_TAG_AIDL,
"BinderCacheWithInvalidation::setItem Successfully Cached");
std::lock_guard<std::mutex> lock(mCacheMutex);
- Entry entry = {.service = item, .deathRecipient = deathRecipient};
- mCache[key] = entry;
+ mCache[key] = {.service = item, .deathRecipient = deathRecipient};
return binder::Status::ok();
}
@@ -147,17 +146,20 @@
const sp<IBinder>& service) override;
binder::Status getServiceDebugInfo(::std::vector<os::ServiceDebugInfo>* _aidl_return) override;
+ void enableAddServiceCache(bool value) { mEnableAddServiceCache = value; }
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
return mTheRealServiceManager->getInterfaceDescriptor();
}
private:
+ bool mEnableAddServiceCache = true;
std::shared_ptr<BinderCacheWithInvalidation> mCacheForGetService;
sp<os::IServiceManager> mTheRealServiceManager;
binder::Status toBinderService(const ::std::string& name, const os::Service& in,
os::Service* _out);
binder::Status updateCache(const std::string& serviceName, const os::Service& service);
+ binder::Status updateCache(const std::string& serviceName, const sp<IBinder>& binder);
bool returnIfCached(const std::string& serviceName, os::Service* _out);
};
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 39d8c24..61866d7 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -127,6 +127,8 @@
}
IBinder* onAsBinder() override { return IInterface::asBinder(mUnifiedServiceManager).get(); }
+ void enableAddServiceCache(bool value) { mUnifiedServiceManager->enableAddServiceCache(value); }
+
protected:
sp<BackendUnifiedServiceManager> mUnifiedServiceManager;
// AidlRegistrationCallback -> services that its been registered for
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 81f7cdb..ca26a57 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -150,6 +150,12 @@
int pid;
};
virtual std::vector<ServiceDebugInfo> getServiceDebugInfo() = 0;
+
+ /**
+ * Directly enable or disable caching binder during addService calls.
+ * Only used for testing.
+ */
+ virtual void enableAddServiceCache(bool value) = 0;
};
LIBBINDER_EXPORTED sp<IServiceManager> defaultServiceManager();
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 28a3f65..642fbf3 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -70,7 +70,10 @@
static_libs: [
"libfakeservicemanager",
],
- defaults: ["libbinder_client_cache_flag"],
+ defaults: [
+ "libbinder_client_cache_flag",
+ "libbinder_addservice_cache_flag",
+ ],
test_suites: ["general-tests"],
require_root: true,
}
diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp
index c5ad793..3195c55 100644
--- a/libs/binder/tests/binderCacheUnitTest.cpp
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -34,6 +34,12 @@
constexpr bool kUseLibbinderCache = false;
#endif
+#ifdef LIBBINDER_ADDSERVICE_CACHE
+constexpr bool kUseCacheInAddService = true;
+#else
+constexpr bool kUseCacheInAddService = false;
+#endif
+
// A service name which is in the static list of cachable services
const String16 kCachedServiceName = String16("isub");
@@ -74,14 +80,58 @@
innerSm.addService(String16(name.c_str()), service, allowIsolated, dumpPriority));
}
+ void clearServices() { innerSm.clear(); }
+
FakeServiceManager innerSm;
};
+class LibbinderCacheAddServiceTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ fakeServiceManager = sp<MockAidlServiceManager>::make();
+ mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(fakeServiceManager);
+ mServiceManager->enableAddServiceCache(true);
+ }
+
+ void TearDown() override {}
+
+public:
+ void cacheAddServiceAndConfirmCacheHit(const sp<IBinder>& binder1) {
+ // Add a service. This also caches it.
+ EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder1));
+ // remove services from fakeservicemanager
+ fakeServiceManager->clearServices();
+
+ sp<IBinder> result = mServiceManager->checkService(kCachedServiceName);
+ if (kUseCacheInAddService && kUseLibbinderCache) {
+ // If cache is enabled, we should get the binder.
+ EXPECT_EQ(binder1, result);
+ } else {
+ // If cache is disabled, then we should get the null binder
+ EXPECT_EQ(nullptr, result);
+ }
+ }
+ sp<MockAidlServiceManager> fakeServiceManager;
+ sp<android::IServiceManager> mServiceManager;
+};
+
+TEST_F(LibbinderCacheAddServiceTest, AddLocalServiceAndConfirmCacheHit) {
+ sp<IBinder> binder1 = sp<BBinder>::make();
+ cacheAddServiceAndConfirmCacheHit(binder1);
+}
+
+TEST_F(LibbinderCacheAddServiceTest, AddRemoteServiceAndConfirmCacheHit) {
+ sp<IBinder> binder1 = defaultServiceManager()->checkService(kServerName);
+ ASSERT_NE(binder1, nullptr);
+ cacheAddServiceAndConfirmCacheHit(binder1);
+}
+
class LibbinderCacheTest : public ::testing::Test {
protected:
void SetUp() override {
- sp<MockAidlServiceManager> sm = sp<MockAidlServiceManager>::make();
- mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(sm);
+ fakeServiceManager = sp<MockAidlServiceManager>::make();
+ mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(fakeServiceManager);
+ mServiceManager->enableAddServiceCache(false);
}
void TearDown() override {}
@@ -108,6 +158,7 @@
}
}
+ sp<MockAidlServiceManager> fakeServiceManager;
sp<android::IServiceManager> mServiceManager;
};
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 970852c..9e92f95 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -223,6 +223,8 @@
ASSERT_GT(m_serverpid, 0);
sp<IServiceManager> sm = defaultServiceManager();
+ // disable caching during addService.
+ sm->enableAddServiceCache(false);
//printf("%s: pid %d, get service\n", __func__, m_pid);
LIBBINDER_IGNORE("-Wdeprecated-declarations")
m_server = sm->getService(binderLibTestServiceName);
@@ -298,6 +300,9 @@
virtual void SetUp() {
m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
IPCThreadState::self()->restoreCallingWorkSource(0);
+ sp<IServiceManager> sm = defaultServiceManager();
+ // disable caching during addService.
+ sm->enableAddServiceCache(false);
}
virtual void TearDown() {
}
@@ -1765,6 +1770,7 @@
TEST(ServiceNotifications, Unregister) {
auto sm = defaultServiceManager();
+ sm->enableAddServiceCache(false);
using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback;
class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback {
void onServiceRegistration(const String16 &, const sp<IBinder> &) override {}
@@ -2529,6 +2535,8 @@
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
+ sm->enableAddServiceCache(false);
+
BinderLibTestService* testServicePtr;
{
sp<BinderLibTestService> testService = new BinderLibTestService(index);
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index f62241d..f2b2aa7 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -65,6 +65,7 @@
std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override;
+ void enableAddServiceCache(bool /*value*/) override {}
// Clear all of the registered services
void clear();
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 0c8f3fa..ba50bf8 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -369,6 +369,10 @@
this, to_string(displayId).c_str(), connectedLevel, maxLevel);
}
+void Choreographer::dispatchModeRejected(PhysicalDisplayId, int32_t) {
+ LOG_ALWAYS_FATAL("dispatchModeRejected was called but was never registered");
+}
+
void Choreographer::handleMessage(const Message& message) {
switch (message.what) {
case MSG_SCHEDULE_CALLBACKS:
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index c46f9c5..68f10f4 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -211,6 +211,9 @@
ev.hdcpLevelsChange.connectedLevel,
ev.hdcpLevelsChange.maxLevel);
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+ dispatchModeRejected(ev.header.displayId, ev.modeRejection.modeId);
+ break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index 2e5aa4a..a93ba14 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -127,6 +127,7 @@
std::vector<FrameRateOverride> overrides) override;
void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) override;
+ void dispatchModeRejected(PhysicalDisplayId displayId, int32_t modeId) override;
void scheduleCallbacks();
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 82cd50c..b06ad07 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -68,6 +68,8 @@
virtual void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) = 0;
+ virtual void dispatchModeRejected(PhysicalDisplayId displayId, int32_t modeId) = 0;
+
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 40a6e79..ab6a6b7 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -62,6 +62,7 @@
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'),
+ DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'),
DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
@@ -96,6 +97,10 @@
nsecs_t vsyncPeriod __attribute__((aligned(8)));
};
+ struct ModeRejection {
+ int32_t modeId;
+ };
+
struct FrameRateOverride {
uid_t uid __attribute__((aligned(8)));
float frameRateHz __attribute__((aligned(8)));
@@ -117,6 +122,7 @@
ModeChange modeChange;
FrameRateOverride frameRateOverride;
HdcpLevelsChange hdcpLevelsChange;
+ ModeRejection modeRejection;
};
};
static_assert(sizeof(Event) == 224);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 0e84d68..dcda1ee 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -75,7 +75,7 @@
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else {
- ALOGE("Linked to input");
+ ALOGI("Linked to input");
}
return interface_cast<IInputFlinger>(input);
}
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
index db0b133..da47aae 100644
--- a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
@@ -96,13 +96,17 @@
void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, sk_sp<SkShader> input,
const float inverseScale, const float radius,
const float alpha) const {
- SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
- blurBuilder.child("child") = std::move(input);
- blurBuilder.uniform("in_inverseScale") = inverseScale;
- blurBuilder.uniform("in_blurOffset") = radius;
- blurBuilder.uniform("in_crossFade") = alpha;
SkPaint paint;
- paint.setShader(blurBuilder.makeShader(nullptr));
+ if (radius == 0) {
+ paint.setShader(std::move(input));
+ paint.setAlphaf(alpha);
+ } else {
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") = std::move(input);
+ blurBuilder.uniform("in_blurOffset") = radius;
+ blurBuilder.uniform("in_crossFade") = alpha;
+ paint.setShader(blurBuilder.makeShader(nullptr));
+ }
paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
drawSurface->getCanvas()->drawPaint(paint);
}
@@ -116,32 +120,35 @@
// Use a variable number of blur passes depending on the radius. The non-integer part of this
// calculation is used to mix the final pass into the second-last with an alpha blend.
- constexpr int kMaxSurfaces = 4;
- const float filterDepth =
- std::min(kMaxSurfaces - 1.0f, 1.0f + std::max(0.0f, log2f(radius * kInputScale)));
+ constexpr int kMaxSurfaces = 3;
+ const float filterDepth = std::min(kMaxSurfaces - 1.0f, radius * kInputScale / 2.5f);
const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth)));
- // Render into surfaces downscaled by 1x, 1x, 2x, and 4x from the initial downscale.
+ // Render into surfaces downscaled by 1x, 2x, and 4x from the initial downscale.
sk_sp<SkSurface> surfaces[kMaxSurfaces] =
{filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
- filterPasses >= 1 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
- filterPasses >= 2 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
- filterPasses >= 3 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};
+ filterPasses >= 1 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
+ filterPasses >= 2 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};
- // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 600.
- static const float kWeights[7] = {1.0f, 2.0f, 3.5f, 1.0f, 2.0f, 2.0f, 2.0f};
+ // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 250.
+ static const float kWeights[5] = {
+ 1.0f, // 1st downsampling pass
+ 1.0f, // 2nd downsampling pass
+ 1.0f, // 3rd downsampling pass
+ 0.0f, // 1st upscaling pass. Set to zero to upscale without blurring for performance.
+ 1.0f, // 2nd upscaling pass
+ };
// Kawase is an approximation of Gaussian, but behaves differently because it is made up of many
// simpler blurs. A transformation is required to approximate the same effect as Gaussian.
- float sumSquaredR = powf(kWeights[0] * powf(2.0f, 1), 2.0f);
+ float sumSquaredR = powf(kWeights[0], 2.0f);
for (int i = 0; i < filterPasses; i++) {
const float alpha = std::min(1.0f, filterDepth - i);
- sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[1 + i], 2.0f);
- sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[6 - i], 2.0f);
+ sumSquaredR += powf(powf(2.0f, i) * alpha * kWeights[1 + i] / kInputScale, 2.0f);
+ sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[4 - i] / kInputScale, 2.0f);
}
- // Solve for R = sqrt(sum(r_i^2)). Divide R by hypot(1,1) to find some (x,y) offsets.
- const float step = M_SQRT1_2 *
- sqrtf(max(0.0f, (powf(radius, 2.0f) - powf(kInverseInputScale, 2.0f)) / sumSquaredR));
+ // Solve for R = sqrt(sum(r_i^2)).
+ const float step = radius * sqrt(1.0f / sumSquaredR);
// Start by downscaling and doing the first blur pass.
{
@@ -162,7 +169,7 @@
}
// Finally blur+upscale back to our original size.
for (int i = filterPasses - 1; i >= 0; i--) {
- blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[6 - i] * step,
+ blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[4 - i] * step,
std::min(1.0f, filterDepth - i));
}
return surfaces[0]->makeImageSnapshot();
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index c4f8663..a58bc77 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -236,6 +236,7 @@
std::call_once(registration, [test]() {
struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
args.backends = test ? PERFETTO_BACKEND_IN_PROCESS : PERFETTO_BACKEND_SYSTEM;
+ args.shmem_size_hint_kb = 1024;
PerfettoProducerInit(args);
PerfettoTeInit();
PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES);
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 38e5974..8eb6bdd 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -205,20 +205,30 @@
}
NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
- std::scoped_lock _l(getLock());
+ NotifyMotionArgs newArgs(args);
+ PointerDisplayChange pointerDisplayChange;
+ { // acquire lock
+ std::scoped_lock _l(getLock());
+ if (isFromMouse(args)) {
+ newArgs = processMouseEventLocked(args);
+ pointerDisplayChange = calculatePointerDisplayChangeToNotify();
+ } else if (isFromTouchpad(args)) {
+ newArgs = processTouchpadEventLocked(args);
+ pointerDisplayChange = calculatePointerDisplayChangeToNotify();
+ } else if (isFromDrawingTablet(args)) {
+ processDrawingTabletEventLocked(args);
+ } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
+ processStylusHoverEventLocked(args);
+ } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
+ processTouchscreenAndStylusEventLocked(args);
+ }
+ } // release lock
- if (isFromMouse(args)) {
- return processMouseEventLocked(args);
- } else if (isFromTouchpad(args)) {
- return processTouchpadEventLocked(args);
- } else if (isFromDrawingTablet(args)) {
- processDrawingTabletEventLocked(args);
- } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
- processStylusHoverEventLocked(args);
- } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
- processTouchscreenAndStylusEventLocked(args);
+ if (pointerDisplayChange) {
+ // pointer display may have changed if mouse crossed display boundary
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
- return args;
+ return newArgs;
}
NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
@@ -245,7 +255,8 @@
// This is a relative mouse, so move the cursor by the specified amount.
processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
}
- if (canUnfadeOnDisplay(displayId)) {
+ // Note displayId may have changed if the cursor moved to a different display
+ if (canUnfadeOnDisplay(newArgs.displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
return newArgs;
@@ -272,7 +283,9 @@
newArgs.xCursorPosition = x;
newArgs.yCursorPosition = y;
}
- if (canUnfadeOnDisplay(displayId)) {
+
+ // Note displayId may have changed if the cursor moved to a different display
+ if (canUnfadeOnDisplay(newArgs.displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
return newArgs;
@@ -283,7 +296,14 @@
const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- pc.move(deltaX, deltaY);
+ FloatPoint unconsumedDelta = pc.move(deltaX, deltaY);
+ if (com::android::input::flags::connected_displays_cursor() &&
+ (std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) {
+ handleUnconsumedDeltaLocked(pc, unconsumedDelta);
+ // pointer may have moved to a different viewport
+ newArgs.displayId = pc.getDisplayId();
+ }
+
const auto [x, y] = pc.getPosition();
newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
@@ -291,6 +311,93 @@
newArgs.yCursorPosition = y;
}
+void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterface& pc,
+ const FloatPoint& unconsumedDelta) {
+ // Display topology is in rotated coordinate space and Pointer controller returns and expects
+ // values in the un-rotated coordinate space. So we need to transform delta and cursor position
+ // back to the rotated coordinate space to lookup adjacent display in the display topology.
+ const auto& sourceDisplayTransform = pc.getDisplayTransform();
+ const vec2 rotatedUnconsumedDelta =
+ transformWithoutTranslation(sourceDisplayTransform,
+ {unconsumedDelta.x, unconsumedDelta.y});
+ const FloatPoint cursorPosition = pc.getPosition();
+ const vec2 rotatedCursorPosition =
+ sourceDisplayTransform.transform(cursorPosition.x, cursorPosition.y);
+
+ // To find out the boundary that cursor is crossing we are checking delta in x and y direction
+ // respectively. This prioritizes x direction over y.
+ // In practise, majority of cases we only have non-zero values in either x or y coordinates,
+ // except sometimes near the corners.
+ // In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on
+ // the destination display for the same reason.
+ DisplayPosition sourceBoundary;
+ float cursorOffset = 0.0f;
+ if (rotatedUnconsumedDelta.x > 0) {
+ sourceBoundary = DisplayPosition::RIGHT;
+ cursorOffset = rotatedCursorPosition.y;
+ } else if (rotatedUnconsumedDelta.x < 0) {
+ sourceBoundary = DisplayPosition::LEFT;
+ cursorOffset = rotatedCursorPosition.y;
+ } else if (rotatedUnconsumedDelta.y > 0) {
+ sourceBoundary = DisplayPosition::BOTTOM;
+ cursorOffset = rotatedCursorPosition.x;
+ } else {
+ sourceBoundary = DisplayPosition::TOP;
+ cursorOffset = rotatedCursorPosition.x;
+ }
+
+ const ui::LogicalDisplayId sourceDisplayId = pc.getDisplayId();
+ std::optional<std::pair<const DisplayViewport*, float /*offset*/>> destination =
+ findDestinationDisplayLocked(sourceDisplayId, sourceBoundary, cursorOffset);
+ if (!destination.has_value()) {
+ // No matching adjacent display
+ return;
+ }
+
+ const DisplayViewport& destinationViewport = *destination->first;
+ const float destinationOffset = destination->second;
+ if (mMousePointersByDisplay.find(destinationViewport.displayId) !=
+ mMousePointersByDisplay.end()) {
+ LOG(FATAL) << "A cursor already exists on destination display"
+ << destinationViewport.displayId;
+ }
+ mDefaultMouseDisplayId = destinationViewport.displayId;
+ auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId);
+ pcNode.key() = destinationViewport.displayId;
+ mMousePointersByDisplay.insert(std::move(pcNode));
+
+ // Before updating the viewport and moving the cursor to appropriate location in the destination
+ // viewport, we need to temporarily hide the cursor. This will prevent it from appearing at the
+ // center of the display in any intermediate frames.
+ pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
+ pc.setDisplayViewport(destinationViewport);
+ vec2 destinationPosition =
+ calculateDestinationPosition(destinationViewport, cursorOffset - destinationOffset,
+ sourceBoundary);
+
+ // Transform position back to un-rotated coordinate space before sending it to controller
+ destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x,
+ destinationPosition.y);
+ pc.setPosition(destinationPosition.x, destinationPosition.y);
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+}
+
+vec2 PointerChoreographer::calculateDestinationPosition(const DisplayViewport& destinationViewport,
+ float pointerOffset,
+ DisplayPosition sourceBoundary) {
+ // destination is opposite of the source boundary
+ switch (sourceBoundary) {
+ case DisplayPosition::RIGHT:
+ return {0, pointerOffset}; // left edge
+ case DisplayPosition::TOP:
+ return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
+ case DisplayPosition::LEFT:
+ return {destinationViewport.logicalRight, pointerOffset}; // right edge
+ case DisplayPosition::BOTTOM:
+ return {pointerOffset, 0}; // top edge
+ }
+}
+
void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
if (args.displayId == ui::LogicalDisplayId::INVALID) {
return;
@@ -436,7 +543,8 @@
}
void PointerChoreographer::onControllerAddedOrRemovedLocked() {
- if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
+ if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows() &&
+ !com::android::input::flags::connected_displays_cursor()) {
return;
}
bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
@@ -502,6 +610,13 @@
mNextListener.notify(args);
}
+void PointerChoreographer::setDisplayTopology(
+ const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
+ displayTopology) {
+ std::scoped_lock _l(getLock());
+ mTopology = displayTopology;
+}
+
void PointerChoreographer::dump(std::string& dump) {
std::scoped_lock _l(getLock());
@@ -873,6 +988,97 @@
return ConstructorDelegate(std::move(ctor));
}
+void PointerChoreographer::populateFakeDisplayTopologyLocked(
+ const std::vector<gui::DisplayInfo>& displayInfos) {
+ if (!com::android::input::flags::connected_displays_cursor()) {
+ return;
+ }
+
+ if (displayInfos.size() == mTopology.size()) {
+ bool displaysChanged = false;
+ for (const auto& displayInfo : displayInfos) {
+ if (mTopology.find(displayInfo.displayId) == mTopology.end()) {
+ displaysChanged = true;
+ break;
+ }
+ }
+
+ if (!displaysChanged) {
+ return;
+ }
+ }
+
+ // create a fake topology assuming following order
+ // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ...
+ // This also adds a 100px offset on corresponding edge for better manual testing
+ // ┌────────┐
+ // │ next ├─────────┐
+ // ┌─└───────┐┤ next 2 │ ...
+ // │ default │└─────────┘
+ // └─────────┘
+ mTopology.clear();
+
+ // treat default display as base, in real topology it should be the primary-display
+ ui::LogicalDisplayId previousDisplay = ui::LogicalDisplayId::DEFAULT;
+ for (const auto& displayInfo : displayInfos) {
+ if (displayInfo.displayId == ui::LogicalDisplayId::DEFAULT) {
+ continue;
+ }
+ if (previousDisplay == ui::LogicalDisplayId::DEFAULT) {
+ mTopology[previousDisplay].push_back(
+ {displayInfo.displayId, DisplayPosition::TOP, 100});
+ mTopology[displayInfo.displayId].push_back(
+ {previousDisplay, DisplayPosition::BOTTOM, -100});
+ } else {
+ mTopology[previousDisplay].push_back(
+ {displayInfo.displayId, DisplayPosition::RIGHT, 100});
+ mTopology[displayInfo.displayId].push_back(
+ {previousDisplay, DisplayPosition::LEFT, -100});
+ }
+ previousDisplay = displayInfo.displayId;
+ }
+
+ // update default pointer display. In real topology it should be the primary-display
+ if (mTopology.find(mDefaultMouseDisplayId) == mTopology.end()) {
+ mDefaultMouseDisplayId = ui::LogicalDisplayId::DEFAULT;
+ }
+}
+
+std::optional<std::pair<const DisplayViewport*, float /*offset*/>>
+PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
+ const DisplayPosition sourceBoundary,
+ float cursorOffset) const {
+ const auto& sourceNode = mTopology.find(sourceDisplayId);
+ if (sourceNode == mTopology.end()) {
+ // Topology is likely out of sync with viewport info, wait for it to be updated
+ LOG(WARNING) << "Source display missing from topology " << sourceDisplayId;
+ return std::nullopt;
+ }
+ for (const AdjacentDisplay& adjacentDisplay : sourceNode->second) {
+ if (adjacentDisplay.position != sourceBoundary) {
+ continue;
+ }
+ const DisplayViewport* destinationViewport =
+ findViewportByIdLocked(adjacentDisplay.displayId);
+ if (destinationViewport == nullptr) {
+ // Topology is likely out of sync with viewport info, wait for them to be updated
+ LOG(WARNING) << "Cannot find viewport for adjacent display "
+ << adjacentDisplay.displayId << "of source display " << sourceDisplayId;
+ continue;
+ }
+ // target position must be within target display boundary
+ const int32_t edgeSize =
+ sourceBoundary == DisplayPosition::TOP || sourceBoundary == DisplayPosition::BOTTOM
+ ? (destinationViewport->logicalRight - destinationViewport->logicalLeft)
+ : (destinationViewport->logicalBottom - destinationViewport->logicalTop);
+ if (cursorOffset >= adjacentDisplay.offsetPx &&
+ cursorOffset <= adjacentDisplay.offsetPx + edgeSize) {
+ return std::make_pair(destinationViewport, adjacentDisplay.offsetPx);
+ }
+ }
+ return std::nullopt;
+}
+
// --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
@@ -883,12 +1089,14 @@
}
auto newPrivacySensitiveDisplays =
getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
+
+ // PointerChoreographer uses Listener's lock.
+ base::ScopedLockAssertion assumeLocked(mPointerChoreographer->getLock());
if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
- // PointerChoreographer uses Listener's lock.
- base::ScopedLockAssertion assumeLocked(mPointerChoreographer->getLock());
mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
}
+ mPointerChoreographer->populateFakeDisplayTopologyLocked(windowInfosUpdate.displayInfos);
}
void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index fba1aef..4ca7323 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -113,6 +113,24 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+ // TODO(b/362719483) remove these when real topology is available
+ enum class DisplayPosition {
+ RIGHT,
+ TOP,
+ LEFT,
+ BOTTOM,
+ ftl_last = BOTTOM,
+ };
+
+ struct AdjacentDisplay {
+ ui::LogicalDisplayId displayId;
+ DisplayPosition position;
+ float offsetPx;
+ };
+ void setDisplayTopology(
+ const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
+ displayTopology);
+
void dump(std::string& dump) override;
private:
@@ -153,6 +171,22 @@
const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays)
REQUIRES(getLock());
+ void handleUnconsumedDeltaLocked(PointerControllerInterface& pc,
+ const FloatPoint& unconsumedDelta) REQUIRES(getLock());
+
+ void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos)
+ REQUIRES(getLock());
+
+ std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
+ const ui::LogicalDisplayId sourceDisplayId, const DisplayPosition sourceBoundary,
+ float cursorOffset) const REQUIRES(getLock());
+
+ static vec2 calculateDestinationPosition(const DisplayViewport& destinationViewport,
+ float pointerOffset, DisplayPosition sourceBoundary);
+
+ std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>> mTopology
+ GUARDED_BY(getLock());
+
/* This listener keeps tracks of visible privacy sensitive displays and updates the
* choreographer if there are any changes.
*
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 8f3d9ca..abca209 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -72,8 +72,12 @@
/* Dumps the state of the pointer controller. */
virtual std::string dump() = 0;
- /* Move the pointer. */
- virtual void move(float deltaX, float deltaY) = 0;
+ /* Move the pointer and return unconsumed delta if the pointer has crossed the current
+ * viewport bounds.
+ *
+ * Return value may be used to move pointer to corresponding adjacent display, if it exists in
+ * the display-topology */
+ [[nodiscard]] virtual FloatPoint move(float deltaX, float deltaY) = 0;
/* Sets the absolute location of the pointer. */
virtual void setPosition(float x, float y) = 0;
@@ -145,6 +149,8 @@
/* Resets the flag to skip screenshot of the pointer indicators for all displays. */
virtual void clearSkipScreenshotFlags() = 0;
+
+ virtual ui::Transform getDisplayTransform() const = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 9eeb2b2..7434ae4 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -47,10 +47,6 @@
return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
}
-static inline bool isKeyboardBacklightCustomLevelsEnabled() {
- return sysprop::InputProperties::enable_keyboard_backlight_custom_levels().value_or(true);
-}
-
/**
* Input controller owned by InputReader device, implements the native API for querying input
* lights, getting and setting the lights brightness and color, by interacting with EventHub
@@ -289,8 +285,7 @@
std::set<BrightnessLevel> PeripheralController::getPreferredBrightnessLevels(
const Light* light) const {
std::set<BrightnessLevel> levels;
- if (!isKeyboardBacklightCustomLevelsEnabled() ||
- light->type != InputDeviceLightType::KEYBOARD_BACKLIGHT) {
+ if (light->type != InputDeviceLightType::KEYBOARD_BACKLIGHT) {
return levels;
}
std::optional<std::string> keyboardBacklightLevels =
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index 7a1d0ec..3c7f432 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -47,6 +47,7 @@
next: Box<dyn Filter + Send + Sync>,
listener: ModifierStateListener,
data: Data,
+ down_key_map: HashMap<i32, HashSet<i32>>,
}
#[derive(Default)]
@@ -69,15 +70,34 @@
next: Box<dyn Filter + Send + Sync>,
listener: ModifierStateListener,
) -> StickyKeysFilter {
- Self { next, listener, data: Default::default() }
+ Self { next, listener, data: Default::default(), down_key_map: HashMap::new() }
}
}
impl Filter for StickyKeysFilter {
fn notify_key(&mut self, event: &KeyEvent) {
+ let down = event.action == KeyEventAction::DOWN;
let up = event.action == KeyEventAction::UP;
let mut modifier_state = self.data.modifier_state;
let mut locked_modifier_state = self.data.locked_modifier_state;
+ if down {
+ let down_keys = self.down_key_map.entry(event.deviceId).or_default();
+ down_keys.insert(event.keyCode);
+ } else {
+ if !self.down_key_map.contains_key(&event.deviceId) {
+ self.next.notify_key(event);
+ return;
+ }
+ let down_keys = self.down_key_map.get_mut(&event.deviceId).unwrap();
+ if !down_keys.contains(&event.keyCode) {
+ self.next.notify_key(event);
+ return;
+ }
+ down_keys.remove(&event.keyCode);
+ if down_keys.is_empty() {
+ self.down_key_map.remove(&event.deviceId);
+ }
+ }
if !is_ephemeral_modifier_key(event.keyCode) {
// If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
// CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
@@ -130,6 +150,7 @@
self.data.locked_modifier_state = ModifierState::None;
self.listener.modifier_state_changed(ModifierState::None, ModifierState::None);
}
+ self.down_key_map.retain(|key, _| device_infos.iter().any(|x| *key == x.deviceId));
self.next.notify_devices_changed(device_infos);
}
@@ -166,6 +187,7 @@
result += &format!("\tmodifier_state = {:?}\n", self.data.modifier_state);
result += &format!("\tlocked_modifier_state = {:?}\n", self.data.locked_modifier_state);
result += &format!("\tcontributing_devices = {:?}\n", self.data.contributing_devices);
+ result += &format!("\tdown_key_map = {:?}\n", self.down_key_map);
self.next.dump(dump_str + &result)
}
}
@@ -322,6 +344,31 @@
}
#[test]
+ fn test_notify_key_passes_ephemeral_modifier_keys_if_only_key_up_occurs() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let key_codes = &[
+ KEYCODE_ALT_LEFT,
+ KEYCODE_ALT_RIGHT,
+ KEYCODE_CTRL_LEFT,
+ KEYCODE_CTRL_RIGHT,
+ KEYCODE_SHIFT_LEFT,
+ KEYCODE_SHIFT_RIGHT,
+ KEYCODE_META_LEFT,
+ KEYCODE_META_RIGHT,
+ ];
+ for key_code in key_codes.iter() {
+ let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+ }
+
+ #[test]
fn test_notify_key_passes_non_ephemeral_modifier_keys() {
let test_filter = TestFilter::new();
let test_callbacks = TestCallbacks::new();
@@ -437,6 +484,26 @@
}
#[test]
+ fn test_modifier_state_unchanged_on_non_modifier_key_up_without_down() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+ );
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
+ }
+
+ #[test]
fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() {
let test_filter = TestFilter::new();
let test_callbacks = TestCallbacks::new();
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 887a939..f53e63b 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -148,15 +148,20 @@
return mIsPointerShown;
}
-void FakePointerController::move(float deltaX, float deltaY) {
- if (!mEnabled) return;
+FloatPoint FakePointerController::move(float deltaX, float deltaY) {
+ if (!mEnabled) return {0, 0};
mX += deltaX;
+ mY += deltaY;
+
+ const FloatPoint position(mX, mY);
+
if (mX < mMinX) mX = mMinX;
if (mX > mMaxX) mX = mMaxX;
- mY += deltaY;
if (mY < mMinY) mY = mMinY;
if (mY > mMaxY) mY = mMaxY;
+
+ return {position.x - mX, position.y - mY};
}
void FakePointerController::fade(Transition) {
@@ -190,4 +195,8 @@
mSpotsByDisplay.clear();
}
+ui::Transform FakePointerController::getDisplayTransform() const {
+ return ui::Transform();
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 9b773a7..0ee3123 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -48,6 +48,7 @@
void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) override;
void clearSkipScreenshotFlags() override;
void fade(Transition) override;
+ ui::Transform getDisplayTransform() const override;
void assertViewportSet(ui::LogicalDisplayId displayId);
void assertViewportNotSet();
@@ -65,7 +66,7 @@
private:
std::string dump() override { return ""; }
- void move(float deltaX, float deltaY) override;
+ FloatPoint move(float deltaX, float deltaY) override;
void unfade(Transition) override;
void setPresentation(Presentation) override {}
void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 411c7ba..453c156 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -2601,6 +2601,178 @@
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT);
}
+using PointerChoreographerDisplayTopologyTestFixtureParam =
+ std::tuple<std::string_view /*name*/, int32_t /*source device*/,
+ ControllerType /*PointerController*/, ToolType /*pointer tool type*/,
+ FloatPoint /*source position*/, FloatPoint /*hover move X/Y*/,
+ ui::LogicalDisplayId /*destination display*/,
+ FloatPoint /*destination position*/>;
+
+class PointerChoreographerDisplayTopologyTestFixture
+ : public PointerChoreographerTest,
+ public testing::WithParamInterface<PointerChoreographerDisplayTopologyTestFixtureParam> {
+public:
+ static constexpr ui::LogicalDisplayId DISPLAY_CENTER_ID = ui::LogicalDisplayId{10};
+ static constexpr ui::LogicalDisplayId DISPLAY_TOP_ID = ui::LogicalDisplayId{20};
+ static constexpr ui::LogicalDisplayId DISPLAY_RIGHT_ID = ui::LogicalDisplayId{30};
+ static constexpr ui::LogicalDisplayId DISPLAY_BOTTOM_ID = ui::LogicalDisplayId{40};
+ static constexpr ui::LogicalDisplayId DISPLAY_LEFT_ID = ui::LogicalDisplayId{50};
+ static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60};
+
+ PointerChoreographerDisplayTopologyTestFixture() {
+ com::android::input::flags::connected_displays_cursor(true);
+ }
+
+protected:
+ std::vector<DisplayViewport> mViewports{
+ createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100, ui::ROTATION_0),
+ createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0),
+ createViewport(DISPLAY_RIGHT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_90),
+ createViewport(DISPLAY_BOTTOM_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_180),
+ createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_270),
+ createViewport(DISPLAY_TOP_RIGHT_CORNER_ID, /*width*/ 90, /*height*/ 90,
+ ui::ROTATION_0),
+ };
+
+ std::unordered_map<ui::LogicalDisplayId, std::vector<PointerChoreographer::AdjacentDisplay>>
+ mTopology{
+ {DISPLAY_CENTER_ID,
+ {{DISPLAY_TOP_ID, PointerChoreographer::DisplayPosition::TOP, 10.0f},
+ {DISPLAY_RIGHT_ID, PointerChoreographer::DisplayPosition::RIGHT, 10.0f},
+ {DISPLAY_BOTTOM_ID, PointerChoreographer::DisplayPosition::BOTTOM, 10.0f},
+ {DISPLAY_LEFT_ID, PointerChoreographer::DisplayPosition::LEFT, 10.0f},
+ {DISPLAY_TOP_RIGHT_CORNER_ID, PointerChoreographer::DisplayPosition::RIGHT,
+ -90.0f}}},
+ };
+
+private:
+ DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
+ ui::Rotation orientation) {
+ DisplayViewport viewport;
+ viewport.displayId = displayId;
+ viewport.logicalRight = width;
+ viewport.logicalBottom = height;
+ viewport.orientation = orientation;
+ return viewport;
+ }
+};
+
+TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDisplayTopologyTest) {
+ const auto& [_, device, pointerControllerType, pointerToolType, initialPosition, hoverMove,
+ destinationDisplay, destinationPosition] = GetParam();
+
+ mChoreographer.setDisplayViewports(mViewports);
+ mChoreographer.setDefaultMouseDisplayId(
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID);
+ mChoreographer.setDisplayTopology(mTopology);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, device, ui::LogicalDisplayId::INVALID)}});
+
+ auto pc = assertPointerControllerCreated(pointerControllerType);
+ ASSERT_EQ(PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(initialPosition.x, initialPosition.y);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ auto pointerBuilder = PointerBuilder(/*id=*/0, pointerToolType)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, hoverMove.x)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, hoverMove.y);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, device)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ ASSERT_TRUE(pc->isPointerShown());
+ ASSERT_EQ(pc->getDisplayId(), destinationDisplay);
+ auto position = pc->getPosition();
+ ASSERT_EQ(position.x, destinationPosition.x);
+ ASSERT_EQ(position.y, destinationPosition.y);
+
+ // Check that x-y coordinates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(destinationPosition.x, destinationPosition.y),
+ WithDisplayId(destinationDisplay),
+ WithCursorPosition(destinationPosition.x, destinationPosition.y)));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PointerChoreographerTest, PointerChoreographerDisplayTopologyTestFixture,
+ testing::Values(
+ // Note: Upon viewport transition cursor will be positioned at the boundary of the
+ // destination, as we drop any unconsumed delta.
+ std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, FloatPoint(50, 50) /* initial x/y */,
+ FloatPoint(25, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ FloatPoint(75, 75) /* destination x/y */),
+ std::make_tuple(
+ "TransitionToRightDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, FloatPoint(50, 50) /* initial x/y */,
+ FloatPoint(100, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_RIGHT_ID,
+ FloatPoint(0, 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
+ std::make_tuple(
+ "TransitionToLeftDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, FloatPoint(50, 50) /* initial x/y */,
+ FloatPoint(-100, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_LEFT_ID,
+ FloatPoint(90,
+ 50 + 25 - 10) /* Right edge: (width, source + delta - offset*/),
+ std::make_tuple(
+ "TransitionToTopDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER,
+ FloatPoint(50, 50) /* initial x/y */, FloatPoint(25, -100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID,
+ FloatPoint(50 + 25 - 10,
+ 90) /* Bottom edge: (source + delta - offset, height) */),
+ std::make_tuple(
+ "TransitionToBottomDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER,
+ FloatPoint(50, 50) /* initial x/y */, FloatPoint(25, 100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID,
+ FloatPoint(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
+ std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE,
+ ControllerType::MOUSE, ToolType::MOUSE,
+ FloatPoint(5, 50) /* initial x/y */,
+ FloatPoint(0, -100) /* Move Up */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ FloatPoint(5, 0) /* Top edge */),
+ std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE,
+ ControllerType::MOUSE, ToolType::MOUSE,
+ FloatPoint(95, 5) /* initial x/y */,
+ FloatPoint(100, 0) /* Move Right */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ FloatPoint(99, 5) /* Top edge */),
+ std::make_tuple("NoTransitionAtBottomOffset",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, FloatPoint(5, 95) /* initial x/y */,
+ FloatPoint(0, 100) /* Move Down */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ FloatPoint(5, 99) /* Bottom edge */),
+ std::make_tuple("NoTransitionAtLeftOffset",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, FloatPoint(5, 5) /* initial x/y */,
+ FloatPoint(-100, 0) /* Move Left */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ FloatPoint(0, 5) /* Left edge */),
+ std::make_tuple(
+ "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER,
+ FloatPoint(95, 5) /* initial x/y */,
+ FloatPoint(10, -10) /* Move dignally to top right corner */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID,
+ FloatPoint(0, 90) /* bottom left corner */)),
+ [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
+ return std::string{std::get<0>(p.param)};
+ });
+
class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
TEST_F_WITH_FLAGS(
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 71d3d1f..682d1f4 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -69,10 +69,6 @@
(const CpuHeadroomParams& params, CpuHeadroomResult* headroom), (override));
MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroom,
(const GpuHeadroomParams& params, GpuHeadroomResult* headroom), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* interval),
- (override));
- MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* interval),
- (override));
MOCK_METHOD(ndk::ScopedAStatus, sendCompositionData,
(const std::vector<CompositionData>& in_data), (override));
MOCK_METHOD(ndk::ScopedAStatus, sendCompositionUpdate,
diff --git a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
new file mode 100644
index 0000000..c68020c
--- /dev/null
+++ b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+
+#include <ui/DisplayId.h>
+
+#include "Utils/Dumper.h"
+
+namespace android::display {
+
+// Immutable state of a virtual display, captured on creation.
+class VirtualDisplaySnapshot {
+public:
+ VirtualDisplaySnapshot(GpuVirtualDisplayId gpuId, std::string uniqueId)
+ : mIsGpu(true), mUniqueId(std::move(uniqueId)), mVirtualId(gpuId) {}
+ VirtualDisplaySnapshot(HalVirtualDisplayId halId, std::string uniqueId)
+ : mIsGpu(false), mUniqueId(std::move(uniqueId)), mVirtualId(halId) {}
+
+ VirtualDisplayId displayId() const { return mVirtualId; }
+ bool isGpu() const { return mIsGpu; }
+
+ void dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
+
+ dumper.dump("isGpu"sv, mIsGpu ? "true"sv : "false"sv);
+ dumper.dump("uniqueId"sv, mUniqueId);
+ }
+
+private:
+ const bool mIsGpu;
+ const std::string mUniqueId;
+ const VirtualDisplayId mVirtualId;
+};
+
+} // namespace android::display
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index b83f2ab..25f6513 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -37,10 +37,6 @@
namespace android {
-using hardware::hidl_handle;
-using hardware::hidl_vec;
-using hardware::Return;
-
using aidl::android::hardware::graphics::composer3::BnComposerCallback;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
@@ -524,11 +520,15 @@
Error AidlComposer::getDisplayAttribute(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
const auto status =
mAidlComposerClient->getDisplayAttribute(translate<int64_t>(display),
translate<int32_t>(config),
static_cast<AidlDisplayAttribute>(attribute),
outValue);
+#pragma clang diagnostic pop
+
if (!status.isOk()) {
ALOGE("getDisplayAttribute failed %s", status.getDescription().c_str());
return static_cast<Error>(status.getServiceSpecificError());
@@ -538,8 +538,13 @@
Error AidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
std::vector<int32_t> configs;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
const auto status =
mAidlComposerClient->getDisplayConfigs(translate<int64_t>(display), &configs);
+#pragma clang diagnostic pop
+
if (!status.isOk()) {
ALOGE("getDisplayConfigs failed %s", status.getDescription().c_str());
return static_cast<Error>(status.getServiceSpecificError());
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index db63d3e..6b5ebc5 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -27,11 +27,6 @@
#include <utility>
#include <vector>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
@@ -43,9 +38,6 @@
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
namespace android::Hwc2 {
using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 224f50e..e90b5b7 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -31,10 +31,11 @@
#include <common/FlagManager.h>
#include <scheduler/Fps.h>
-#include "DisplayHardware/Hal.h"
+#include "Hal.h"
namespace android {
+using aidl::android::hardware::graphics::composer3::OutputType;
namespace hal = android::hardware::graphics::composer::hal;
class DisplayMode;
@@ -114,6 +115,11 @@
return *this;
}
+ Builder& setHdrOutputType(OutputType type) {
+ mDisplayMode->mHdrOutputType = type;
+ return *this;
+ }
+
private:
float getDefaultDensity() {
// Default density is based on TVs: 1080p displays get XHIGH density, lower-
@@ -166,6 +172,8 @@
// without visual interruptions such as a black screen.
int32_t getGroup() const { return mGroup; }
+ OutputType getHdrOutputType() const { return mHdrOutputType; }
+
private:
explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
@@ -179,21 +187,25 @@
Dpi mDpi;
int32_t mGroup = -1;
std::optional<hal::VrrConfig> mVrrConfig;
+ OutputType mHdrOutputType;
};
inline bool equalsExceptDisplayModeId(const DisplayMode& lhs, const DisplayMode& rhs) {
return lhs.getHwcId() == rhs.getHwcId() && lhs.getResolution() == rhs.getResolution() &&
lhs.getVsyncRate().getPeriodNsecs() == rhs.getVsyncRate().getPeriodNsecs() &&
- lhs.getDpi() == rhs.getDpi() && lhs.getGroup() == rhs.getGroup();
+ lhs.getDpi() == rhs.getDpi() && lhs.getGroup() == rhs.getGroup() &&
+ lhs.getVrrConfig() == rhs.getVrrConfig() &&
+ lhs.getHdrOutputType() == rhs.getHdrOutputType();
}
inline std::string to_string(const DisplayMode& mode) {
return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, vsyncRate=%s, "
- "dpi=%.2fx%.2f, group=%d, vrrConfig=%s}",
+ "dpi=%.2fx%.2f, group=%d, vrrConfig=%s, supportedHdrTypes=%s}",
ftl::to_underlying(mode.getId()), mode.getHwcId(), mode.getWidth(),
mode.getHeight(), to_string(mode.getVsyncRate()).c_str(),
mode.getDpi().x, mode.getDpi().y, mode.getGroup(),
- to_string(mode.getVrrConfig()).c_str());
+ to_string(mode.getVrrConfig()).c_str(),
+ toString(mode.getHdrOutputType()).c_str());
}
template <typename... DisplayModePtrs>
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 9943856..55ccdef 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -336,7 +336,8 @@
.height = config.height,
.vsyncPeriod = config.vsyncPeriod,
.configGroup = config.configGroup,
- .vrrConfig = config.vrrConfig};
+ .vrrConfig = config.vrrConfig,
+ .hdrOutputType = config.hdrOutputType};
const DisplayConfiguration::Dpi estimatedDPI =
getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e21ce1d..52662cf 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -55,6 +55,7 @@
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <aidl/android/hardware/graphics/composer3/OutputType.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -112,12 +113,14 @@
float dpiY = -1.f;
int32_t configGroup = -1;
std::optional<hal::VrrConfig> vrrConfig;
+ OutputType hdrOutputType;
friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
return os << "id=" << mode.hwcId << " res=" << mode.width << "x" << mode.height
<< " vsyncPeriod=" << mode.vsyncPeriod << " dpi=" << mode.dpiX << "x"
<< mode.dpiY << " group=" << mode.configGroup
- << " vrrConfig=" << to_string(mode.vrrConfig).c_str();
+ << " vrrConfig=" << to_string(mode.vrrConfig).c_str()
+ << " hdrOutputType=" << toString(mode.hdrOutputType);
}
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index fff4284..c6d7160 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -43,6 +43,7 @@
#include <utils/Errors.h>
#include <common/FlagManager.h>
+#include <scheduler/FrameRateMode.h>
#include <scheduler/VsyncConfig.h>
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
@@ -102,6 +103,10 @@
to_string(event.header.displayId).c_str(),
event.hdcpLevelsChange.connectedLevel,
event.hdcpLevelsChange.maxLevel);
+ case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+ return StringPrintf("ModeRejected{displayId=%s, modeId=%u}",
+ to_string(event.header.displayId).c_str(),
+ event.modeRejection.modeId);
default:
return "Event{}";
}
@@ -186,6 +191,18 @@
};
}
+DisplayEventReceiver::Event makeModeRejection(PhysicalDisplayId displayId, DisplayModeId modeId) {
+ return DisplayEventReceiver::Event{
+ .header =
+ DisplayEventReceiver::Event::Header{
+ .type = DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION,
+ .displayId = displayId,
+ .timestamp = systemTime(),
+ },
+ .modeRejection.modeId = ftl::to_underlying(modeId),
+ };
+}
+
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
@@ -480,6 +497,13 @@
mCondition.notify_all();
}
+void EventThread::onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mPendingEvents.push_back(makeModeRejection(displayId, modeId));
+ mCondition.notify_all();
+}
+
// Merge lists of buffer stuffed Uids
void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
std::lock_guard<std::mutex> lock(mMutex);
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 95632c7..18bf416 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -21,6 +21,7 @@
#include <gui/DisplayEventReceiver.h>
#include <private/gui/BitTube.h>
#include <sys/types.h>
+#include <ui/DisplayId.h>
#include <utils/Errors.h>
#include <scheduler/FrameRateMode.h>
@@ -32,6 +33,7 @@
#include <thread>
#include <vector>
+#include "DisplayHardware/DisplayMode.h"
#include "TracedOrdinal.h"
#include "VSyncDispatch.h"
#include "VsyncSchedule.h"
@@ -115,6 +117,9 @@
// called when SF changes the active mode and apps needs to be notified about the change
virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
+ // called when SF rejects the mode change request
+ virtual void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) = 0;
+
// called when SF updates the Frame Rate Override list
virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) = 0;
@@ -179,6 +184,8 @@
void onModeChanged(const scheduler::FrameRateMode&) override;
+ void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) override;
+
void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 2875650..86d91d9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -465,6 +465,12 @@
}
#pragma clang diagnostic pop
+void Scheduler::onDisplayModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) {
+ if (hasEventThreads()) {
+ eventThreadFor(Cycle::Render).onModeRejected(displayId, modeId);
+ }
+}
+
void Scheduler::emitModeChangeIfNeeded() {
if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
ALOGW("No mode change to emit");
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 61c68a9..3fdddac 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,12 +36,14 @@
#include <ftl/non_null.h>
#include <ftl/optional.h>
#include <scheduler/Features.h>
+#include <scheduler/FrameRateMode.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/Time.h>
#include <scheduler/VsyncConfig.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMap.h>
+#include "DisplayHardware/DisplayMode.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "ISchedulerCallback.h"
@@ -153,6 +155,8 @@
bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&,
bool clearContentRequirements) EXCLUDES(mPolicyLock);
+ void onDisplayModeRejected(PhysicalDisplayId, DisplayModeId);
+
void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 97c8623..8f41112 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -172,6 +172,7 @@
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/OutputType.h>
#include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
#undef NO_THREAD_SAFETY_ANALYSIS
@@ -649,11 +650,12 @@
}
}
-VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution,
- ui::PixelFormat format) {
+VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
+ const std::string& uniqueId) {
if (auto& generator = mVirtualDisplayIdGenerators.hal) {
if (const auto id = generator->generateId()) {
if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
+ acquireVirtualDisplaySnapshot(*id, uniqueId);
return *id;
}
@@ -667,6 +669,7 @@
const auto id = mVirtualDisplayIdGenerators.gpu.generateId();
LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display");
+ acquireVirtualDisplaySnapshot(*id, uniqueId);
return *id;
}
@@ -674,6 +677,7 @@
if (const auto id = HalVirtualDisplayId::tryCast(displayId)) {
if (auto& generator = mVirtualDisplayIdGenerators.hal) {
generator->releaseId(*id);
+ releaseVirtualDisplaySnapshot(*id);
}
return;
}
@@ -681,6 +685,14 @@
const auto id = GpuVirtualDisplayId::tryCast(displayId);
LOG_ALWAYS_FATAL_IF(!id);
mVirtualDisplayIdGenerators.gpu.releaseId(*id);
+ releaseVirtualDisplaySnapshot(*id);
+}
+
+void SurfaceFlinger::releaseVirtualDisplaySnapshot(VirtualDisplayId displayId) {
+ std::lock_guard lock(mVirtualDisplaysMutex);
+ if (!mVirtualDisplays.erase(displayId)) {
+ ALOGW("%s: Virtual display snapshot was not removed", __func__);
+ }
}
std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
@@ -1532,9 +1544,15 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- if (mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
- constraints, outTimeline) !=
- display::DisplayModeController::ModeChangeResult::Changed) {
+ const auto error =
+ mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
+ constraints, outTimeline);
+ if (error != display::DisplayModeController::ModeChangeResult::Changed) {
+ dropModeRequest(displayId);
+ if (FlagManager::getInstance().display_config_error_hal() &&
+ error == display::DisplayModeController::ModeChangeResult::Rejected) {
+ mScheduler->onDisplayModeRejected(displayId, desiredModeId);
+ }
continue;
}
@@ -3498,6 +3516,9 @@
DisplayModes newModes;
for (const auto& hwcMode : hwcModes) {
const auto id = nextModeId++;
+ OutputType hdrOutputType = FlagManager::getInstance().connected_display_hdr()
+ ? hwcMode.hdrOutputType
+ : OutputType::INVALID;
newModes.try_emplace(id,
DisplayMode::Builder(hwcMode.hwcId)
.setId(id)
@@ -3508,6 +3529,7 @@
.setDpiX(hwcMode.dpiX)
.setDpiY(hwcMode.dpiY)
.setGroup(hwcMode.configGroup)
+ .setHdrOutputType(hdrOutputType)
.build());
}
@@ -3798,7 +3820,7 @@
if (const auto& physical = state.physical) {
builder.setId(physical->id);
} else {
- builder.setId(acquireVirtualDisplay(resolution, pixelFormat));
+ builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId));
}
builder.setPixels(resolution);
@@ -5785,6 +5807,14 @@
utils::Dumper::Section section(dumper,
ftl::Concat("Virtual Display ", displayId.value).str());
display->dump(dumper);
+
+ if (const auto virtualIdOpt = VirtualDisplayId::tryCast(displayId)) {
+ std::lock_guard lock(mVirtualDisplaysMutex);
+ const auto virtualSnapshotIt = mVirtualDisplays.find(virtualIdOpt.value());
+ if (virtualSnapshotIt != mVirtualDisplays.end()) {
+ virtualSnapshotIt->second.dump(dumper);
+ }
+ }
}
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 211f374..b20a894 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -73,6 +73,7 @@
#include "BackgroundExecutor.h"
#include "Display/DisplayModeController.h"
#include "Display/PhysicalDisplay.h"
+#include "Display/VirtualDisplaySnapshot.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayIdGenerator.h"
@@ -1075,8 +1076,20 @@
void enableHalVirtualDisplays(bool);
// Virtual display lifecycle for ID generation and HAL allocation.
- VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
+ VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, const std::string& uniqueId)
+ REQUIRES(mStateLock);
+ template <typename ID>
+ void acquireVirtualDisplaySnapshot(ID displayId, const std::string& uniqueId) {
+ std::lock_guard lock(mVirtualDisplaysMutex);
+ const bool emplace_success =
+ mVirtualDisplays.try_emplace(displayId, displayId, uniqueId).second;
+ if (!emplace_success) {
+ ALOGW("%s: Virtual display snapshot with the same ID already exists", __func__);
+ }
+ }
+
void releaseVirtualDisplay(VirtualDisplayId);
+ void releaseVirtualDisplaySnapshot(VirtualDisplayId displayId);
// Returns a display other than `mActiveDisplayId` that can be activated, if any.
sp<DisplayDevice> getActivatableDisplay() const REQUIRES(mStateLock, kMainThreadContext);
@@ -1277,6 +1290,10 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
+ mutable std::mutex mVirtualDisplaysMutex;
+ ftl::SmallMap<VirtualDisplayId, const display::VirtualDisplaySnapshot, 2> mVirtualDisplays
+ GUARDED_BY(mVirtualDisplaysMutex);
+
// The inner or outer display for foldables, while unfolded or folded, respectively.
std::atomic<PhysicalDisplayId> mActiveDisplayId;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 3960bf6..e28a0c1 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -259,7 +259,7 @@
FLAG_MANAGER_ACONFIG_FLAG(force_compile_graphite_renderengine, "");
FLAG_MANAGER_ACONFIG_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
FLAG_MANAGER_ACONFIG_FLAG(display_config_error_hal, "");
-FLAG_MANAGER_ACONFIG_FLAG(connected_display_hdr, "");
+FLAG_MANAGER_ACONFIG_FLAG(connected_display_hdr, "debug.sf.connected_display_hdr");
FLAG_MANAGER_ACONFIG_FLAG(deprecate_frame_tracker, "");
FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, "");
FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 30bce2e..ba2d3e2 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -384,6 +384,7 @@
const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
const float expectedDpiX = (kWidth * kMmPerInch / size.width);
const float expectedDpiY = (kHeight * kMmPerInch / size.height);
+ const OutputType hdrOutputType = OutputType::SYSTEM;
const hal::VrrConfig vrrConfig =
hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(),
.notifyExpectedPresentConfig = hal::VrrConfig::
@@ -394,7 +395,8 @@
.height = kHeight,
.configGroup = kConfigGroup,
.vsyncPeriod = kVsyncPeriod,
- .vrrConfig = vrrConfig};
+ .vrrConfig = vrrConfig,
+ .hdrOutputType = hdrOutputType};
EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
.WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
@@ -410,6 +412,7 @@
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
+ EXPECT_EQ(modes.front().hdrOutputType, hdrOutputType);
if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
EXPECT_EQ(modes.front().dpiX, -1);
EXPECT_EQ(modes.front().dpiY, -1);
@@ -435,6 +438,7 @@
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
+ EXPECT_EQ(modes.front().hdrOutputType, hdrOutputType);
EXPECT_EQ(modes.front().dpiX, kDpi);
EXPECT_EQ(modes.front().dpiY, kDpi);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index c976697..3036fec 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -34,6 +34,8 @@
MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override));
+ MOCK_METHOD(void, onModeRejected, (PhysicalDisplayId displayId, DisplayModeId modeId),
+ (override));
MOCK_METHOD(void, onFrameRateOverridesChanged,
(PhysicalDisplayId, std::vector<FrameRateOverride>), (override));
MOCK_METHOD(void, dump, (std::string&), (const, override));