summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin F. Haggerty <haggertk@lineageos.org>2020-06-01 21:10:00 -0600
committerKevin F. Haggerty <haggertk@lineageos.org>2020-06-01 21:59:33 -0600
commit9b6e5d54b95656d56026c9617a3645ddf14e9057 (patch)
tree2703c7247a24e0edd90e5d4e1cd70dd8c892a535
parent470dde656e0ee547f78ac403a6f959e1438c2158 (diff)
parentb55734b8854d864d0feeeb1a5b12fa92dd0a7aef (diff)
downloadandroid_frameworks_native-9b6e5d54b95656d56026c9617a3645ddf14e9057.tar.gz
android_frameworks_native-9b6e5d54b95656d56026c9617a3645ddf14e9057.tar.bz2
android_frameworks_native-9b6e5d54b95656d56026c9617a3645ddf14e9057.zip
Merge tag 'android-10.0.0_r37' into staging/lineage-17.1_merge-android-10.0.0_r37
Android 10.0.0 Release 37 (QQ3A.200605.001) * tag 'android-10.0.0_r37': Add a flag for refresh rate switching Disable deep press when long press timeout is long Properly initialize MotionClassifier sf: avoid assumption remote binder is local installd: add dex2oat cpu-set system properties Swap video frame rotation direction. libvulkan: remove a redundant check debuggable logic vkjson: Use Vulkan API version 1.1 on VkJsonGetInstance Changing logic that generates virtual SENSOR_TYPE_LINEAR_ACCELERATION. DO NOT MERGE: SF: fix SurfaceFlinger freeze [installd] Remove boot complete marker Fix addition/overflow checks. GraphicsEnv: refactor to unify the debuggable logic Conflicts: services/surfaceflinger/SurfaceFlinger.cpp Change-Id: I39fd5ce530da4efea3bdcf4550e199cacedf378d
-rw-r--r--cmds/installd/InstalldNativeService.cpp20
-rw-r--r--cmds/installd/InstalldNativeService.h1
-rw-r--r--cmds/installd/binder/android/os/IInstalld.aidl1
-rw-r--r--cmds/installd/dexopt.cpp5
-rw-r--r--cmds/installd/otapreopt.cpp4
-rw-r--r--libs/binder/BufferedTextOutput.cpp3
-rw-r--r--libs/binder/Parcel.cpp10
-rw-r--r--libs/graphicsenv/GraphicsEnv.cpp8
-rw-r--r--libs/graphicsenv/include/graphicsenv/GraphicsEnv.h11
-rw-r--r--libs/input/TouchVideoFrame.cpp4
-rw-r--r--libs/input/tests/TouchVideoFrame_test.cpp8
-rw-r--r--opengl/libs/EGL/egl_layers.cpp2
-rw-r--r--services/inputflinger/Android.bp1
-rw-r--r--services/inputflinger/InputClassifier.cpp159
-rw-r--r--services/inputflinger/InputClassifier.h92
-rw-r--r--services/inputflinger/InputManager.cpp4
-rw-r--r--services/inputflinger/InputManager.h2
-rw-r--r--services/inputflinger/tests/InputClassifier_test.cpp55
-rw-r--r--services/sensorservice/SensorService.cpp11
-rw-r--r--services/surfaceflinger/BufferQueueLayer.cpp2
-rw-r--r--services/surfaceflinger/RegionSamplingThread.cpp7
-rw-r--r--services/surfaceflinger/Scheduler/PhaseOffsets.cpp1
-rw-r--r--services/surfaceflinger/Scheduler/RefreshRateConfigs.h184
-rw-r--r--services/surfaceflinger/Scheduler/RefreshRateStats.h71
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.cpp107
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.h30
-rw-r--r--services/surfaceflinger/Scheduler/SchedulerUtils.h6
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp214
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h8
-rw-r--r--services/surfaceflinger/SurfaceFlingerProperties.cpp8
-rw-r--r--services/surfaceflinger/SurfaceFlingerProperties.h2
-rw-r--r--services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop14
-rw-r--r--services/surfaceflinger/sysprop/api/system-current.txt1
-rw-r--r--services/surfaceflinger/tests/unittests/CompositionTest.cpp12
-rw-r--r--services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp12
-rw-r--r--services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp112
-rw-r--r--services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp139
-rw-r--r--services/surfaceflinger/tests/unittests/SchedulerTest.cpp19
-rw-r--r--services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h4
-rw-r--r--vulkan/libvulkan/api.cpp5
-rw-r--r--vulkan/libvulkan/driver.cpp4
-rw-r--r--vulkan/libvulkan/driver.h1
-rw-r--r--vulkan/libvulkan/layers_extensions.cpp3
43 files changed, 676 insertions, 691 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e6de4f02f..ecf09fcaf 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -2141,26 +2141,6 @@ binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath,
return *_aidl_return ? ok() : error("viewcompiler failed");
}
-binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
- ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- const char* instruction_set = instructionSet.c_str();
-
- char boot_marker_path[PKG_PATH_MAX];
- sprintf(boot_marker_path,
- "%s/%s/%s/.booting",
- android_data_dir.c_str(),
- DALVIK_CACHE,
- instruction_set);
-
- ALOGV("mark_boot_complete : %s", boot_marker_path);
- if (unlink(boot_marker_path) != 0) {
- return error(StringPrintf("Failed to unlink %s", boot_marker_path));
- }
- return ok();
-}
-
binder::Status InstalldNativeService::linkNativeLibraryDirectory(
const std::unique_ptr<std::string>& uuid, const std::string& packageName,
const std::string& nativeLibPath32, int32_t userId) {
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 2b7bf33cb..eba966d4a 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -123,7 +123,6 @@ public:
int32_t uid);
binder::Status removeIdmap(const std::string& overlayApkPath);
binder::Status rmPackageDir(const std::string& packageDir);
- binder::Status markBootComplete(const std::string& instructionSet);
binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
int64_t cacheReservedBytes, int32_t flags);
binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index d99bcc8d1..f0d38acd0 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -75,7 +75,6 @@ interface IInstalld {
void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
void removeIdmap(@utf8InCpp String overlayApkPath);
void rmPackageDir(@utf8InCpp String packageDir);
- void markBootComplete(@utf8InCpp String instructionSet);
void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
long cacheReservedBytes, int flags);
void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index dbb4f2237..5fb13a5c5 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -336,6 +336,10 @@ class RunDex2Oat : public ExecVHelper {
? "dalvik.vm.dex2oat-threads"
: "dalvik.vm.boot-dex2oat-threads";
std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s");
+ const char* cpu_set_property = post_bootcomplete
+ ? "dalvik.vm.dex2oat-cpu-set"
+ : "dalvik.vm.boot-dex2oat-cpu-set";
+ std::string dex2oat_cpu_set_arg = MapPropertyToArg(cpu_set_property, "--cpu-set=%s");
std::string bootclasspath;
char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
@@ -507,6 +511,7 @@ class RunDex2Oat : public ExecVHelper {
AddArg(image_block_size_arg);
AddArg(dex2oat_compiler_filter_arg);
AddArg(dex2oat_threads_arg);
+ AddArg(dex2oat_cpu_set_arg);
AddArg(dex2oat_swap_fd);
AddArg(dex2oat_image_fd);
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index de7b2499b..9bc5fc81d 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -478,6 +478,10 @@ private:
"-j",
false,
cmd);
+ AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set",
+ "--cpu-set=",
+ false,
+ cmd);
AddCompilerOptionFromSystemProperty(
StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
"--instruction-set-variant=",
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 857bbf951..2978b539d 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -49,9 +49,10 @@ struct BufferedTextOutput::BufferState : public RefBase
}
status_t append(const char* txt, size_t len) {
+ if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow
if ((len+bufferPos) > bufferSize) {
+ if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((len+bufferPos)*3)/2;
- if (newSize < (len+bufferPos)) return NO_MEMORY; // overflow
void* b = realloc(buffer, newSize);
if (!b) return NO_MEMORY;
buffer = (char*)b;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 5ad30271d..11441eca6 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -520,8 +520,10 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
const sp<ProcessState> proc(ProcessState::self());
// grow objects
if (mObjectsCapacity < mObjectsSize + numObjects) {
+ if ((size_t) numObjects > SIZE_MAX - mObjectsSize) return NO_MEMORY; // overflow
+ if (mObjectsSize + numObjects > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mObjectsSize + numObjects)*3)/2;
- if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow
+ if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
binder_size_t *objects =
(binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
if (objects == (binder_size_t*)nullptr) {
@@ -1399,8 +1401,10 @@ restart_write:
if (err != NO_ERROR) return err;
}
if (!enoughObjects) {
+ if (mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow
+ if ((mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mObjectsSize+2)*3)/2;
- if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow
+ if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
if (objects == nullptr) return NO_MEMORY;
mObjects = objects;
@@ -2685,6 +2689,8 @@ status_t Parcel::growData(size_t len)
return BAD_VALUE;
}
+ if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow
+ if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index bb9e263ac..6e59cc504 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -139,12 +139,8 @@ static const std::string getSystemNativeLibraries(NativeLibrary type) {
return env;
}
-int GraphicsEnv::getCanLoadSystemLibraries() {
- if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
- // Return an integer value since this crosses library boundaries
- return 1;
- }
- return 0;
+bool GraphicsEnv::isDebuggable() {
+ return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
}
void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 937bcd9ac..227b4587c 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -84,7 +84,16 @@ private:
public:
static GraphicsEnv& getInstance();
- int getCanLoadSystemLibraries();
+ // Check if the process is debuggable. It returns false except in any of the
+ // following circumstances:
+ // 1. ro.debuggable=1 (global debuggable enabled).
+ // 2. android:debuggable="true" in the manifest for an individual app.
+ // 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1).
+ // 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of
+ // <meta-data android:name="com.android.graphics.injectLayers.enable"
+ // android:value="true"/>
+ // in the application manifest.
+ bool isDebuggable();
// Set a search path for loading graphics drivers. The path is a list of
// directories separated by ':'. A directory can be contained in a zip file
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index 8a4298a36..ce76e3fa6 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -42,13 +42,13 @@ const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp;
void TouchVideoFrame::rotate(int32_t orientation) {
switch (orientation) {
case DISPLAY_ORIENTATION_90:
- rotateQuarterTurn(true /*clockwise*/);
+ rotateQuarterTurn(false /*clockwise*/);
break;
case DISPLAY_ORIENTATION_180:
rotate180();
break;
case DISPLAY_ORIENTATION_270:
- rotateQuarterTurn(false /*clockwise*/);
+ rotateQuarterTurn(true /*clockwise*/);
break;
}
}
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 815424ee3..fe06cad72 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -85,14 +85,14 @@ TEST(TouchVideoFrame, Rotate90_1x1) {
TEST(TouchVideoFrame, Rotate90_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_90);
ASSERT_EQ(frame, frameRotated);
}
@@ -170,14 +170,14 @@ TEST(TouchVideoFrame, Rotate270_1x1) {
TEST(TouchVideoFrame, Rotate270_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_270);
ASSERT_EQ(frame, frameRotated);
}
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index ac01dc8f9..ba7cacdbf 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -337,7 +337,7 @@ void LayerLoader::LoadLayers() {
// Only enable the system search path for non-user builds
std::string system_path;
- if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ if (android::GraphicsEnv::getInstance().isDebuggable()) {
system_path = kSystemLayerLibraryDir;
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index bebc41787..0a6eed8c4 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -48,7 +48,6 @@ cc_library_shared {
"liblog",
"libutils",
"libui",
- "server_configurable_flags",
],
cflags: [
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index ef1a2247e..cda0e0cdc 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -27,7 +27,6 @@
#if defined(__linux__)
#include <pthread.h>
#endif
-#include <server_configurable_flags/get_flags.h>
#include <unordered_set>
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
@@ -46,13 +45,6 @@ using namespace android::hardware::input;
namespace android {
-static constexpr bool DEBUG = false;
-
-// Category (=namespace) name for the input settings that are applied at boot time
-static const char* INPUT_NATIVE_BOOT = "input_native_boot";
-// Feature flag name for the deep press feature
-static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
-
//Max number of elements to store in mEvents.
static constexpr size_t MAX_EVENTS = 5;
@@ -79,20 +71,6 @@ static bool isTouchEvent(const NotifyMotionArgs& args) {
return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
}
-// Check if the "deep touch" feature is on.
-static bool deepPressEnabled() {
- std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
- INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
- std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
- if (flag_value == "1" || flag_value == "true") {
- ALOGI("Deep press feature enabled.");
- return true;
- }
- ALOGI("Deep press feature is not enabled.");
- return false;
-}
-
-
// --- ClassifierEvent ---
ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
@@ -141,53 +119,40 @@ std::optional<int32_t> ClassifierEvent::getDeviceId() const {
// --- MotionClassifier ---
-MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) :
- mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
- mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
+MotionClassifier::MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
+ : mEvents(MAX_EVENTS), mService(service) {
+ // Under normal operation, we do not need to reset the HAL here. But in the case where system
+ // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
+ // have received events in the past. That means, that HAL could be in an inconsistent state
+ // once it receives events from the newly created MotionClassifier.
+ mEvents.push(ClassifierEvent::createHalResetEvent());
+
+ mHalThread = std::thread(&MotionClassifier::processEvents, this);
#if defined(__linux__)
// Set the thread name for debugging
pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
#endif
}
-/**
- * This function may block for some time to initialize the HAL, so it should only be called
- * from the "InputClassifier HAL" thread.
- */
-bool MotionClassifier::init() {
- ensureHalThread(__func__);
+std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient) {
sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
classifier::V1_0::IInputClassifier::getService();
if (!service) {
// Not really an error, maybe the device does not have this HAL,
// but somehow the feature flag is flipped
ALOGI("Could not obtain InputClassifier HAL");
- return false;
+ return nullptr;
}
- sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
- if (recipient != nullptr) {
- const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
- if (!linked) {
- ALOGE("Could not link MotionClassifier to the HAL death");
- return false;
- }
+ const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
+ if (!linked) {
+ ALOGE("Could not link death recipient to the HAL death");
+ return nullptr;
}
-
- // Under normal operation, we do not need to reset the HAL here. But in the case where system
- // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
- // have received events in the past. That means, that HAL could be in an inconsistent state
- // once it receives events from the newly created MotionClassifier.
- mEvents.push(ClassifierEvent::createHalResetEvent());
-
- {
- std::scoped_lock lock(mLock);
- if (mService) {
- ALOGE("MotionClassifier::%s should only be called once", __func__);
- }
- mService = service;
- }
- return true;
+ // Using 'new' to access a non-public constructor
+ return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
}
MotionClassifier::~MotionClassifier() {
@@ -195,14 +160,6 @@ MotionClassifier::~MotionClassifier() {
mHalThread.join();
}
-void MotionClassifier::ensureHalThread(const char* function) {
- if (DEBUG) {
- if (std::this_thread::get_id() != mHalThread.get_id()) {
- LOG_FATAL("Function %s should only be called from InputClassifier thread", function);
- }
- }
-}
-
/**
* Obtain the classification from the HAL for a given MotionEvent.
* Should only be called from the InputClassifier thread (mHalThread).
@@ -213,23 +170,7 @@ void MotionClassifier::ensureHalThread(const char* function) {
* To remove any possibility of negatively affecting the touch latency, the HAL
* is called from a dedicated thread.
*/
-void MotionClassifier::callInputClassifierHal() {
- ensureHalThread(__func__);
- const bool initialized = init();
- if (!initialized) {
- // MotionClassifier no longer useful.
- // Deliver death notification from a separate thread
- // because ~MotionClassifier may be invoked, which calls mHalThread.join()
- std::thread([deathRecipient = mDeathRecipient](){
- sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
- if (recipient != nullptr) {
- recipient->serviceDied(0 /*cookie*/, nullptr);
- }
- }).detach();
- return;
- }
- // From this point on, mService is guaranteed to be non-null.
-
+void MotionClassifier::processEvents() {
while (true) {
ClassifierEvent event = mEvents.pop();
bool halResponseOk = true;
@@ -383,24 +324,41 @@ void MotionClassifier::dump(std::string& dump) {
}
}
+// --- HalDeathRecipient
-// --- InputClassifier ---
+InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
-InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
- mListener(listener) {
- // The rest of the initialization is done in onFirstRef, because we need to obtain
- // an sp to 'this' in order to register for HAL death notifications
+void InputClassifier::HalDeathRecipient::serviceDied(
+ uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
+ sp<android::hidl::base::V1_0::IBase> service = who.promote();
+ if (service) {
+ service->unlinkToDeath(this);
+ }
+ mParent.setMotionClassifier(nullptr);
}
-void InputClassifier::onFirstRef() {
- if (!deepPressEnabled()) {
- // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
- // When MotionClassifier is null, InputClassifier will forward all events
- // to the next InputListener, unmodified.
- return;
+// --- InputClassifier ---
+
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
+ : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
+
+void InputClassifier::setMotionClassifierEnabled(bool enabled) {
+ if (enabled) {
+ ALOGI("Enabling motion classifier");
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+ mInitializeMotionClassifierThread = std::thread(
+ [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
+ "Create MotionClassifier");
+#endif
+ } else {
+ ALOGI("Disabling motion classifier");
+ setMotionClassifier(nullptr);
}
- std::scoped_lock lock(mLock);
- mMotionClassifier = std::make_unique<MotionClassifier>(this);
}
void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
@@ -441,15 +399,10 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
mListener->notifyDeviceReset(args);
}
-void InputClassifier::serviceDied(uint64_t /*cookie*/,
- const wp<android::hidl::base::V1_0::IBase>& who) {
+void InputClassifier::setMotionClassifier(
+ std::unique_ptr<MotionClassifierInterface> motionClassifier) {
std::scoped_lock lock(mLock);
- ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
- mMotionClassifier = nullptr;
- sp<android::hidl::base::V1_0::IBase> service = who.promote();
- if (service) {
- service->unlinkToDeath(this);
- }
+ mMotionClassifier = std::move(motionClassifier);
}
void InputClassifier::dump(std::string& dump) {
@@ -465,4 +418,10 @@ void InputClassifier::dump(std::string& dump) {
dump += "\n";
}
+InputClassifier::~InputClassifier() {
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 47e20dbf7..9ac8e2db2 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -19,8 +19,8 @@
#include <android-base/thread_annotations.h>
#include <utils/RefBase.h>
-#include <unordered_map>
#include <thread>
+#include <unordered_map>
#include "BlockingQueue.h"
#include "InputListener.h"
@@ -90,6 +90,7 @@ public:
*/
class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
public:
+ virtual void setMotionClassifierEnabled(bool enabled) = 0;
/**
* Dump the state of the input classifier.
* This method may be called on any thread (usually by the input manager).
@@ -113,23 +114,23 @@ protected:
*/
class MotionClassifier final : public MotionClassifierInterface {
public:
- /**
- * The deathRecipient will be subscribed to the HAL death. If the death recipient
- * owns MotionClassifier and receives HAL death, it should delete its copy of it.
- * The callback serviceDied will also be sent if the MotionClassifier itself fails
- * to initialize. If the MotionClassifier fails to initialize, it is not useful, and
- * should be deleted.
- * If no death recipient is supplied, then the registration step will be skipped, so there will
- * be no listeners registered for the HAL death. This is useful for testing
- * MotionClassifier in isolation.
+ /*
+ * Create an instance of MotionClassifier.
+ * The death recipient, if provided, will be subscribed to the HAL death.
+ * The death recipient could be used to destroy MotionClassifier.
+ *
+ * This function should be called asynchronously, because getService takes a long time.
*/
- explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr);
+ static std::unique_ptr<MotionClassifierInterface> create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient);
+
~MotionClassifier();
/**
* Classifies events asynchronously; that is, it doesn't block events on a classification,
- * but instead sends them over to the classifier HAL and after a classification is
- * determined, it then marks the next event it sees in the stream with it.
+ * but instead sends them over to the classifier HAL. After a classification of a specific
+ * event is determined, MotionClassifier then marks the next event in the stream with this
+ * classification.
*
* Therefore, it is acceptable to have the classifications be delayed by 1-2 events
* in a particular gesture.
@@ -141,15 +142,9 @@ public:
virtual void dump(std::string& dump) override;
private:
- /**
- * Initialize MotionClassifier.
- * Return true if initializaion is successful.
- */
- bool init();
- /**
- * Entity that will be notified of the HAL death (most likely InputClassifier).
- */
- wp<android::hardware::hidl_death_recipient> mDeathRecipient;
+ friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
+ explicit MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
// The events that need to be sent to the HAL.
BlockingQueue<ClassifierEvent> mEvents;
@@ -164,14 +159,9 @@ private:
*/
std::thread mHalThread;
/**
- * Print an error message if the caller is not on the InputClassifier thread.
- * Caller must supply the name of the calling function as __func__
+ * Process events and call the InputClassifier HAL
*/
- void ensureHalThread(const char* function);
- /**
- * Call the InputClassifier HAL
- */
- void callInputClassifierHal();
+ void processEvents();
/**
* Access to the InputClassifier HAL. May be null if init() hasn't completed yet.
* When init() successfully completes, mService is guaranteed to remain non-null and to not
@@ -223,19 +213,15 @@ private:
const char* getServiceStatus() REQUIRES(mLock);
};
-
/**
* Implementation of the InputClassifierInterface.
* Represents a separate stage of input processing. All of the input events go through this stage.
* Acts as a passthrough for all input events except for motion events.
* The events of motion type are sent to MotionClassifier.
*/
-class InputClassifier : public InputClassifierInterface,
- public android::hardware::hidl_death_recipient {
+class InputClassifier : public InputClassifierInterface {
public:
explicit InputClassifier(const sp<InputListenerInterface>& listener);
- // Some of the constructor logic is finished in onFirstRef
- virtual void onFirstRef() override;
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -243,17 +229,47 @@ public:
virtual void notifySwitch(const NotifySwitchArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override;
-
virtual void dump(std::string& dump) override;
+ ~InputClassifier();
+
+ // Called from InputManager
+ virtual void setMotionClassifierEnabled(bool enabled) override;
+
private:
// Protect access to mMotionClassifier, since it may become null via a hidl callback
std::mutex mLock;
- std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
// The next stage to pass input events to
sp<InputListenerInterface> mListener;
+
+ std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
+ std::thread mInitializeMotionClassifierThread;
+ /**
+ * Set the value of mMotionClassifier.
+ * This is called from 2 different threads:
+ * 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier
+ * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause
+ * mMotionClassifier to become nullptr.
+ */
+ void setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier);
+
+ /**
+ * The deathRecipient will call setMotionClassifier(null) when the HAL dies.
+ */
+ class HalDeathRecipient : public android::hardware::hidl_death_recipient {
+ public:
+ explicit HalDeathRecipient(InputClassifier& parent);
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+ InputClassifier& mParent;
+ };
+ // We retain a reference to death recipient, because the death recipient will be calling
+ // ~MotionClassifier if the HAL dies.
+ // If we don't retain a reference, and MotionClassifier is the only owner of the death
+ // recipient, the serviceDied call will cause death recipient to call its own destructor.
+ sp<HalDeathRecipient> mHalDeathRecipient;
};
} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index d4ab096bb..f7420be00 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -139,4 +139,8 @@ void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
mDispatcher->unregisterInputChannel(channel);
}
+void InputManager::setMotionClassifierEnabled(bool enabled) {
+ mClassifier->setMotionClassifierEnabled(enabled);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 32f7ae005..c75611f08 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -100,6 +100,8 @@ public:
virtual void registerInputChannel(const sp<InputChannel>& channel);
virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+ void setMotionClassifierEnabled(bool enabled);
+
private:
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 7cc17a221..4cddec50d 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -22,6 +22,9 @@
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
using namespace android::hardware::input;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::input::common::V1_0::Classification;
namespace android {
@@ -129,6 +132,49 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) {
ASSERT_EQ(args, outArgs);
}
+TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) {
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) {
+ mClassifier->setMotionClassifierEnabled(false);
+}
+
+/**
+ * Try to break it by calling setMotionClassifierEnabled multiple times.
+ */
+TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) {
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+/**
+ * A minimal implementation of IInputClassifier.
+ */
+struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier {
+ Return<Classification> classify(
+ const android::hardware::input::common::V1_0::MotionEvent& event) override {
+ return Classification::NONE;
+ };
+ Return<void> reset() override { return Void(); };
+ Return<void> resetDevice(int32_t deviceId) override { return Void(); };
+};
+
+/**
+ * An entity that will be subscribed to the HAL death.
+ */
+class TestDeathRecipient : public android::hardware::hidl_death_recipient {
+public:
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override{};
+};
+
// --- MotionClassifierTest ---
class MotionClassifierTest : public testing::Test {
@@ -136,7 +182,14 @@ protected:
std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
virtual void SetUp() override {
- mMotionClassifier = std::make_unique<MotionClassifier>();
+ mMotionClassifier = MotionClassifier::create(new TestDeathRecipient());
+ if (mMotionClassifier == nullptr) {
+ // If the device running this test does not have IInputClassifier service,
+ // use the test HAL instead.
+ // Using 'new' to access non-public constructor
+ mMotionClassifier =
+ std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal()));
+ }
}
};
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3206009c3..6bb250e7b 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -209,12 +209,6 @@ void SensorService::onFirstRef() {
registerSensor(new RotationVectorSensor(), !needRotationVector, true);
registerSensor(new OrientationSensor(), !needOrientation, true);
- bool needLinearAcceleration =
- (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
-
- registerSensor(new LinearAccelerationSensor(list, count),
- !needLinearAcceleration, true);
-
// virtual debugging sensors are not for user
registerSensor( new CorrectedGyroSensor(list, count), true, true);
registerSensor( new GyroDriftSensor(), true, true);
@@ -224,6 +218,11 @@ void SensorService::onFirstRef() {
bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0;
registerSensor(new GravitySensor(list, count), !needGravitySensor, true);
+ bool needLinearAcceleration =
+ (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
+ registerSensor(new LinearAccelerationSensor(list, count),
+ !needLinearAcceleration, true);
+
bool needGameRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) != 0;
registerSensor(new GameRotationVectorSensor(), !needGameRotationVector, true);
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 062f927eb..cf7c15d63 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -462,6 +462,7 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
if (result != NO_ERROR) {
ALOGE("[%s] Timed out waiting on callback", mName.string());
+ break;
}
}
@@ -496,6 +497,7 @@ void BufferQueueLayer::onFrameReplaced(const BufferItem& item) {
status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
if (result != NO_ERROR) {
ALOGE("[%s] Timed out waiting on callback", mName.string());
+ break;
}
}
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 07fdead31..80da15427 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -200,9 +200,10 @@ RegionSamplingThread::~RegionSamplingThread() {
void RegionSamplingThread::addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) {
- wp<Layer> stopLayer = stopLayerHandle != nullptr
- ? static_cast<Layer::Handle*>(stopLayerHandle.get())->owner
- : nullptr;
+ wp<Layer> stopLayer;
+ if (stopLayerHandle != nullptr && stopLayerHandle->localBinder() != nullptr) {
+ stopLayer = static_cast<Layer::Handle*>(stopLayerHandle.get())->owner;
+ }
sp<IBinder> asBinder = IInterface::asBinder(listener);
asBinder->linkToDeath(this);
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 8a2604f4a..9fa2bbc96 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -96,7 +96,6 @@ PhaseOffsets::PhaseOffsets() {
highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs,
highFpsLateAppOffsetNs};
- mOffsets.insert({RefreshRateType::POWER_SAVING, defaultOffsets});
mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index d7300583c..d8137085d 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -34,10 +34,9 @@ namespace scheduler {
*/
class RefreshRateConfigs {
public:
- // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
- // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
+ // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance
// is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
- enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
+ enum class RefreshRateType { DEFAULT, PERFORMANCE };
struct RefreshRate {
// This config ID corresponds to the position of the config in the vector that is stored
@@ -47,26 +46,57 @@ public:
std::string name;
// Refresh rate in frames per second, rounded to the nearest integer.
uint32_t fps = 0;
- // config Id (returned from HWC2::Display::Config::getId())
- hwc2_config_t id;
+ // Vsync period in nanoseconds.
+ nsecs_t vsyncPeriod;
+ // Hwc config Id (returned from HWC2::Display::Config::getId())
+ hwc2_config_t hwcId;
};
+ // Returns true if this device is doing refresh rate switching. This won't change at runtime.
+ bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }
+
+ // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
+ // from multiple threads. This can only be called if refreshRateSwitching() returns true.
// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
// baking them in.
- const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const {
- return mRefreshRates;
+ const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const {
+ LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
+ return mRefreshRateMap;
}
- std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const {
- const auto& refreshRate = mRefreshRates.find(type);
- if (refreshRate != mRefreshRates.end()) {
+
+ const RefreshRate& getRefreshRateFromType(RefreshRateType type) const {
+ if (!mRefreshRateSwitchingSupported) {
+ return getCurrentRefreshRate().second;
+ } else {
+ auto refreshRate = mRefreshRateMap.find(type);
+ LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
return refreshRate->second;
}
- return nullptr;
}
- RefreshRateType getRefreshRateType(hwc2_config_t id) const {
- for (const auto& [type, refreshRate] : mRefreshRates) {
- if (refreshRate->id == id) {
+ std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const {
+ int currentConfig = mCurrentConfig;
+ if (mRefreshRateSwitchingSupported) {
+ for (const auto& [type, refresh] : mRefreshRateMap) {
+ if (refresh.configId == currentConfig) {
+ return {type, refresh};
+ }
+ }
+ LOG_ALWAYS_FATAL();
+ }
+ return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
+ }
+
+ const RefreshRate& getRefreshRateFromConfigId(int configId) const {
+ LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
+ return mRefreshRates[configId];
+ }
+
+ RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
+ if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
+
+ for (const auto& [type, refreshRate] : mRefreshRateMap) {
+ if (refreshRate.hwcId == hwcId) {
return type;
}
}
@@ -74,64 +104,102 @@ public:
return RefreshRateType::DEFAULT;
}
- void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
- mRefreshRates.clear();
+ void setCurrentConfig(int config) {
+ LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
+ mCurrentConfig = config;
+ }
- // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
- mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
- std::make_shared<RefreshRate>(
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0,
- HWC2_SCREEN_OFF_CONFIG_ID}));
+ struct InputConfig {
+ hwc2_config_t hwcId = 0;
+ nsecs_t vsyncPeriod = 0;
+ };
- if (configs.size() < 1) {
- ALOGE("Device does not have valid configs. Config size is 0.");
- return;
+ RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+ int currentConfig) {
+ init(refreshRateSwitching, configs, currentConfig);
+ }
+
+ RefreshRateConfigs(bool refreshRateSwitching,
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+ int currentConfig) {
+ std::vector<InputConfig> inputConfigs;
+ for (const auto& config : configs) {
+ inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
}
+ init(refreshRateSwitching, inputConfigs, currentConfig);
+ }
+
+private:
+ void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
+ int currentConfig) {
+ mRefreshRateSwitchingSupported = refreshRateSwitching;
+ LOG_ALWAYS_FATAL_IF(configs.empty());
+ LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
+ mCurrentConfig = currentConfig;
+
+ auto buildRefreshRate = [&](int configId) -> RefreshRate {
+ const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
+ const float fps = 1e9 / vsyncPeriod;
+ return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
+ vsyncPeriod, configs[configId].hwcId};
+ };
- // Create a map between config index and vsync period. This is all the info we need
- // from the configs.
- std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
for (int i = 0; i < configs.size(); ++i) {
- configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
+ mRefreshRates.push_back(buildRefreshRate(i));
}
- std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
- [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
- return a.second > b.second;
- });
+ if (!mRefreshRateSwitchingSupported) return;
- // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
- nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
- if (vsyncPeriod != 0) {
- const float fps = 1e9 / vsyncPeriod;
- const int configId = configIdToVsyncPeriod[0].first;
- mRefreshRates.emplace(RefreshRateType::DEFAULT,
- std::make_shared<RefreshRate>(
- RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps),
- configs.at(configId)->getId()}));
- }
+ auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
+ if (configs.size() < 2) {
+ return {};
+ }
- if (configs.size() < 2) {
+ std::vector<const RefreshRate*> sortedRefreshRates;
+ for (const auto& refreshRate : mRefreshRates) {
+ sortedRefreshRates.push_back(&refreshRate);
+ }
+ std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
+ [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
+ return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ });
+
+ // When the configs are ordered by the resync rate, we assume that
+ // the first one is DEFAULT and the second one is PERFORMANCE,
+ // i.e. the higher rate.
+ if (sortedRefreshRates[0]->vsyncPeriod == 0 ||
+ sortedRefreshRates[1]->vsyncPeriod == 0) {
+ return {};
+ }
+
+ return std::pair<int, int>(sortedRefreshRates[0]->configId,
+ sortedRefreshRates[1]->configId);
+ };
+
+ auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
+ if (!defaultAndPerfConfigs) {
+ mRefreshRateSwitchingSupported = false;
return;
}
- // When the configs are ordered by the resync rate. We assume that the second one is
- // PERFORMANCE, eg. the higher rate.
- vsyncPeriod = configIdToVsyncPeriod[1].second;
- if (vsyncPeriod != 0) {
- const float fps = 1e9 / vsyncPeriod;
- const int configId = configIdToVsyncPeriod[1].first;
- mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
- std::make_shared<RefreshRate>(
- RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps),
- configs.at(configId)->getId()}));
- }
+ mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
+ mRefreshRateMap[RefreshRateType::PERFORMANCE] =
+ mRefreshRates[defaultAndPerfConfigs->second];
}
-private:
- std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
+ // Whether this device is doing refresh rate switching or not. This must not change after this
+ // object is initialized.
+ bool mRefreshRateSwitchingSupported;
+ // The list of refresh rates, indexed by display config ID. This must not change after this
+ // object is initialized.
+ std::vector<RefreshRate> mRefreshRates;
+ // The mapping of refresh rate type to RefreshRate. This must not change after this object is
+ // initialized.
+ std::map<RefreshRateType, RefreshRate> mRefreshRateMap;
+ // The ID of the current config. This will change at runtime. This is set by SurfaceFlinger on
+ // the main thread, and read by the Scheduler (and other objects) on other threads, so it's
+ // atomic.
+ std::atomic<int> mCurrentConfig;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 7e7c6307a..947eb08d9 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -41,21 +41,18 @@ class RefreshRateStats {
static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
public:
- RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats)
- : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {}
-
- // Sets power mode. We only collect the information when the power mode is not
- // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
- // on config mode.
+ RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
+ int currentConfigMode, int currentPowerMode)
+ : mRefreshRateConfigs(refreshRateConfigs),
+ mTimeStats(timeStats),
+ mCurrentConfigMode(currentConfigMode),
+ mCurrentPowerMode(currentPowerMode) {}
+
+ // Sets power mode.
void setPowerMode(int mode) {
if (mCurrentPowerMode == mode) {
return;
}
- // If power mode is normal, the time is going to be recorded under config modes.
- if (mode == HWC_POWER_MODE_NORMAL) {
- mCurrentPowerMode = mode;
- return;
- }
flushTime();
mCurrentPowerMode = mode;
}
@@ -79,16 +76,15 @@ public:
flushTime();
std::unordered_map<std::string, int64_t> totalTime;
- for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
- int64_t totalTimeForConfig = 0;
- if (!config) {
- continue;
- }
- if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
- totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
- }
- totalTime[config->name] = totalTimeForConfig;
+ // Multiple configs may map to the same name, e.g. "60fps". Add the
+ // times for such configs together.
+ for (const auto& [config, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0;
+ }
+ for (const auto& [config, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
}
+ totalTime["ScreenOff"] = mScreenOffTime;
return totalTime;
}
@@ -104,32 +100,26 @@ public:
}
private:
- void flushTime() {
- // Normal power mode is counted under different config modes.
- if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
- flushTimeForMode(mCurrentConfigMode);
- } else {
- flushTimeForMode(SCREEN_OFF_CONFIG_ID);
- }
- }
-
// Calculates the time that passed in ms between the last time we recorded time and the time
// this method was called.
- void flushTimeForMode(int mode) {
+ void flushTime() {
nsecs_t currentTime = systemTime();
nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
int64_t timeElapsedMs = ns2ms(timeElapsed);
mPreviousRecordedTime = currentTime;
- mConfigModesTotalTime[mode] += timeElapsedMs;
- for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
- if (!config) {
- continue;
- }
- if (config->configId == mode) {
- mTimeStats.recordRefreshRate(config->fps, timeElapsed);
+ uint32_t fps = 0;
+ if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+ // Normal power mode is counted under different config modes.
+ if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) {
+ mConfigModesTotalTime[mCurrentConfigMode] = 0;
}
+ mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
+ fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps;
+ } else {
+ mScreenOffTime += timeElapsedMs;
}
+ mTimeStats.recordRefreshRate(fps, timeElapsed);
}
// Formats the time in milliseconds into easy to read format.
@@ -149,10 +139,11 @@ private:
// Aggregate refresh rate statistics for telemetry.
TimeStats& mTimeStats;
- int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID;
- int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+ int mCurrentConfigMode;
+ int32_t mCurrentPowerMode;
- std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+ std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
+ int64_t mScreenOffTime = 0;
nsecs_t mPreviousRecordedTime = systemTime();
};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f8bd95872..0b4337839 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -133,7 +133,6 @@ Scheduler::~Scheduler() {
sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
- ResyncCallback resyncCallback,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
const int64_t id = sNextId++;
ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
@@ -143,8 +142,7 @@ sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
offsetThresholdForNextVsync, std::move(interceptCallback));
auto eventThreadConnection =
- createConnectionInternal(eventThread.get(), std::move(resyncCallback),
- ISurfaceComposer::eConfigChangedSuppress);
+ createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
mConnections.emplace(id,
std::make_unique<Connection>(new ConnectionHandle(id),
eventThreadConnection,
@@ -164,17 +162,15 @@ std::unique_ptr<EventThread> Scheduler::makeEventThread(
}
sp<EventThreadConnection> Scheduler::createConnectionInternal(
- EventThread* eventThread, ResyncCallback&& resyncCallback,
- ISurfaceComposer::ConfigChanged configChanged) {
- return eventThread->createEventConnection(std::move(resyncCallback), configChanged);
+ EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
+ return eventThread->createEventConnection([&] { resync(); }, configChanged);
}
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
- const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback,
+ const sp<Scheduler::ConnectionHandle>& handle,
ISurfaceComposer::ConfigChanged configChanged) {
RETURN_VALUE_IF_INVALID(nullptr);
- return createConnectionInternal(mConnections[handle->id]->thread.get(),
- std::move(resyncCallback), configChanged);
+ return createConnectionInternal(mConnections[handle->id]->thread.get(), configChanged);
}
EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -264,23 +260,15 @@ void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
setVsyncPeriod(period);
}
-ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
- std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
- return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
- if (const auto vsync = ptr.lock()) {
- vsync->resync(getVsyncPeriod);
- }
- };
-}
-
-void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
+void Scheduler::resync() {
static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
const nsecs_t now = systemTime();
- const nsecs_t last = lastResyncTime.exchange(now);
+ const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
+ resyncToHardwareVsync(false,
+ mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
}
}
@@ -338,15 +326,19 @@ void Scheduler::dumpPrimaryDispSync(std::string& result) const {
std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
std::string const& name, int windowType) {
- RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
- ? RefreshRateType::DEFAULT
- : RefreshRateType::PERFORMANCE;
-
- const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
- const uint32_t performanceFps = (refreshRate) ? refreshRate->fps : 0;
-
- const auto defaultRefreshRate = mRefreshRateConfigs.getRefreshRate(RefreshRateType::DEFAULT);
- const uint32_t defaultFps = (defaultRefreshRate) ? defaultRefreshRate->fps : 0;
+ uint32_t defaultFps, performanceFps;
+ if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
+ defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
+ performanceFps =
+ mRefreshRateConfigs
+ .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
+ ? RefreshRateType::DEFAULT
+ : RefreshRateType::PERFORMANCE)
+ .fps;
+ } else {
+ defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
+ performanceFps = defaultFps;
+ }
return mLayerHistory.createLayer(name, defaultFps, performanceFps);
}
@@ -398,17 +390,6 @@ void Scheduler::setChangeRefreshRateCallback(
mChangeRefreshRateCallback = changeRefreshRateCallback;
}
-void Scheduler::setGetCurrentRefreshRateTypeCallback(
- const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateTypeCallback) {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- mGetCurrentRefreshRateTypeCallback = getCurrentRefreshRateTypeCallback;
-}
-
-void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- mGetVsyncPeriod = getVsyncPeriod;
-}
-
void Scheduler::updateFrameSkipping(const int64_t skipCount) {
ATRACE_INT("FrameSkipCount", skipCount);
if (mSkipCount != skipCount) {
@@ -460,14 +441,12 @@ void Scheduler::resetTimerCallback() {
void Scheduler::resetKernelTimerCallback() {
ATRACE_INT("ExpiredKernelIdleTimer", 0);
- std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mGetVsyncPeriod && mGetCurrentRefreshRateTypeCallback) {
+ const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+ if (refreshRate.first == RefreshRateType::PERFORMANCE) {
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
- if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) {
- resyncToHardwareVsync(true, mGetVsyncPeriod());
- }
+ resyncToHardwareVsync(true, refreshRate.second.vsyncPeriod);
}
}
@@ -497,15 +476,13 @@ void Scheduler::expiredDisplayPowerTimerCallback() {
}
void Scheduler::expiredKernelTimerCallback() {
- std::lock_guard<std::mutex> lock(mCallbackLock);
ATRACE_INT("ExpiredKernelIdleTimer", 1);
- if (mGetCurrentRefreshRateTypeCallback) {
- if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) {
- // Disable HW Vsync if the timer expired, as we don't need it
- // enabled if we're not pushing frames, and if we're in PERFORMANCE
- // mode then we'll need to re-update the DispSync model anyways.
- disableHardwareVsync(false);
- }
+ const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+ if (refreshRate.first != RefreshRateType::PERFORMANCE) {
+ // Disable HW Vsync if the timer expired, as we don't need it
+ // enabled if we're not pushing frames, and if we're in PERFORMANCE
+ // mode then we'll need to re-update the DispSync model anyways.
+ disableHardwareVsync(false);
}
}
@@ -540,6 +517,10 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO
}
Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+ if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
+ return RefreshRateType::DEFAULT;
+ }
+
// HDR content is not supported on PERFORMANCE mode
if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
return RefreshRateType::DEFAULT;
@@ -567,16 +548,12 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
}
// Content detection is on, find the appropriate refresh rate with minimal error
- auto begin = mRefreshRateConfigs.getRefreshRates().cbegin();
+ auto begin = mRefreshRateConfigs.getRefreshRateMap().cbegin();
- // Skip POWER_SAVING config as it is not a real config
- if (begin->first == RefreshRateType::POWER_SAVING) {
- ++begin;
- }
- auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRates().cend(),
+ auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRateMap().cend(),
[rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
- return std::abs(l.second->fps - static_cast<float>(rate)) <
- std::abs(r.second->fps - static_cast<float>(rate));
+ return std::abs(l.second.fps - static_cast<float>(rate)) <
+ std::abs(r.second.fps - static_cast<float>(rate));
});
RefreshRateType currRefreshRateType = iter->first;
@@ -584,11 +561,11 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
// 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
// align well with both
constexpr float MARGIN = 0.05f;
- float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
+ float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps /
float(mContentRefreshRate);
if (std::abs(std::round(ratio) - ratio) > MARGIN) {
- while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
- ratio = iter->second->fps / float(mContentRefreshRate);
+ while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
+ ratio = iter->second.fps / float(mContentRefreshRate);
if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
currRefreshRateType = iter->first;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 38184570c..4b21dadde 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -49,9 +49,7 @@ public:
}
using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
- using GetCurrentRefreshRateTypeCallback = std::function<RefreshRateType()>;
using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
- using GetVsyncPeriod = std::function<nsecs_t()>;
// Enum to indicate whether to start the transaction early, or at vsync time.
enum class TransactionStart { EARLY, NORMAL };
@@ -81,16 +79,6 @@ public:
const std::unique_ptr<EventThread> thread;
};
- // Stores per-display state about VSYNC.
- struct VsyncState {
- explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {}
-
- void resync(const GetVsyncPeriod&);
-
- Scheduler& scheduler;
- std::atomic<nsecs_t> lastResyncTime = 0;
- };
-
explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
const scheduler::RefreshRateConfigs& refreshRateConfig);
@@ -98,12 +86,11 @@ public:
/** Creates an EventThread connection. */
sp<ConnectionHandle> createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
- nsecs_t offsetThresholdForNextVsync, ResyncCallback,
+ nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback);
sp<IDisplayEventConnection> createDisplayEventConnection(
- const sp<ConnectionHandle>& handle, ResyncCallback,
- ISurfaceComposer::ConfigChanged configChanged);
+ const sp<ConnectionHandle>& handle, ISurfaceComposer::ConfigChanged configChanged);
// Getter methods.
EventThread* getEventThread(const sp<ConnectionHandle>& handle);
@@ -143,8 +130,7 @@ public:
// no-op.
// The period is the vsync period from the current display configuration.
void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
- // Creates a callback for resyncing.
- ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod);
+ void resync();
void setRefreshSkipCount(int count);
// Passes a vsync sample to DispSync. periodFlushed will be true if
// DispSync detected that the vsync period changed, and false otherwise.
@@ -167,9 +153,6 @@ public:
void updateFpsBasedOnContent();
// Callback that gets invoked when Scheduler wants to change the refresh rate.
void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback);
- void setGetCurrentRefreshRateTypeCallback(
- const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateType);
- void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod);
// Returns whether idle timer is enabled or not
bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
@@ -209,7 +192,7 @@ private:
enum class DisplayPowerTimerState { EXPIRED, RESET };
// Creates a connection on the given EventThread and forwards the given callbacks.
- sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
+ sp<EventThreadConnection> createConnectionInternal(EventThread*,
ISurfaceComposer::ConfigChanged);
nsecs_t calculateAverage() const;
@@ -264,7 +247,8 @@ private:
std::mutex mHWVsyncLock;
bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock);
bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock);
- const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)};
+
+ std::atomic<nsecs_t> mLastResyncTime = 0;
std::unique_ptr<DispSync> mPrimaryDispSync;
std::unique_ptr<EventControlThread> mEventControlThread;
@@ -300,9 +284,7 @@ private:
std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer;
std::mutex mCallbackLock;
- GetCurrentRefreshRateTypeCallback mGetCurrentRefreshRateTypeCallback GUARDED_BY(mCallbackLock);
ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
- GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index ac10f83ad..f193553ea 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -30,12 +30,6 @@ using namespace std::chrono_literals;
// about layers.
static constexpr size_t ARRAY_SIZE = 30;
-// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently
-// the config is not visible to SF, and is completely maintained by HWC. However, we would
-// still like to keep track of time when the device is in this config.
-static constexpr int SCREEN_OFF_CONFIG_ID = -1;
-static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff;
-
// This number is used when we try to determine how long do we keep layer information around
// before we remove it. It is also used to determine how long the layer stays relevant.
// This time period captures infrequent updates when playing YouTube video with static image,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8f3fb242d..e2ba4de6d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -574,14 +574,16 @@ void SurfaceFlinger::bootFinished()
readPersistentProperties();
mBootStage = BootStage::FINISHED;
- // set the refresh rate according to the policy
- const auto& performanceRefreshRate =
- mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE);
+ if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+ // set the refresh rate according to the policy
+ const auto& performanceRefreshRate =
+ mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE);
- if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) {
- setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
- } else {
- setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::Changed);
+ if (isDisplayConfigAllowed(performanceRefreshRate.configId)) {
+ setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
+ } else {
+ setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::Changed);
+ }
}
}));
}
@@ -624,32 +626,6 @@ void SurfaceFlinger::init() {
ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
Mutex::Autolock _l(mStateLock);
- // start the EventThread
- mScheduler =
- getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
- mRefreshRateConfigs);
- auto resyncCallback =
- mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
- mAppConnectionHandle =
- mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
- resyncCallback,
- impl::EventThread::InterceptVSyncsCallback());
- mSfConnectionHandle =
- mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf,
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
- resyncCallback, [this](nsecs_t timestamp) {
- mInterceptor->saveVSyncEvent(timestamp);
- });
-
- mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
- mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
- mSfConnectionHandle.get());
-
- mRegionSamplingThread =
- new RegionSamplingThread(*this, *mScheduler,
- RegionSamplingThread::EnvironmentTimingTunables());
// Get a RenderEngine for the given display / config (can't fail)
int32_t renderEngineFeature = 0;
@@ -720,37 +696,6 @@ void SurfaceFlinger::init() {
ALOGE("Run StartPropertySetThread failed!");
}
- mScheduler->setChangeRefreshRateCallback(
- [this](RefreshRateType type, Scheduler::ConfigEvent event) {
- Mutex::Autolock lock(mStateLock);
- setRefreshRateTo(type, event);
- });
- mScheduler->setGetCurrentRefreshRateTypeCallback([this] {
- Mutex::Autolock lock(mStateLock);
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display) {
- // If we don't have a default display the fallback to the default
- // refresh rate type
- return RefreshRateType::DEFAULT;
- }
-
- const int configId = display->getActiveConfig();
- for (const auto& [type, refresh] : mRefreshRateConfigs.getRefreshRates()) {
- if (refresh && refresh->configId == configId) {
- return type;
- }
- }
- // This should never happen, but just gracefully fallback to default.
- return RefreshRateType::DEFAULT;
- });
- mScheduler->setGetVsyncPeriodCallback([this] {
- Mutex::Autolock lock(mStateLock);
- return getVsyncPeriod();
- });
-
- mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
- mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
-
ALOGV("Done initializing");
}
@@ -904,7 +849,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
info.xdpi = xdpi;
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId());
+ const auto refreshRateType =
+ mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId());
const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
info.appVsyncOffset = offset.late.app;
@@ -1006,7 +952,8 @@ void SurfaceFlinger::setActiveConfigInternal() {
}
std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId);
+ mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId);
+ mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
display->setActiveConfig(mUpcomingActiveConfig.configId);
@@ -1285,9 +1232,6 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
return;
}
- auto resyncCallback =
- mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
// TODO(b/128863962): Part of the Injector should be refactored, so that it
// can be passed to Scheduler.
if (enable) {
@@ -1299,11 +1243,11 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
impl::EventThread::InterceptVSyncsCallback(),
"injEventThread");
}
- mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback));
+ mEventQueue->setEventThread(mInjectorEventThread.get(), [&] { mScheduler->resync(); });
} else {
ALOGV("VSync Injections disabled");
mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle),
- std::move(resyncCallback));
+ [&] { mScheduler->resync(); });
}
mInjectVSyncs = enable;
@@ -1414,16 +1358,10 @@ status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) {
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
- auto resyncCallback = mScheduler->makeResyncCallback([this] {
- Mutex::Autolock lock(mStateLock);
- return getVsyncPeriod();
- });
-
const auto& handle =
vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
- return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
- configChanged);
+ return mScheduler->createDisplayEventConnection(handle, configChanged);
}
// ----------------------------------------------------------------------------
@@ -1520,13 +1458,8 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::Co
ATRACE_CALL();
// Don't do any updating if the current fps is the same as the new one.
- const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate);
- if (!refreshRateConfig) {
- ALOGV("Skipping refresh rate change request for unsupported rate.");
- return;
- }
-
- const int desiredConfigId = refreshRateConfig->configId;
+ const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate);
+ const int desiredConfigId = refreshRateConfig.configId;
if (!isDisplayConfigAllowed(desiredConfigId)) {
ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
@@ -2631,6 +2564,9 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() {
if (event.connection == HWC2::Connection::Connected) {
if (!mPhysicalDisplayTokens.count(info->id)) {
ALOGV("Creating display %s", to_string(info->id).c_str());
+ if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+ initScheduler(info->id);
+ }
mPhysicalDisplayTokens[info->id] = new BBinder();
DisplayDeviceState state;
state.displayId = info->id;
@@ -3091,6 +3027,55 @@ void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
layer->releasePendingBuffer(systemTime());
}
+void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
+ if (mScheduler) {
+ // In practice it's not allowed to hotplug in/out the primary display once it's been
+ // connected during startup, but some tests do it, so just warn and return.
+ ALOGW("Can't re-init scheduler");
+ return;
+ }
+
+ int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId);
+ mRefreshRateConfigs =
+ std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
+ getHwComposer().getConfigs(
+ primaryDisplayId),
+ currentConfig);
+ mRefreshRateStats =
+ std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats,
+ currentConfig, HWC_POWER_MODE_OFF);
+ mRefreshRateStats->setConfigMode(currentConfig);
+
+ // start the EventThread
+ mScheduler =
+ getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
+ *mRefreshRateConfigs);
+ mAppConnectionHandle =
+ mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
+ mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ impl::EventThread::InterceptVSyncsCallback());
+ mSfConnectionHandle =
+ mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf,
+ mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ [this](nsecs_t timestamp) {
+ mInterceptor->saveVSyncEvent(timestamp);
+ });
+
+ mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+ mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
+ mSfConnectionHandle.get());
+
+ mRegionSamplingThread =
+ new RegionSamplingThread(*this, *mScheduler,
+ RegionSamplingThread::EnvironmentTimingTunables());
+
+ mScheduler->setChangeRefreshRateCallback(
+ [this](RefreshRateType type, Scheduler::ConfigEvent event) {
+ Mutex::Autolock lock(mStateLock);
+ setRefreshRateTo(type, event);
+ });
+}
+
void SurfaceFlinger::commitTransaction()
{
if (!mLayersPendingRemoval.isEmpty()) {
@@ -4611,7 +4596,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int
if (display->isPrimary()) {
mTimeStats->setPowerMode(mode);
- mRefreshRateStats.setPowerMode(mode);
+ mRefreshRateStats->setPowerMode(mode);
mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL);
}
@@ -4779,15 +4764,14 @@ void SurfaceFlinger::dumpVSync(std::string& result) const {
mUseSmart90ForVideo ? "on" : "off");
StringAppendF(&result, "Allowed Display Configs: ");
for (int32_t configId : mAllowedDisplayConfigs) {
- for (auto refresh : mRefreshRateConfigs.getRefreshRates()) {
- if (refresh.second && refresh.second->configId == configId) {
- StringAppendF(&result, "%dHz, ", refresh.second->fps);
- }
- }
+ StringAppendF(&result, "%" PRIu32 " Hz, ",
+ mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps);
}
StringAppendF(&result, "(config override by backdoor: %s)\n\n",
mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
mScheduler->dump(mAppConnectionHandle, result);
+ StringAppendF(&result, "+ Refresh rate switching: %s\n",
+ mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off");
}
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -5152,7 +5136,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co
result.append("\nScheduler state:\n");
result.append(mScheduler->doDump() + "\n");
StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
- result.append(mRefreshRateStats.doDump() + "\n");
+ result.append(mRefreshRateStats->doDump() + "\n");
result.append(mTimeStats->miniDump());
result.append("\n");
@@ -5623,7 +5607,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
case 1034: {
// TODO(b/129297325): expose this via developer menu option
n = data.readInt32();
- if (n && !mRefreshRateOverlay) {
+ if (n && !mRefreshRateOverlay &&
+ mRefreshRateConfigs->refreshRateSwitchingSupported()) {
RefreshRateType type;
{
std::lock_guard<std::mutex> lock(mActiveConfigLock);
@@ -6201,25 +6186,6 @@ void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& disp
}
}
-void SurfaceFlinger::setPreferredDisplayConfig() {
- const auto& type = mScheduler->getPreferredRefreshRateType();
- const auto& config = mRefreshRateConfigs.getRefreshRate(type);
- if (config && isDisplayConfigAllowed(config->configId)) {
- ALOGV("switching to Scheduler preferred config %d", config->configId);
- setDesiredActiveConfig({type, config->configId, Scheduler::ConfigEvent::Changed});
- } else {
- // Set the highest allowed config by iterating backwards on available refresh rates
- const auto& refreshRates = mRefreshRateConfigs.getRefreshRates();
- for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
- if (iter->second && isDisplayConfigAllowed(iter->second->configId)) {
- ALOGV("switching to allowed config %d", iter->second->configId);
- setDesiredActiveConfig({iter->first, iter->second->configId,
- Scheduler::ConfigEvent::Changed});
- }
- }
- }
-}
-
void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
const std::vector<int32_t>& allowedConfigs) {
if (!display->isPrimary()) {
@@ -6241,7 +6207,29 @@ void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& d
mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
display->getActiveConfig());
- setPreferredDisplayConfig();
+ if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+ const auto& type = mScheduler->getPreferredRefreshRateType();
+ const auto& config = mRefreshRateConfigs->getRefreshRateFromType(type);
+ if (isDisplayConfigAllowed(config.configId)) {
+ ALOGV("switching to Scheduler preferred config %d", config.configId);
+ setDesiredActiveConfig({type, config.configId, Scheduler::ConfigEvent::Changed});
+ } else {
+ // Set the highest allowed config by iterating backwards on available refresh rates
+ const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap();
+ for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
+ if (isDisplayConfigAllowed(iter->second.configId)) {
+ ALOGV("switching to allowed config %d", iter->second.configId);
+ setDesiredActiveConfig(
+ {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed});
+ break;
+ }
+ }
+ }
+ } else if (!isDisplayConfigAllowed(display->getActiveConfig())) {
+ ALOGV("switching to config %d", allowedConfigs[0]);
+ setDesiredActiveConfig(
+ {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed});
+ }
}
#ifdef QCOM_UM_FAMILY
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 08d9dd36a..4340336f3 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -536,9 +536,6 @@ private:
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
- // Query the Scheduler or allowed display configs list for a matching config, and set it
- void setPreferredDisplayConfig() REQUIRES(mStateLock);
-
// called on the main thread in response to setAllowedDisplayConfigs()
void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
const std::vector<int32_t>& allowedConfigs)
@@ -560,6 +557,7 @@ private:
void commitInputWindowCommands() REQUIRES(mStateLock);
void setInputWindowsFinished();
void updateCursorAsync();
+ void initScheduler(DisplayId primaryDisplayId);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -1140,8 +1138,8 @@ private:
sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
- scheduler::RefreshRateConfigs mRefreshRateConfigs;
- scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats};
+ std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+ std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
// All configs are allowed if the set is empty.
using DisplayConfigs = std::set<int32_t>;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 768074a6c..b4716eb61 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -226,6 +226,14 @@ int64_t color_space_agnostic_dataspace(Dataspace defaultValue) {
return static_cast<int64_t>(defaultValue);
}
+bool refresh_rate_switching(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::refresh_rate_switching();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
int32_t set_idle_timer_ms(int32_t defaultValue) {
auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 5f88322f7..e394ccab7 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -73,6 +73,8 @@ int32_t wcg_composition_pixel_format(
int64_t color_space_agnostic_dataspace(
android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+bool refresh_rate_switching(bool defaultValue);
+
int32_t set_idle_timer_ms(int32_t defaultValue);
int32_t set_touch_timer_ms(int32_t defaultValue);
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 74baf3773..000f21c54 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -15,7 +15,7 @@
module: "android.sysprop.SurfaceFlingerProperties"
owner: Platform
-# The following two propertiess define (respectively):
+# The following two properties define (respectively):
#
# - The phase offset between hardware vsync and when apps are woken up by the
# Choreographer callback
@@ -301,6 +301,18 @@ prop {
prop_name: "ro.surface_flinger.display_primary_white"
}
+# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate
+# switching on the device, e.g. to switch between 60 and 90 Hz. The settings
+# below that are related to refresh rate switching will only have an effect if
+# refresh_rate_switching is enabled.
+prop {
+ api_name: "refresh_rate_switching"
+ type: Boolean
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.refresh_rate_switching"
+}
+
# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
# refresh rate. Setting this property to 0 means there is no timer.
diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt
index 79854b36a..2e804c8a3 100644
--- a/services/surfaceflinger/sysprop/api/system-current.txt
+++ b/services/surfaceflinger/sysprop/api/system-current.txt
@@ -17,6 +17,7 @@ package android.sysprop {
method public static java.util.Optional<java.lang.Long> max_virtual_display_dimension();
method public static java.util.Optional<java.lang.Long> present_time_offset_from_vsync_ns();
method public static java.util.Optional<android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values> primary_display_orientation();
+ method public static java.util.Optional<java.lang.Boolean> refresh_rate_switching();
method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework();
method public static java.util.Optional<java.lang.Integer> set_display_power_timer_ms();
method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms();
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4f8ed1ae1..349dd3f2f 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -125,7 +125,17 @@ public:
}
void setupScheduler() {
- mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+ mFlinger.mutableRefreshRateConfigs() =
+ std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false,
+ configs,
+ /*currentConfig=*/0);
+ mFlinger.mutableRefreshRateStats() =
+ std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(),
+ *mFlinger.mutableTimeStats(),
+ /*currentConfig=*/0,
+ /*powerMode=*/HWC_POWER_MODE_OFF);
+ mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs());
mScheduler->mutableEventControlThread().reset(mEventControlThread);
mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 5f58e7dce..f40996eec 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -29,6 +29,7 @@
#include <ui/DebugUtils.h>
#include "DisplayIdentificationTest.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -179,7 +180,16 @@ DisplayTransactionTest::~DisplayTransactionTest() {
}
void DisplayTransactionTest::setupScheduler() {
- mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+ mFlinger.mutableRefreshRateConfigs() =
+ std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfig=*/0);
+ mFlinger.mutableRefreshRateStats() =
+ std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(),
+ *mFlinger.mutableTimeStats(),
+ /*currentConfig=*/0,
+ /*powerMode=*/HWC_POWER_MODE_OFF);
+ mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs());
mScheduler->mutableEventControlThread().reset(mEventControlThread);
mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 5067fe890..f315a8a86 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -23,7 +23,6 @@
#include "DisplayHardware/HWC2.h"
#include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplay.h"
using namespace std::chrono_literals;
using testing::_;
@@ -50,9 +49,8 @@ protected:
ASSERT_EQ(left.configId, right.configId);
ASSERT_EQ(left.name, right.name);
ASSERT_EQ(left.fps, right.fps);
+ ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod);
}
-
- RefreshRateConfigs mConfigs;
};
RefreshRateConfigsTest::RefreshRateConfigsTest() {
@@ -71,101 +69,39 @@ namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
- std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
- mConfigs.populate(displayConfigs);
-
- // We always store a configuration for screen off.
- const auto& rates = mConfigs.getRefreshRates();
- ASSERT_EQ(1, rates.size());
- const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
- ASSERT_NE(rates.end(), powerSavingRate);
- ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
- ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
-
- RefreshRate expectedConfig =
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
- assertRatesEqual(expectedConfig, *powerSavingRate->second);
-
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
- ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-
- // Sanity check that getRefreshRate() does not modify the underlying configs.
- ASSERT_EQ(1, mConfigs.getRefreshRates().size());
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfig=*/0);
+ ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
}
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
- auto display = new Hwc2::mock::Display();
- std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
- auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
- config60.setVsyncPeriod(VSYNC_60);
- displayConfigs.push_back(config60.build());
- mConfigs.populate(displayConfigs);
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60},
+ {HWC2_CONFIG_ID_90, VSYNC_90}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfig=*/0);
- const auto& rates = mConfigs.getRefreshRates();
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ const auto& rates = refreshRateConfigs->getRefreshRateMap();
ASSERT_EQ(2, rates.size());
- const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
- const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
- ASSERT_NE(rates.end(), powerSavingRate);
- ASSERT_NE(rates.end(), defaultRate);
- ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
-
- RefreshRate expectedPowerSavingConfig =
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
- assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
- RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
- assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- assertRatesEqual(expectedPowerSavingConfig,
- *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
- assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
- ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-
- // Sanity check that getRefreshRate() does not modify the underlying configs.
- ASSERT_EQ(2, mConfigs.getRefreshRates().size());
-}
-
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
- auto display = new Hwc2::mock::Display();
- std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
- auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
- config60.setVsyncPeriod(VSYNC_60);
- displayConfigs.push_back(config60.build());
- auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
- config90.setVsyncPeriod(VSYNC_90);
- displayConfigs.push_back(config90.build());
- mConfigs.populate(displayConfigs);
-
- const auto& rates = mConfigs.getRefreshRates();
- ASSERT_EQ(3, rates.size());
- const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
- ASSERT_NE(rates.end(), powerSavingRate);
ASSERT_NE(rates.end(), defaultRate);
ASSERT_NE(rates.end(), performanceRate);
- RefreshRate expectedPowerSavingConfig =
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
- assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
- RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
- assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
- RefreshRate expectedPerformanceConfig =
- RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90};
- assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
-
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- assertRatesEqual(expectedPowerSavingConfig,
- *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
- assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
- ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60};
+ assertRatesEqual(expectedDefaultConfig, defaultRate->second);
+ RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90,
+ HWC2_CONFIG_ID_90};
+ assertRatesEqual(expectedPerformanceConfig, performanceRate->second);
+
+ assertRatesEqual(expectedDefaultConfig,
+ refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT));
assertRatesEqual(expectedPerformanceConfig,
- *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE));
}
} // namespace
} // namespace scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 411ec6177..cec0b32a6 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -22,7 +22,6 @@
#include <thread>
#include "Scheduler/RefreshRateStats.h"
-#include "mock/DisplayHardware/MockDisplay.h"
#include "mock/MockTimeStats.h"
using namespace std::chrono_literals;
@@ -42,9 +41,18 @@ protected:
RefreshRateStatsTest();
~RefreshRateStatsTest();
+ void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
+ mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
+ /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0);
+ mRefreshRateStats =
+ std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
+ /*currentConfig=*/0,
+ /*currentPowerMode=*/HWC_POWER_MODE_OFF);
+ }
+
mock::TimeStats mTimeStats;
- RefreshRateConfigs mRefreshRateConfigs;
- RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats};
+ std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+ std::unique_ptr<RefreshRateStats> mRefreshRateStats;
};
RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -63,63 +71,46 @@ namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
- mRefreshRateConfigs.populate(configs);
-
- // There is one default config, so the refresh rates should have one item.
- EXPECT_EQ(1, mRefreshRateStats.getTotalTimes().size());
-}
-
TEST_F(RefreshRateStatsTest, oneConfigTest) {
- auto display = new Hwc2::mock::Display();
-
- auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
- config.setVsyncPeriod(VSYNC_90);
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
- configs.push_back(config.build());
-
- mRefreshRateConfigs.populate(configs);
+ init({{CONFIG_ID_90, VSYNC_90}});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
- std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
- EXPECT_EQ(2, times.size());
+ std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(1, times.size());
EXPECT_NE(0u, times.count("ScreenOff"));
- EXPECT_EQ(1u, times.count("90fps"));
- EXPECT_EQ(0, times["90fps"]);
// Setting up tests on mobile harness can be flaky with time passing, so testing for
// exact time changes can result in flaxy numbers. To avoid that remember old
// numbers to make sure the correct values are increasing in the next test.
int screenOff = times["ScreenOff"];
- int ninety = times["90fps"];
// Screen is off by default.
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(0, times["90fps"]);
+ EXPECT_EQ(0u, times.count("90fps"));
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
- screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_LT(ninety, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90fps"));
+ EXPECT_LT(0, times["90fps"]);
- mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
- ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+ int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
// Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
// does not update refresh rates that come from the config.
EXPECT_LT(screenOff, times["ScreenOff"]);
@@ -127,93 +118,75 @@ TEST_F(RefreshRateStatsTest, oneConfigTest) {
}
TEST_F(RefreshRateStatsTest, twoConfigsTest) {
- auto display = new Hwc2::mock::Display();
-
- auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
- config90.setVsyncPeriod(VSYNC_90);
- std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
- configs.push_back(config90.build());
-
- auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
- config60.setVsyncPeriod(VSYNC_60);
- configs.push_back(config60.build());
-
- mRefreshRateConfigs.populate(configs);
+ init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
- std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
- EXPECT_EQ(3, times.size());
+ std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+ ASSERT_EQ(1, times.size());
EXPECT_NE(0u, times.count("ScreenOff"));
- EXPECT_EQ(1u, times.count("60fps"));
- EXPECT_EQ(0, times["60fps"]);
- EXPECT_EQ(1u, times.count("90fps"));
- EXPECT_EQ(0, times["90fps"]);
// Setting up tests on mobile harness can be flaky with time passing, so testing for
// exact time changes can result in flaxy numbers. To avoid that remember old
// numbers to make sure the correct values are increasing in the next test.
int screenOff = times["ScreenOff"];
- int sixty = times["60fps"];
- int ninety = times["90fps"];
// Screen is off by default.
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(sixty, times["60fps"]);
- EXPECT_EQ(ninety, times["90fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
- screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(sixty, times["60fps"]);
- EXPECT_LT(ninety, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90fps"));
+ EXPECT_LT(0, times["90fps"]);
// When power mode is normal, time for configs updates.
- mRefreshRateStats.setConfigMode(CONFIG_ID_60);
- ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_LT(sixty, times["60fps"]);
+ ASSERT_EQ(1u, times.count("60fps"));
+ EXPECT_LT(0, times["60fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
EXPECT_LT(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_60);
- ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
EXPECT_LT(sixty, times["60fps"]);
// Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
// does not update refresh rates that come from the config.
- mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
- mRefreshRateStats.setConfigMode(CONFIG_ID_90);
- sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+ mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ sixty = mRefreshRateStats->getTotalTimes()["60fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
- mRefreshRateStats.setConfigMode(CONFIG_ID_60);
- screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+ mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
- times = mRefreshRateStats.getTotalTimes();
+ times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 740115ea3..571fdfd9f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -3,13 +3,13 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
#include <log/log.h>
#include <mutex>
#include "Scheduler/EventControlThread.h"
#include "Scheduler/EventThread.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/Scheduler.h"
#include "mock/MockEventThread.h"
@@ -34,7 +34,7 @@ protected:
MOCK_METHOD0(requestNextVsync, void());
};
- scheduler::RefreshRateConfigs mRefreshRateConfigs;
+ std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
/**
* This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
@@ -73,9 +73,14 @@ SchedulerTest::SchedulerTest() {
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
+ mRefreshRateConfigs =
+ std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfig=*/0);
+
std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
- mScheduler = std::make_unique<MockScheduler>(mRefreshRateConfigs, std::move(eventThread));
+ mScheduler = std::make_unique<MockScheduler>(*mRefreshRateConfigs, std::move(eventThread));
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
mEventThreadConnection = new MockEventThreadConnection(mEventThread);
@@ -85,7 +90,7 @@ SchedulerTest::SchedulerTest() {
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16, ResyncCallback(),
+ mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16,
impl::EventThread::InterceptVSyncsCallback());
EXPECT_TRUE(mConnectionHandle != nullptr);
}
@@ -107,7 +112,7 @@ TEST_F(SchedulerTest, testNullPtr) {
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(nullptr, ResyncCallback(),
+ mScheduler->createDisplayEventConnection(nullptr,
ISurfaceComposer::
eConfigChangedSuppress));
EXPECT_TRUE(returnedValue == nullptr);
@@ -130,7 +135,7 @@ TEST_F(SchedulerTest, invalidConnectionHandle) {
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback(),
+ mScheduler->createDisplayEventConnection(connectionHandle,
ISurfaceComposer::
eConfigChangedSuppress));
EXPECT_TRUE(returnedValue == nullptr);
@@ -161,7 +166,7 @@ TEST_F(SchedulerTest, validConnectionHandle) {
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback(),
+ mScheduler->createDisplayEventConnection(mConnectionHandle,
ISurfaceComposer::
eConfigChangedSuppress));
EXPECT_TRUE(returnedValue != nullptr);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 64d34ee10..1c1b0201d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -32,11 +32,11 @@
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/MessageQueue.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceInterceptor.h"
-
#include "TimeStats/TimeStats.h"
namespace android {
@@ -342,6 +342,8 @@ public:
auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; }
auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; }
auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; }
+ auto& mutableRefreshRateStats() { return mFlinger->mRefreshRateStats; }
+ auto& mutableTimeStats() { return mFlinger->mTimeStats; }
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 3d5665689..6e08d2318 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -124,7 +124,7 @@ class OverrideLayerNames {
};
void AddImplicitLayers() {
- if (!is_instance_ || !driver::Debuggable())
+ if (!is_instance_)
return;
GetLayersFromSettings();
@@ -370,7 +370,8 @@ class OverrideExtensionNames {
private:
bool EnableDebugCallback() const {
- return (is_instance_ && driver::Debuggable() &&
+ return (is_instance_ &&
+ android::GraphicsEnv::getInstance().isDebuggable() &&
property_get_bool("debug.vulkan.enable_callback", false));
}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index bca46b2c3..681a289c1 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -754,10 +754,6 @@ void FreeDeviceData(DeviceData* data, const VkAllocationCallbacks& allocator) {
} // anonymous namespace
-bool Debuggable() {
- return (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) >= 0);
-}
-
bool OpenHAL() {
return Hal::Open();
}
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 047a27adc..ffe2a8bfe 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -101,7 +101,6 @@ struct DeviceData {
uint32_t driver_version;
};
-bool Debuggable();
bool OpenHAL();
const VkAllocationCallbacks& GetDefaultAllocator();
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index af1adcff6..691f3b0f5 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -484,8 +484,7 @@ void* GetLayerGetProcAddr(const Layer& layer,
void DiscoverLayers() {
ATRACE_CALL();
- if (property_get_bool("ro.debuggable", false) &&
- prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ if (android::GraphicsEnv::getInstance().isDebuggable()) {
DiscoverLayersInPathList(kSystemLayerLibraryDir);
}
if (!android::GraphicsEnv::getInstance().getLayerPaths().empty())