Merge "Remove legacy idmap binary"
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 40b8dc7..bb92fd3 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -20,9 +20,11 @@
"android.hardware.vibrator@1.2",
"android.hardware.vibrator@1.3",
"libbase",
+ "libbinder",
"libhidlbase",
"liblog",
"libutils",
+ "vintf-vibrator-cpp",
],
cflags: [
"-DLOG_TAG=\"idlcli\"",
@@ -34,6 +36,7 @@
defaults: ["idlcli-defaults"],
srcs: [
"CommandVibrator.cpp",
+ "vibrator/CommandGetCapabilities.cpp",
"vibrator/CommandOff.cpp",
"vibrator/CommandOn.cpp",
"vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index bcb207b..2f11923 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -18,6 +18,8 @@
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
#include "utils.h"
@@ -31,9 +33,25 @@
// Creates a Return<R> with STATUS::EX_NULL_POINTER.
template <class R>
-inline Return<R> NullptrStatus() {
+inline R NullptrStatus() {
using ::android::hardware::Status;
- return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+}
+
+template <>
+inline binder::Status NullptrStatus() {
+ using binder::Status;
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+}
+
+template <typename I>
+inline sp<I> getService() {
+ return I::getService();
+}
+
+template <>
+inline sp<hardware::vibrator::IVibrator> getService() {
+ return waitForVintfService<hardware::vibrator::IVibrator>();
}
template <typename I>
@@ -42,12 +60,12 @@
static std::unique_ptr<HalWrapper> Create() {
// Assume that if getService returns a nullptr, HAL is not available on the
// device.
- auto hal = I::getService();
+ auto hal = getService<I>();
return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
}
template <class R, class... Args0, class... Args1>
- Return<R> call(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+ R call(R (I::*fn)(Args0...), Args1&&... args1) {
return (*mHal.*fn)(std::forward<Args1>(args1)...);
}
@@ -65,7 +83,7 @@
}
template <class R, class I, class... Args0, class... Args1>
-Return<R> halCall(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+R halCall(R (I::*fn)(Args0...), Args1&&... args1) {
auto hal = getHal<I>();
return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
}
@@ -77,6 +95,7 @@
namespace V1_1 = ::android::hardware::vibrator::V1_1;
namespace V1_2 = ::android::hardware::vibrator::V1_2;
namespace V1_3 = ::android::hardware::vibrator::V1_3;
+namespace aidl = ::android::hardware::vibrator;
} // namespace vibrator
} // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
new file mode 100644
index 0000000..30d8587
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetCapabilities : public Command {
+ std::string getDescription() const override { return "Retrieves vibrator capabilities."; }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t cap;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Capabilities: " << std::bitset<32>(cap) << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetCapabilities>("getCapabilities");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
index a674f01..53fada0 100644
--- a/cmds/idlcli/vibrator/CommandOff.cpp
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -42,15 +42,24 @@
}
Status doMain(Args && /*args*/) override {
- auto ret = halCall(&V1_0::IVibrator::off);
+ std::string statusStr;
+ Status ret;
- if (!ret.isOk()) {
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::off);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else if (auto hal = getHal<V1_0::IVibrator>()) {
+ auto status = hal->call(&V1_0::IVibrator::off);
+ statusStr = toString(status);
+ ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+ } else {
return UNAVAILABLE;
}
- std::cout << "Status: " << toString(ret) << std::endl;
+ std::cout << "Status: " << statusStr << std::endl;
- return ret == V1_0::Status::OK ? OK : ERROR;
+ return ret;
}
};
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 2164b7d..ccb3c19 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -50,15 +50,24 @@
}
Status doMain(Args && /*args*/) override {
- auto ret = halCall(&V1_0::IVibrator::on, mDuration);
+ std::string statusStr;
+ Status ret;
- if (!ret.isOk()) {
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else if (auto hal = getHal<V1_0::IVibrator>()) {
+ auto status = hal->call(&V1_0::IVibrator::on, mDuration);
+ statusStr = toString(status);
+ ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+ } else {
return UNAVAILABLE;
}
- std::cout << "Status: " << toString(ret) << std::endl;
+ std::cout << "Status: " << statusStr << std::endl;
- return ret == V1_0::Status::OK ? OK : ERROR;
+ return ret;
}
uint32_t mDuration;
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 688cbd8..58d4e0a 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -23,6 +23,34 @@
namespace vibrator {
+/*
+ * The following static asserts are only relevant here because the argument
+ * parser uses a single implementation for determining the string names.
+ */
+static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
+ static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
+static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
+ static_cast<uint8_t>(aidl::EffectStrength::MEDIUM));
+static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) ==
+ static_cast<uint8_t>(aidl::EffectStrength::STRONG));
+static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) ==
+ static_cast<uint8_t>(aidl::Effect::CLICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) ==
+ static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD));
+static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP));
+static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) ==
+ static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) ==
+ static_cast<uint8_t>(aidl::Effect::RINGTONE_1));
+static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) ==
+ static_cast<uint8_t>(aidl::Effect::RINGTONE_2));
+static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
+ static_cast<uint8_t>(aidl::Effect::RINGTONE_15));
+static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
+ static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
+
using V1_0::EffectStrength;
using V1_3::Effect;
@@ -62,38 +90,50 @@
}
Status doMain(Args && /*args*/) override {
- Return<void> ret;
- V1_0::Status status;
+ std::string statusStr;
uint32_t lengthMs;
- auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
- status = retStatus;
- lengthMs = retLengthMs;
- };
+ Status ret;
- if (auto hal = getHal<V1_3::IVibrator>()) {
- ret = hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
- mStrength, callback);
- } else if (auto hal = getHal<V1_2::IVibrator>()) {
- ret = hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
- mStrength, callback);
- } else if (auto hal = getHal<V1_1::IVibrator>()) {
- ret = hal->call(&V1_1::IVibrator::perform_1_1, static_cast<V1_1::Effect_1_1>(mEffect),
- mStrength, callback);
- } else if (auto hal = getHal<V1_0::IVibrator>()) {
- ret = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
- mStrength, callback);
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ int32_t aidlLengthMs;
+ auto status =
+ hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
+ static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
+ statusStr = status.toString8();
+ lengthMs = static_cast<uint32_t>(aidlLengthMs);
+ ret = status.isOk() ? OK : ERROR;
} else {
- ret = NullptrStatus<void>();
+ Return<void> hidlRet;
+ V1_0::Status status;
+ auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+ status = retStatus;
+ lengthMs = retLengthMs;
+ };
+
+ if (auto hal = getHal<V1_3::IVibrator>()) {
+ hidlRet = hal->call(&V1_3::IVibrator::perform_1_3,
+ static_cast<V1_3::Effect>(mEffect), mStrength, callback);
+ } else if (auto hal = getHal<V1_2::IVibrator>()) {
+ hidlRet = hal->call(&V1_2::IVibrator::perform_1_2,
+ static_cast<V1_2::Effect>(mEffect), mStrength, callback);
+ } else if (auto hal = getHal<V1_1::IVibrator>()) {
+ hidlRet = hal->call(&V1_1::IVibrator::perform_1_1,
+ static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback);
+ } else if (auto hal = getHal<V1_0::IVibrator>()) {
+ hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
+ mStrength, callback);
+ } else {
+ return UNAVAILABLE;
+ }
+
+ statusStr = toString(status);
+ ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
}
- if (!ret.isOk()) {
- return UNAVAILABLE;
- }
-
- std::cout << "Status: " << toString(status) << std::endl;
+ std::cout << "Status: " << statusStr << std::endl;
std::cout << "Length: " << lengthMs << std::endl;
- return status == V1_0::Status::OK ? OK : ERROR;
+ return ret;
}
Effect mEffect;
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 38a1dc2..6e2261f 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -50,15 +50,24 @@
}
Status doMain(Args && /*args*/) override {
- auto ret = halCall(&V1_0::IVibrator::setAmplitude, mAmplitude);
+ std::string statusStr;
+ Status ret;
- if (!ret.isOk()) {
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::setAmplitude, mAmplitude);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else if (auto hal = getHal<V1_0::IVibrator>()) {
+ auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude);
+ statusStr = toString(status);
+ ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+ } else {
return UNAVAILABLE;
}
- std::cout << "Status: " << toString(ret) << std::endl;
+ std::cout << "Status: " << statusStr << std::endl;
- return ret == V1_0::Status::OK ? OK : ERROR;
+ return ret;
}
uint8_t mAmplitude;
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
index 5fb1fac..5bc827e 100644
--- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -48,15 +48,24 @@
}
Status doMain(Args && /*args*/) override {
- auto ret = halCall(&V1_3::IVibrator::setExternalControl, mEnable);
+ std::string statusStr;
+ Status ret;
- if (!ret.isOk()) {
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else if (auto hal = getHal<V1_3::IVibrator>()) {
+ auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable);
+ statusStr = toString(status);
+ ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+ } else {
return UNAVAILABLE;
}
- std::cout << "Status: " << toString(ret) << std::endl;
+ std::cout << "Status: " << statusStr << std::endl;
- return ret == V1_0::Status::OK ? OK : ERROR;
+ return ret;
}
bool mEnable;
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 768d900..f95e445 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -339,6 +339,10 @@
? "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");
@@ -518,6 +522,7 @@
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);
@@ -708,12 +713,13 @@
}
}
-static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
-static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
-static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
-static constexpr int PROFMAN_BIN_RETURN_CODE_SUCCESS = 5;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SUCCESS = 0;
+static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 1;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 2;
+static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 3;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 4;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 5;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_DIFFERENT_VERSIONS = 6;
class RunProfman : public ExecVHelper {
public:
@@ -889,6 +895,11 @@
should_clear_current_profiles = false;
should_clear_reference_profile = false;
break;
+ case PROFMAN_BIN_RETURN_CODE_ERROR_DIFFERENT_VERSIONS:
+ need_to_compile = false;
+ should_clear_current_profiles = true;
+ should_clear_reference_profile = true;
+ break;
default:
// Unknown return code or error. Unlink profiles.
LOG(WARNING) << "Unexpected error code while processing profiles for location "
diff --git a/cmds/installd/migrate_legacy_obb_data.sh b/cmds/installd/migrate_legacy_obb_data.sh
index 0e6d7b9..7399681 100644
--- a/cmds/installd/migrate_legacy_obb_data.sh
+++ b/cmds/installd/migrate_legacy_obb_data.sh
@@ -15,17 +15,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-rm -rf /data/media/Android/obb/test_probe
-mkdir -p /data/media/Android/obb/
-touch /data/media/Android/obb/test_probe
+rm -rf /data/media/0/Android/obb/test_probe
+mkdir -p /data/media/0/Android/obb/
+touch /data/media/0/Android/obb/test_probe
if ! test -f /data/media/0/Android/obb/test_probe ; then
log -p i -t migrate_legacy_obb_data "No support for 'unshared_obb'. Not migrating"
- rm -rf /data/media/Android/obb/test_probe
+ rm -rf /data/media/0/Android/obb/test_probe
exit 0
fi
# Delete the test file, and remove the obb folder if it is empty
-rm -rf /data/media/Android/obb/test_probe
+rm -rf /data/media/0/Android/obb/test_probe
rmdir /data/media/obb
if ! test -d /data/media/obb ; then
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index db36ce3..eefbe4f 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -480,6 +480,10 @@
"-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/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 861401c..141171b 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -68,7 +68,15 @@
}
#endif // !VENDORSERVICEMANAGER
-ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {}
+ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {
+#ifndef VENDORSERVICEMANAGER
+ // can process these at any times, don't want to delay first VINTF client
+ std::thread([] {
+ vintf::VintfObject::GetDeviceHalManifest();
+ vintf::VintfObject::GetFrameworkHalManifest();
+ }).detach();
+#endif // !VENDORSERVICEMANAGER
+}
ServiceManager::~ServiceManager() {
// this should only happen in tests
@@ -306,7 +314,7 @@
if (listeners.empty()) {
*it = mNameToCallback.erase(*it);
} else {
- it++;
+ (*it)++;
}
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 3f47f3b..37c0d77 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -385,6 +385,12 @@
, mThreadPoolSeq(1)
, mCallRestriction(CallRestriction::NONE)
{
+
+// TODO(b/139016109): enforce in build system
+#if defined(__ANDROID_APEX__) && !defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__)
+ LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
+#endif
+
if (mDriverFD >= 0) {
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index 0ad99ce..674f065 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -232,9 +232,10 @@
ret.append("No error");
} else {
ret.appendFormat("Status(%d, %s): '", mException, exceptionToString(mException).c_str());
- if (mException == EX_SERVICE_SPECIFIC ||
- mException == EX_TRANSACTION_FAILED) {
+ if (mException == EX_SERVICE_SPECIFIC) {
ret.appendFormat("%d: ", mErrorCode);
+ } else if (mException == EX_TRANSACTION_FAILED) {
+ ret.appendFormat("%s: ", statusToString(mErrorCode).c_str());
}
ret.append(String8(mMessage));
ret.append("'");
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 8c7ebba..b965881 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -58,7 +58,7 @@
* Returns null if the service does not exist.
*/
@UnsupportedAppUsage
- IBinder getService(@utf8InCpp String name);
+ @nullable IBinder getService(@utf8InCpp String name);
/**
* Retrieve an existing service called @a name from the service
@@ -66,7 +66,7 @@
* exist.
*/
@UnsupportedAppUsage
- IBinder checkService(@utf8InCpp String name);
+ @nullable IBinder checkService(@utf8InCpp String name);
/**
* Place a new @a service called @a name into the service
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
index f3bc31b..7871667 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -441,6 +441,42 @@
}
/**
+ * Writes a ScopedFileDescriptor object inside a std::vector<ScopedFileDescriptor> at index 'index'
+ * to 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_writeStdVectorParcelableElement<ScopedFileDescriptor>(
+ AParcel* parcel, const void* vectorData, size_t index) {
+ const std::vector<ScopedFileDescriptor>* vector =
+ static_cast<const std::vector<ScopedFileDescriptor>*>(vectorData);
+ int writeFd = vector->at(index).get();
+ if (writeFd < 0) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ return AParcel_writeParcelFileDescriptor(parcel, writeFd);
+}
+
+/**
+ * Reads a ScopedFileDescriptor object inside a std::vector<ScopedFileDescriptor> at index 'index'
+ * from 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_readStdVectorParcelableElement<ScopedFileDescriptor>(
+ const AParcel* parcel, void* vectorData, size_t index) {
+ std::vector<ScopedFileDescriptor>* vector =
+ static_cast<std::vector<ScopedFileDescriptor>*>(vectorData);
+ int readFd;
+ binder_status_t status = AParcel_readParcelFileDescriptor(parcel, &readFd);
+ if (status == STATUS_OK) {
+ if (readFd < 0) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ vector->at(index).set(readFd);
+ }
+ return status;
+}
+
+/**
* Convenience API for writing a std::vector<P>
*/
template <typename P>
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index db4a36b..94ab9f0 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1034,9 +1034,9 @@
binder_buffer_object obj {
.hdr = { .type = BINDER_TYPE_PTR },
+ .flags = 0,
.buffer = reinterpret_cast<binder_uintptr_t>((void*)&buf),
.length = 4,
- .flags = 0,
};
data.setDataCapacity(1024);
// Write a bogus object at offset 0 to get an entry in the offset table
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 45fea85..4ee9f55 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -397,7 +397,7 @@
if (deleteMapEntry(gTisMapFd, &key) && errno != ENOENT) return false;
}
- concurrent_val_t czeros = {.policy = {0}, .active = {0}};
+ concurrent_val_t czeros = { .active = {0}, .policy = {0}, };
std::vector<concurrent_val_t> cvals(gNCpus, czeros);
for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
if (writeToMapEntry(gConcurrentMapFd, &key, cvals.data(), BPF_EXIST) && errno != ENOENT)
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 250f902..56b94c1 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -62,6 +62,9 @@
"android.hardware.sensors@1.0::ISensors",
"android.hardware.thermal@2.0::IThermal",
"android.hardware.vr@1.0::IVr",
+ "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
+ "android.hardware.automotive.vehicle@2.0::IVehicle",
+ "android.hardware.automotive.evs@1.0::IEvsCamera",
NULL,
};
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 27ab482..55400c7 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -85,6 +85,11 @@
export_header_lib_headers: [
"libnativebase_headers",
],
+
+ stubs: {
+ symbol_file: "libnativewindow.map.txt",
+ versions: ["29"],
+ },
}
llndk_library {
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index daf1dcc..f59e8f0 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,9 +2,9 @@
global:
AHardwareBuffer_acquire;
AHardwareBuffer_allocate;
- AHardwareBuffer_createFromHandle; # llndk
+ AHardwareBuffer_createFromHandle; # llndk # apex
AHardwareBuffer_describe;
- AHardwareBuffer_getNativeHandle; # llndk
+ AHardwareBuffer_getNativeHandle; # llndk # apex
AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock;
AHardwareBuffer_lockAndGetInfo; # introduced=29
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
index cc2731d..71b1f9f 100644
--- a/libs/nativewindow/tests/AHardwareBufferTest.cpp
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -20,6 +20,7 @@
#include <android/hardware_buffer.h>
#include <private/android/AHardwareBufferHelpers.h>
#include <android/hardware/graphics/common/1.0/types.h>
+#include <vndk/hardware_buffer.h>
#include <gtest/gtest.h>
@@ -100,9 +101,33 @@
(uint64_t)BufferUsage::CPU_WRITE_RARELY,
AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY));
-EXPECT_TRUE(TestUsageConversion(
+ EXPECT_TRUE(TestUsageConversion(
(uint64_t)BufferUsage::GPU_RENDER_TARGET | (uint64_t)BufferUsage::GPU_TEXTURE |
- 1ull << 29 | 1ull << 57,
+ 1ull << 29 | 1ull << 57,
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_VENDOR_1 | AHARDWAREBUFFER_USAGE_VENDOR_13));
}
+
+TEST(AHardwareBufferTest, GetCreateHandleTest) {
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 1,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 64,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ EXPECT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer));
+ const native_handle_t* handle = AHardwareBuffer_getNativeHandle(buffer);
+ EXPECT_NE(nullptr, handle);
+
+ AHardwareBuffer* otherBuffer = nullptr;
+ EXPECT_EQ(0, AHardwareBuffer_createFromHandle(
+ &desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &otherBuffer));
+ EXPECT_NE(nullptr, otherBuffer);
+
+ AHardwareBuffer_release(buffer);
+ AHardwareBuffer_release(otherBuffer);
+}
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 1043390..e7640dd 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -46,6 +46,7 @@
}
void InputManager::initialize() {
+ mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
@@ -56,9 +57,9 @@
return result;
}
- result = mReader->start();
+ result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
- ALOGE("Could not start InputReader due to error %d.", result);
+ ALOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
@@ -68,9 +69,9 @@
}
status_t InputManager::stop() {
- status_t result = mReader->stop();
+ status_t result = mReaderThread->requestExitAndWait();
if (result) {
- ALOGW("Could not stop InputReader due to error %d.", result);
+ ALOGW("Could not stop InputReader thread due to error %d.", result);
}
result = mDispatcherThread->requestExitAndWait();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 2a7ed0f..40f66d8 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -43,15 +43,15 @@
/*
* The input manager is the core of the system event processing.
*
- * The input manager has two components.
+ * The input manager uses two threads.
*
- * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
- * policy, and posts messages to a queue managed by the InputDispatcherThread.
+ * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
+ * applies policy, and posts messages to a queue managed by the DispatcherThread.
* 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
* queue and asynchronously dispatches them to applications.
*
- * By design, the InputReader class and InputDispatcherThread class do not share any
- * internal state. Moreover, all communication is done one way from the InputReader
+ * By design, the InputReaderThread class and InputDispatcherThread class do not share any
+ * internal state. Moreover, all communication is done one way from the InputReaderThread
* into the InputDispatcherThread and never the reverse. Both classes may interact with the
* InputDispatchPolicy, however.
*
@@ -102,6 +102,7 @@
private:
sp<InputReaderInterface> mReader;
+ sp<InputReaderThread> mReaderThread;
sp<InputClassifierInterface> mClassifier;
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 2d6f2c1..0422d83 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,6 +33,20 @@
namespace android {
+// --- InputReaderThread ---
+
+InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
+ Thread(/*canCallJava*/ true), mReader(reader) {
+}
+
+InputReaderThread::~InputReaderThread() {
+}
+
+bool InputReaderThread::threadLoop() {
+ mReader->loopOnce();
+ return true;
+}
+
// --- InputReaderConfiguration ---
std::string InputReaderConfiguration::changesToString(uint32_t changes) {
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 56c0a73..5d576b9 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -19,12 +19,12 @@
#include "PointerControllerInterface.h"
-#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
+#include <input/DisplayViewport.h>
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
-#include <utils/Errors.h>
+#include <utils/Thread.h>
#include <utils/RefBase.h>
#include <stddef.h>
@@ -44,16 +44,7 @@
namespace android {
-// --- InputReaderInterface ---
-
-/* The interface for the InputReader shared library.
- *
- * Manages one or more threads that process raw input events and sends cooked event data to an
- * input listener.
- *
- * The implementation must guarantee thread safety for this interface. However, since the input
- * listener is NOT thread safe, all calls to the listener must happen from the same thread.
- */
+/* Processes raw input events and sends cooked event data to an input listener. */
class InputReaderInterface : public virtual RefBase {
protected:
InputReaderInterface() { }
@@ -65,17 +56,18 @@
* This method may be called on any thread (usually by the input manager). */
virtual void dump(std::string& dump) = 0;
- /* Called by the heartbeat to ensures that the reader has not deadlocked. */
+ /* Called by the heatbeat to ensures that the reader has not deadlocked. */
virtual void monitor() = 0;
/* Returns true if the input device is enabled. */
virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
- /* Makes the reader start processing events from the kernel. */
- virtual status_t start() = 0;
-
- /* Makes the reader stop processing any more events. */
- virtual status_t stop() = 0;
+ /* Runs a single iteration of the processing loop.
+ * Nominally reads and processes one incoming message from the EventHub.
+ *
+ * This method should be called on the input reader thread.
+ */
+ virtual void loopOnce() = 0;
/* Gets information about all input devices.
*
@@ -112,7 +104,17 @@
virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
};
-// --- InputReaderConfiguration ---
+/* Reads raw events from the event hub and processes them, endlessly. */
+class InputReaderThread : public Thread {
+public:
+ explicit InputReaderThread(const sp<InputReaderInterface>& reader);
+ virtual ~InputReaderThread();
+
+private:
+ sp<InputReaderInterface> mReader;
+
+ virtual bool threadLoop();
+};
/*
* Input reader configuration.
@@ -283,8 +285,6 @@
std::vector<DisplayViewport> mDisplays;
};
-// --- TouchAffineTransformation ---
-
struct TouchAffineTransformation {
float x_scale;
float x_ymix;
@@ -307,8 +307,6 @@
void applyTo(float& x, float& y) const;
};
-// --- InputReaderPolicyInterface ---
-
/*
* Input reader policy interface.
*
@@ -318,8 +316,8 @@
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This interface is also mocked in the unit tests.
*
- * These methods will NOT re-enter the input reader interface, so they may be called from
- * any method in the input reader interface.
+ * These methods must NOT re-enter the input reader since they may be called while
+ * holding the input reader lock.
*/
class InputReaderPolicyInterface : public virtual RefBase {
protected:
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 05f0db1..1c5adc3 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,38 +38,16 @@
#include <unistd.h>
#include <log/log.h>
-#include <utils/Errors.h>
#include <android-base/stringprintf.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
-#include <utils/Thread.h>
+
using android::base::StringPrintf;
namespace android {
-// --- InputReader::InputReaderThread ---
-
-/* Thread that reads raw events from the event hub and processes them, endlessly. */
-class InputReader::InputReaderThread : public Thread {
-public:
- explicit InputReaderThread(InputReader* reader)
- : Thread(/* canCallJava */ true), mReader(reader) {}
-
- ~InputReaderThread() {}
-
-private:
- InputReader* mReader;
-
- bool threadLoop() override {
- mReader->loopOnce();
- return true;
- }
-};
-
-// --- InputReader ---
-
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
@@ -83,7 +61,6 @@
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
- mThread = new InputReaderThread(this);
{ // acquire lock
AutoMutex _l(mLock);
@@ -99,28 +76,6 @@
}
}
-status_t InputReader::start() {
- if (mThread->isRunning()) {
- return ALREADY_EXISTS;
- }
- return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
-}
-
-status_t InputReader::stop() {
- if (!mThread->isRunning()) {
- return OK;
- }
- if (gettid() == mThread->getTid()) {
- ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
- return INVALID_OPERATION;
- }
- // Directly calling requestExitAndWait() causes the thread to not exit
- // if mEventHub is waiting for a long timeout.
- mThread->requestExit();
- mEventHub->wake();
- return mThread->requestExitAndWait();
-}
-
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 0a4e808..557eb3b 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -38,12 +38,12 @@
* that it sends to the input listener. Some functions of the input reader, such as early
* event filtering in low power states, are controlled by a separate policy object.
*
- * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where
- * most of the work happens, but the InputReader can receive queries from other system
+ * The InputReader owns a collection of InputMappers. Most of the work it does happens
+ * on the input reader thread but the InputReader can receive queries from other system
* components running on arbitrary threads. To keep things manageable, the InputReader
* uses a single Mutex to guard its state. The Mutex may be held while calling into the
* EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener. All calls to InputListener must happen from InputReader's thread.
+ * InputListener.
*/
class InputReader : public InputReaderInterface {
public:
@@ -55,8 +55,7 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
- virtual status_t start() override;
- virtual status_t stop() override;
+ virtual void loopOnce() override;
virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
@@ -112,9 +111,6 @@
friend class ContextImpl;
private:
- class InputReaderThread;
- sp<InputReaderThread> mThread;
-
Mutex mLock;
Condition mReaderIsAliveCondition;
@@ -137,10 +133,6 @@
std::unordered_map<int32_t /*deviceId*/, InputDevice*> mDevices;
- // With each iteration of the loop, InputReader reads and processes one incoming message from
- // the EventHub.
- void loopOnce();
-
// low-level input event decoding and device management
void processEventsLocked(const RawEvent* rawEvents, size_t count);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index b706a74..2a29e0d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -50,43 +50,35 @@
public:
FakeInputDispatcherPolicy() {
- mInputEventFiltered = false;
- mTime = -1;
- mAction = -1;
- mDisplayId = -1;
mOnPointerDownToken.clear();
}
- void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
- ASSERT_TRUE(mInputEventFiltered)
- << "Expected filterInputEvent() to have been called.";
+ void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
+ ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+ ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_KEY);
- ASSERT_EQ(mTime, args->eventTime)
- << "Expected time of filtered event was not matched";
- ASSERT_EQ(mAction, args->action)
- << "Expected action of filtered event was not matched";
- ASSERT_EQ(mDisplayId, args->displayId)
- << "Expected displayId of filtered event was not matched";
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
+ ASSERT_EQ(keyEvent.getEventTime(), args.eventTime);
+ ASSERT_EQ(keyEvent.getAction(), args.action);
+ ASSERT_EQ(keyEvent.getDisplayId(), args.displayId);
reset();
}
- void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) {
- ASSERT_TRUE(mInputEventFiltered)
- << "Expected filterInputEvent() to have been called.";
+ void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
+ ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+ ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_MOTION);
- ASSERT_EQ(mTime, args->eventTime)
- << "Expected time of filtered event was not matched";
- ASSERT_EQ(mAction, args->action)
- << "Expected action of filtered event was not matched";
- ASSERT_EQ(mDisplayId, args->displayId)
- << "Expected displayId of filtered event was not matched";
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
+ ASSERT_EQ(motionEvent.getEventTime(), args.eventTime);
+ ASSERT_EQ(motionEvent.getAction(), args.action);
+ ASSERT_EQ(motionEvent.getDisplayId(), args.displayId);
reset();
}
void assertFilterInputEventWasNotCalled() {
- ASSERT_FALSE(mInputEventFiltered)
+ ASSERT_EQ(nullptr, mFilteredEvent)
<< "Expected filterInputEvent() to not have been called.";
}
@@ -97,10 +89,7 @@
}
private:
- bool mInputEventFiltered;
- nsecs_t mTime;
- int32_t mAction;
- int32_t mDisplayId;
+ std::unique_ptr<InputEvent> mFilteredEvent;
sp<IBinder> mOnPointerDownToken;
virtual void notifyConfigurationChanged(nsecs_t) {
@@ -122,26 +111,20 @@
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
- mTime = keyEvent->getEventTime();
- mAction = keyEvent->getAction();
- mDisplayId = keyEvent->getDisplayId();
+ mFilteredEvent = std::make_unique<KeyEvent>(*keyEvent);
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
- mTime = motionEvent->getEventTime();
- mAction = motionEvent->getAction();
- mDisplayId = motionEvent->getDisplayId();
+ mFilteredEvent = std::make_unique<MotionEvent>(*motionEvent);
break;
}
}
-
- mInputEventFiltered = true;
return true;
}
@@ -176,10 +159,7 @@
}
void reset() {
- mInputEventFiltered = false;
- mTime = -1;
- mAction = -1;
- mDisplayId = -1;
+ mFilteredEvent = nullptr;
mOnPointerDownToken.clear();
}
};
@@ -385,8 +365,8 @@
class FakeInputReceiver {
public:
- void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId,
- int32_t expectedFlags = 0) {
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+ int32_t expectedFlags) {
uint32_t consumeSeq;
InputEvent* event;
status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
@@ -399,33 +379,41 @@
ASSERT_EQ(expectedEventType, event->getType())
<< mName.c_str() << ": event type should match.";
- ASSERT_EQ(expectedDisplayId, event->getDisplayId())
- << mName.c_str() << ": event displayId should be the same as expected.";
+ EXPECT_EQ(expectedDisplayId, event->getDisplayId());
- int32_t flags;
switch (expectedEventType) {
case AINPUT_EVENT_TYPE_KEY: {
- KeyEvent* typedEvent = static_cast<KeyEvent*>(event);
- flags = typedEvent->getFlags();
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
+ EXPECT_EQ(expectedAction, keyEvent.getAction());
+ EXPECT_EQ(expectedFlags, keyEvent.getFlags());
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
- MotionEvent* typedEvent = static_cast<MotionEvent*>(event);
- flags = typedEvent->getFlags();
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ EXPECT_EQ(expectedAction, motionEvent.getAction());
+ EXPECT_EQ(expectedFlags, motionEvent.getFlags());
break;
}
default: {
FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
}
}
- ASSERT_EQ(expectedFlags, flags)
- << mName.c_str() << ": event flags should be the same as expected.";
status = mConsumer->sendFinishedSignal(consumeSeq, handled());
ASSERT_EQ(OK, status)
<< mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
+ void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+ expectedFlags);
+ }
+
+ void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
+ expectedFlags);
+ }
+
void assertNoEvents() {
uint32_t consumeSeq;
InputEvent* event;
@@ -631,7 +619,7 @@
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
// Window should receive motion event.
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
// The foreground window should receive the first touch down event.
@@ -652,7 +640,7 @@
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
// Top window should receive the touch down event. Second window should not receive anything.
- windowTop->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowSecond->assertNoEvents();
}
@@ -678,7 +666,7 @@
// Focused window should receive event.
windowTop->assertNoEvents();
- windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+ windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
}
TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
@@ -703,7 +691,7 @@
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
// Top focused window should receive event.
- windowTop->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+ windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
windowSecond->assertNoEvents();
}
@@ -733,7 +721,7 @@
// Top window is invalid, so it should not receive any input event.
windowTop->assertNoEvents();
- windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+ windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
}
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
@@ -758,7 +746,7 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
ADISPLAY_ID_DEFAULT, 610, 400, 599, 400));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowRight->assertNoEvents();
}
@@ -814,7 +802,7 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test touch down on second display.
@@ -822,29 +810,29 @@
AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
- windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+ windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
}
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test inject a key down without display id specified.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
- windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+ windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
// Remove secondary display.
std::vector<sp<InputWindowHandle>> noWindows;
mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
// Expect old focus should receive a cancel event.
- windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE,
- AKEY_EVENT_FLAG_CANCELED);
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
+ AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
@@ -873,8 +861,8 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
- monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitorInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
monitorInSecondary->assertNoEvents();
@@ -884,8 +872,8 @@
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary->assertNoEvents();
- windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
- monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+ windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
+ monitorInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
// Test inject a non-pointer motion event.
// If specific a display, it will dispatch to the focused window of particular display,
@@ -895,8 +883,8 @@
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary->assertNoEvents();
- windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
- monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+ windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
+ monitorInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
}
// Test per-display input monitors for key event.
@@ -912,8 +900,8 @@
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary->assertNoEvents();
- windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
- monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+ windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+ monitorInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
}
class InputFilterTest : public InputDispatcherTest {
@@ -931,7 +919,7 @@
mDispatcher->notifyMotion(&motionArgs);
if (expectToBeFiltered) {
- mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+ mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
} else {
mFakePolicy->assertFilterInputEventWasNotCalled();
}
@@ -946,7 +934,7 @@
mDispatcher->notifyKey(&keyArgs);
if (expectToBeFiltered) {
- mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+ mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
} else {
mFakePolicy->assertFilterInputEventWasNotCalled();
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8d4ab6a..c1c9122 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1133,8 +1133,12 @@
protected:
sp<FakeInputReaderPolicy> mFakePolicy;
- virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
- virtual void TearDown() override { mFakePolicy.clear(); }
+ virtual void SetUp() {
+ mFakePolicy = new FakeInputReaderPolicy();
+ }
+ virtual void TearDown() {
+ mFakePolicy.clear();
+ }
};
/**
@@ -1317,20 +1321,18 @@
sp<TestInputListener> mFakeListener;
sp<FakeInputReaderPolicy> mFakePolicy;
std::shared_ptr<FakeEventHub> mFakeEventHub;
- std::unique_ptr<InstrumentedInputReader> mReader;
+ sp<InstrumentedInputReader> mReader;
- virtual void SetUp() override {
+ virtual void SetUp() {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
- mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- mFakeListener);
- ASSERT_EQ(OK, mReader->start());
+ mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
}
- virtual void TearDown() override {
- ASSERT_EQ(OK, mReader->stop());
+ virtual void TearDown() {
+ mReader.clear();
mFakeListener.clear();
mFakePolicy.clear();
@@ -1344,18 +1346,24 @@
mFakeEventHub->addConfigurationMap(deviceId, configuration);
}
mFakeEventHub->finishDeviceScan();
+ mReader->loopOnce();
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
}
void disableDevice(int32_t deviceId, InputDevice* device) {
mFakePolicy->addDisabledDevice(deviceId);
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
}
void enableDevice(int32_t deviceId, InputDevice* device) {
mFakePolicy->removeDisabledDevice(deviceId);
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+ }
+
+ void configureDevice(uint32_t changes, InputDevice* device) {
+ device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
}
FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
@@ -1409,22 +1417,28 @@
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), true);
disableDevice(deviceId, device);
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), false);
disableDevice(deviceId, device);
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
ASSERT_EQ(device->isEnabled(), false);
enableDevice(deviceId, device);
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), true);
}
@@ -1546,7 +1560,7 @@
ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
}
-TEST_F(InputReaderTest, WhenDeviceScanFinished_SendsConfigurationChanged) {
+TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
NotifyConfigurationChangedArgs args;
@@ -1555,12 +1569,13 @@
ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
}
-TEST_F(InputReaderTest, ForwardsRawEventsToMappers) {
+TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
FakeInputMapper* mapper = nullptr;
ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
RawEvent event;
@@ -1587,16 +1602,19 @@
uint32_t prevSequenceNum = resetArgs.sequenceNum;
disableDevice(deviceId, device);
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
prevSequenceNum = resetArgs.sequenceNum;
enableDevice(deviceId, device);
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
prevSequenceNum = resetArgs.sequenceNum;
disableDevice(deviceId, device);
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
prevSequenceNum = resetArgs.sequenceNum;
@@ -1611,6 +1629,7 @@
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
device->addMapper(mapper);
mReader->setNextDevice(device);
+ addDevice(deviceId, "fake", deviceClass, nullptr);
const uint8_t hdmi1 = 1;
@@ -1618,20 +1637,13 @@
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
// Add default and second display.
- mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
-
- // Add the device, and make sure all of the callbacks are triggered.
- // The device is added after the input port associations are processed since
- // we do not yet support dynamic device-to-display associations.
- ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
+ mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
- ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
// Device should only dispatch to the specified display.
ASSERT_EQ(deviceId, device->getId());
@@ -1640,8 +1652,6 @@
// Can't dispatch event from a disabled device.
disableDevice(deviceId, device);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
- ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
}
@@ -1664,7 +1674,7 @@
InputDevice* mDevice;
- virtual void SetUp() override {
+ virtual void SetUp() {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1678,7 +1688,7 @@
DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
}
- virtual void TearDown() override {
+ virtual void TearDown() {
delete mDevice;
delete mFakeContext;
@@ -1902,7 +1912,7 @@
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
- virtual void SetUp() override {
+ virtual void SetUp() {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1916,7 +1926,7 @@
mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
}
- virtual void TearDown() override {
+ virtual void TearDown() {
delete mDevice;
delete mFakeContext;
mFakeListener.clear();
@@ -2579,7 +2589,7 @@
sp<FakePointerController> mFakePointerController;
- virtual void SetUp() override {
+ virtual void SetUp() {
InputMapperTest::SetUp();
mFakePointerController = new FakePointerController();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index f529a44..4b71bd8 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -165,7 +165,9 @@
"Scheduler/PhaseOffsets.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
+ "Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
+ "Scheduler/VSyncPredictor.cpp",
"Scheduler/VSyncModulator.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index a25709c..94c4a81 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -77,9 +77,9 @@
// with the clone layer trying to use the deleted texture.
mFlinger->deleteTextureAsync(mTextureName);
}
- const int32_t layerID = getSequence();
- mFlinger->mTimeStats->onDestroy(layerID);
- mFlinger->mFrameTracer->onDestroy(layerID);
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->onDestroy(layerId);
+ mFlinger->mFrameTracer->onDestroy(layerId);
}
void BufferLayer::useSurfaceDamage() {
@@ -286,7 +286,7 @@
return hasReadyFrame();
}
-bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId,
+bool BufferLayer::onPostComposition(sp<const DisplayDevice> displayDevice,
const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
const CompositorTiming& compositorTiming) {
@@ -305,8 +305,16 @@
nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
- const int32_t layerID = getSequence();
- mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
+
+ const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+ if (outputLayer && outputLayer->requiresClientComposition()) {
+ nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
+ mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+ clientCompositionTimestamp,
+ FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+ }
std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
if (frameReadyFence->isValid()) {
@@ -317,17 +325,18 @@
mFrameTracker.setFrameReadyTime(desiredPresentTime);
}
+ const auto displayId = displayDevice->getId();
if (presentFence->isValid()) {
- mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
- mFlinger->mFrameTracer->traceFence(layerID, getCurrentBufferId(), mCurrentFrameNumber,
+ mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence);
+ mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
} else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
- mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
- mFlinger->mFrameTracer->traceTimestamp(layerID, getCurrentBufferId(), mCurrentFrameNumber,
+ mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
actualPresentTime,
FrameTracer::FrameEvent::PRESENT_FENCE);
mFrameTracker.setActualPresentTime(actualPresentTime);
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 656ba12..16855d2 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -78,7 +78,7 @@
bool isHdrY410() const override;
- bool onPostComposition(const std::optional<DisplayId>& displayId,
+ bool onPostComposition(sp<const DisplayDevice> displayDevice,
const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
const CompositorTiming& compositorTiming) override;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index dc61e49..d51d34b 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -223,7 +223,7 @@
// BufferItem's that weren't actually queued. This can happen in shared
// buffer mode.
bool queuedBuffer = false;
- const int32_t layerID = getSequence();
+ const int32_t layerId = getSequence();
LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
getProducerStickyTransform() != 0, mName, mOverrideScalingMode,
getTransformToDisplayInverse());
@@ -264,7 +264,7 @@
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
- mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
}
@@ -278,8 +278,8 @@
Mutex::Autolock lock(mQueueItemLock);
mQueueItems.clear();
mQueuedFrames = 0;
- mFlinger->mTimeStats->onDestroy(layerID);
- mFlinger->mFrameTracer->onDestroy(layerID);
+ mFlinger->mTimeStats->onDestroy(layerId);
+ mFlinger->mFrameTracer->onDestroy(layerId);
}
// Once we have hit this state, the shadow queue may no longer
@@ -301,19 +301,17 @@
// updateTexImage
while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
- mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
}
uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
- mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber,
- mQueueItems[0].mFenceTime);
- mFlinger->mFrameTracer->traceFence(layerID, bufferID, currentFrameNumber,
+ mFlinger->mFrameTracer->traceFence(layerId, bufferID, currentFrameNumber,
mQueueItems[0].mFenceTime,
FrameTracer::FrameEvent::ACQUIRE_FENCE);
- mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime);
- mFlinger->mFrameTracer->traceTimestamp(layerID, bufferID, currentFrameNumber, latchTime,
+ mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
FrameTracer::FrameEvent::LATCH);
mQueueItems.removeAt(0);
@@ -373,38 +371,36 @@
// -----------------------------------------------------------------------
void BufferQueueLayer::onFrameDequeued(const uint64_t bufferId) {
- const int32_t layerID = getSequence();
- mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+ const int32_t layerId = getSequence();
+ mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
systemTime(), FrameTracer::FrameEvent::DEQUEUE);
}
void BufferQueueLayer::onFrameDetached(const uint64_t bufferId) {
- const int32_t layerID = getSequence();
- mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+ const int32_t layerId = getSequence();
+ mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
systemTime(), FrameTracer::FrameEvent::DETACH);
}
void BufferQueueLayer::onFrameCancelled(const uint64_t bufferId) {
- const int32_t layerID = getSequence();
- mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+ const int32_t layerId = getSequence();
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
systemTime(), FrameTracer::FrameEvent::CANCEL);
}
void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
- const int32_t layerID = getSequence();
- mFlinger->mFrameTracer->traceTimestamp(layerID, item.mGraphicBuffer->getId(), item.mFrameNumber,
+ const int32_t layerId = getSequence();
+ mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
systemTime(), FrameTracer::FrameEvent::QUEUE);
ATRACE_CALL();
// Add this buffer from our internal queue tracker
{ // Autolock scope
- if (mFlinger->mUseSmart90ForVideo) {
- const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime,
- item.mHdrMetadata.validTypes != 0);
- }
+ const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
+ const bool isHDR = item.mHdrMetadata.validTypes != 0;
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime, isHDR);
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 5768edd..d68fe8e 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -240,18 +240,15 @@
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
- const int32_t layerID = getSequence();
- mFlinger->mTimeStats->setPostTime(layerID, mFrameNumber, getName().c_str(), postTime);
- mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerID, buffer->getId(), mFrameNumber, postTime,
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->setPostTime(layerId, mFrameNumber, getName().c_str(), postTime);
+ mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+ mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mFrameNumber, postTime,
FrameTracer::FrameEvent::POST);
mCurrentState.desiredPresentTime = desiredPresentTime;
- if (mFlinger->mUseSmart90ForVideo) {
- const nsecs_t presentTime = (desiredPresentTime == -1) ? 0 : desiredPresentTime;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime,
- mCurrentState.hdrMetadata.validTypes != 0);
- }
+ const bool isHDR = mCurrentState.hdrMetadata.validTypes != 0;
+ mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime, isHDR);
return true;
}
@@ -461,7 +458,7 @@
return NO_ERROR;
}
- const int32_t layerID = getSequence();
+ const int32_t layerId = getSequence();
// Reject if the layer is invalid
uint32_t bufferWidth = s.buffer->width;
@@ -483,7 +480,7 @@
ALOGE("[%s] rejecting buffer: "
"bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mFlinger->mTimeStats->removeTimeRecord(layerID, mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mFrameNumber);
return BAD_VALUE;
}
@@ -500,18 +497,18 @@
// a GL-composited layer) not at all.
status_t err = bindTextureImage();
if (err != NO_ERROR) {
- mFlinger->mTimeStats->onDestroy(layerID);
- mFlinger->mFrameTracer->onDestroy(layerID);
+ mFlinger->mTimeStats->onDestroy(layerId);
+ mFlinger->mFrameTracer->onDestroy(layerId);
return BAD_VALUE;
}
}
const uint64_t bufferID = getCurrentBufferId();
- mFlinger->mTimeStats->setAcquireFence(layerID, mFrameNumber, mBufferInfo.mFenceTime);
- mFlinger->mFrameTracer->traceFence(layerID, bufferID, mFrameNumber, mBufferInfo.mFenceTime,
+ mFlinger->mTimeStats->setAcquireFence(layerId, mFrameNumber, mBufferInfo.mFenceTime);
+ mFlinger->mFrameTracer->traceFence(layerId, bufferID, mFrameNumber, mBufferInfo.mFenceTime,
FrameTracer::FrameEvent::ACQUIRE_FENCE);
- mFlinger->mTimeStats->setLatchTime(layerID, mFrameNumber, latchTime);
- mFlinger->mFrameTracer->traceTimestamp(layerID, bufferID, mFrameNumber, latchTime,
+ mFlinger->mTimeStats->setLatchTime(layerId, mFrameNumber, latchTime);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mFrameNumber, latchTime,
FrameTracer::FrameEvent::LATCH);
mCurrentStateModified = false;
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 16fe27c..a5be01c 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -42,7 +42,7 @@
return false;
}
- auto& processBuffers = it->second;
+ auto& processBuffers = it->second.second;
auto bufItr = processBuffers.find(id);
if (bufItr == processBuffers.end()) {
@@ -86,12 +86,14 @@
return false;
}
auto [itr, success] =
- mBuffers.emplace(processToken, std::unordered_map<uint64_t, ClientCacheBuffer>());
+ mBuffers.emplace(processToken,
+ std::make_pair(token,
+ std::unordered_map<uint64_t, ClientCacheBuffer>()));
LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache");
it = itr;
}
- auto& processBuffers = it->second;
+ auto& processBuffers = it->second.second;
if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
ALOGE("failed to cache buffer: cache is full");
@@ -120,7 +122,7 @@
}
}
- mBuffers[processToken].erase(id);
+ mBuffers[processToken].second.erase(id);
}
for (auto& recipient : pendingErase) {
@@ -180,7 +182,7 @@
return;
}
- for (auto& [id, clientCacheBuffer] : itr->second) {
+ for (auto& [id, clientCacheBuffer] : itr->second.second) {
client_cache_t cacheId = {processToken, id};
for (auto& recipient : clientCacheBuffer.recipients) {
sp<ErasedRecipient> erasedRecipient = recipient.promote();
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index aa6c80d..d7af7c0 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -61,7 +61,8 @@
std::set<wp<ErasedRecipient>> recipients;
};
std::map<wp<IBinder> /*caching process*/,
- std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>>
+ std::pair<sp<IBinder> /*strong ref to caching process*/,
+ std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>>>
mBuffers GUARDED_BY(mMutex);
class CacheDeathRecipient : public IBinder::DeathRecipient {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 1347449..11cfccc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -98,6 +98,9 @@
// Debugging
void dump(std::string& result) const;
+
+ // Timestamp for when the layer is queued for client composition
+ nsecs_t clientCompositionTimestamp;
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 1953005..6877f8b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -904,6 +904,7 @@
layerSettings.disableBlending = true;
}
+ layer->editState().clientCompositionTimestamp = systemTime();
clientCompositionLayers.push_back(*result);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 0dbf8f0..49e7c70 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -15,10 +15,12 @@
*/
#include <compositionengine/CompositionRefreshArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/mock/Layer.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/Output.h>
+#include <compositionengine/mock/OutputLayer.h>
#include <gtest/gtest.h>
#include <renderengine/mock/RenderEngine.h>
@@ -28,16 +30,28 @@
namespace {
using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Ref;
using ::testing::Return;
+using ::testing::ReturnRef;
using ::testing::SaveArg;
using ::testing::StrictMock;
-class CompositionEngineTest : public testing::Test {
-public:
+struct CompositionEngineTest : public testing::Test {
android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
renderengine::mock::RenderEngine* mRenderEngine =
new StrictMock<renderengine::mock::RenderEngine>();
impl::CompositionEngine mEngine;
+ CompositionRefreshArgs mRefreshArgs;
+
+ std::shared_ptr<mock::Output> mOutput1{std::make_shared<StrictMock<mock::Output>>()};
+ std::shared_ptr<mock::Output> mOutput2{std::make_shared<StrictMock<mock::Output>>()};
+ std::shared_ptr<mock::Output> mOutput3{std::make_shared<StrictMock<mock::Output>>()};
+
+ std::shared_ptr<mock::Layer> mLayer1{std::make_shared<StrictMock<mock::Layer>>()};
+ std::shared_ptr<mock::Layer> mLayer2{std::make_shared<StrictMock<mock::Layer>>()};
+ std::shared_ptr<mock::Layer> mLayer3{std::make_shared<StrictMock<mock::Layer>>()};
+ std::shared_ptr<mock::Layer> mLayer4{std::make_shared<StrictMock<mock::Layer>>()};
};
TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
@@ -58,38 +72,165 @@
}
/*
+ * CompositionEngine::present
+ */
+
+struct CompositionEnginePresentTest : public CompositionEngineTest {
+ struct CompositionEnginePartialMock : public impl::CompositionEngine {
+ // These are the overridable functions CompositionEngine::present() may
+ // call, and have separate test coverage.
+ MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+ };
+
+ StrictMock<CompositionEnginePartialMock> mEngine;
+};
+
+TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) {
+ // present() always calls preComposition()
+ EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEnginePresentTest, worksAsExpected) {
+ // Expect calls to in a certain sequence
+ InSequence seq;
+
+ // present() always calls preComposition()
+ EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+
+ // The first step in presenting is to make sure all outputs are prepared.
+ EXPECT_CALL(*mOutput1, prepare(Ref(mRefreshArgs), _));
+ EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
+ EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
+
+ // The next step in presenting is to make sure all outputs have the latest
+ // state from the front-end (SurfaceFlinger).
+ EXPECT_CALL(*mOutput1, updateLayerStateFromFE(Ref(mRefreshArgs)));
+ EXPECT_CALL(*mOutput2, updateLayerStateFromFE(Ref(mRefreshArgs)));
+ EXPECT_CALL(*mOutput3, updateLayerStateFromFE(Ref(mRefreshArgs)));
+
+ // The last step is to actually present each output.
+ EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
+ EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
+ EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)));
+
+ mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+ mEngine.present(mRefreshArgs);
+}
+
+/*
+ * CompositionEngine::updateCursorAsync
+ */
+
+struct CompositionEngineUpdateCursorAsyncTest : public CompositionEngineTest {
+public:
+ CompositionEngineUpdateCursorAsyncTest() {
+ EXPECT_CALL(*mOutput1, getOutputLayerCount()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mOutput1, getOutputLayerOrderedByZByIndex(_)).Times(0);
+
+ EXPECT_CALL(*mOutput2, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*mOutput2, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mOutput2OutputLayer1));
+
+ EXPECT_CALL(*mOutput3, getOutputLayerCount()).WillRepeatedly(Return(2));
+ EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mOutput3OutputLayer1));
+ EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mOutput3OutputLayer2));
+
+ EXPECT_CALL(mOutput2OutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mOutput2Layer1FE));
+ EXPECT_CALL(mOutput3OutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mOutput3Layer1FE));
+ EXPECT_CALL(mOutput3OutputLayer2, getLayerFE()).WillRepeatedly(ReturnRef(mOutput3Layer2FE));
+
+ EXPECT_CALL(mOutput2OutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mOutput2Layer1));
+ EXPECT_CALL(mOutput3OutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mOutput3Layer1));
+ EXPECT_CALL(mOutput3OutputLayer2, getLayer()).WillRepeatedly(ReturnRef(mOutput3Layer2));
+
+ EXPECT_CALL(mOutput2Layer1, editFEState()).WillRepeatedly(ReturnRef(mOutput2Layer1FEState));
+ EXPECT_CALL(mOutput3Layer1, editFEState()).WillRepeatedly(ReturnRef(mOutput3Layer1FEState));
+ EXPECT_CALL(mOutput3Layer2, editFEState()).WillRepeatedly(ReturnRef(mOutput3Layer2FEState));
+ }
+
+ StrictMock<mock::OutputLayer> mOutput2OutputLayer1;
+ StrictMock<mock::OutputLayer> mOutput3OutputLayer1;
+ StrictMock<mock::OutputLayer> mOutput3OutputLayer2;
+
+ StrictMock<mock::LayerFE> mOutput2Layer1FE;
+ StrictMock<mock::LayerFE> mOutput3Layer1FE;
+ StrictMock<mock::LayerFE> mOutput3Layer2FE;
+
+ StrictMock<mock::Layer> mOutput2Layer1;
+ StrictMock<mock::Layer> mOutput3Layer1;
+ StrictMock<mock::Layer> mOutput3Layer2;
+
+ LayerFECompositionState mOutput2Layer1FEState;
+ LayerFECompositionState mOutput3Layer1FEState;
+ LayerFECompositionState mOutput3Layer2FEState;
+};
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoOutputs) {
+ mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoLayersBeingCursorLayers) {
+ EXPECT_CALL(mOutput2OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput3OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput3OutputLayer2, isHardwareCursor()).WillRepeatedly(Return(false));
+
+ mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+
+ mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesMultipleLayersBeingCursorLayers) {
+ {
+ InSequence seq;
+ EXPECT_CALL(mOutput2OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput2Layer1FE, latchCursorCompositionState(Ref(mOutput2Layer1FEState)));
+ EXPECT_CALL(mOutput2OutputLayer1, writeCursorPositionToHWC());
+ }
+
+ {
+ InSequence seq;
+ EXPECT_CALL(mOutput3OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput3Layer1FE, latchCursorCompositionState(Ref(mOutput3Layer1FEState)));
+ EXPECT_CALL(mOutput3OutputLayer1, writeCursorPositionToHWC());
+ }
+
+ {
+ InSequence seq;
+ EXPECT_CALL(mOutput3OutputLayer2, isHardwareCursor()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput3Layer2FE, latchCursorCompositionState(Ref(mOutput3Layer2FEState)));
+ EXPECT_CALL(mOutput3OutputLayer2, writeCursorPositionToHWC());
+ }
+
+ mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+
+ mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+/*
* CompositionEngine::preComposition
*/
-class PreCompositionTest : public CompositionEngineTest {
-public:
- PreCompositionTest() {
+struct CompositionTestPreComposition : public CompositionEngineTest {
+ CompositionTestPreComposition() {
EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(Return(mLayer1FE));
EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(Return(mLayer2FE));
EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(Return(mLayer3FE));
// getLayerFE() can return nullptr. Ensure that this is handled.
EXPECT_CALL(*mLayer4, getLayerFE()).WillRepeatedly(Return(nullptr));
-
- mRefreshArgs.outputs = {mOutput};
- mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
}
- std::shared_ptr<mock::Output> mOutput{std::make_shared<StrictMock<mock::Output>>()};
- std::shared_ptr<mock::Layer> mLayer1{std::make_shared<StrictMock<mock::Layer>>()};
- std::shared_ptr<mock::Layer> mLayer2{std::make_shared<StrictMock<mock::Layer>>()};
- std::shared_ptr<mock::Layer> mLayer3{std::make_shared<StrictMock<mock::Layer>>()};
- std::shared_ptr<mock::Layer> mLayer4{std::make_shared<StrictMock<mock::Layer>>()};
sp<StrictMock<mock::LayerFE>> mLayer1FE{new StrictMock<mock::LayerFE>()};
sp<StrictMock<mock::LayerFE>> mLayer2FE{new StrictMock<mock::LayerFE>()};
sp<StrictMock<mock::LayerFE>> mLayer3FE{new StrictMock<mock::LayerFE>()};
-
- CompositionRefreshArgs mRefreshArgs;
};
-TEST_F(PreCompositionTest, preCompositionSetsFrameTimestamp) {
+TEST_F(CompositionTestPreComposition, preCompositionSetsFrameTimestamp) {
const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC);
- CompositionRefreshArgs emptyArgs;
- mEngine.preComposition(emptyArgs);
+ mEngine.preComposition(mRefreshArgs);
const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC);
// The frame timestamp should be between the before and after timestamps
@@ -97,7 +238,7 @@
EXPECT_LE(mEngine.getLastFrameRefreshTimestamp(), after);
}
-TEST_F(PreCompositionTest, preCompositionInvokesLayerPreCompositionWithFrameTimestamp) {
+TEST_F(CompositionTestPreComposition, preCompositionInvokesLayerPreCompositionWithFrameTimestamp) {
nsecs_t ts1 = 0;
nsecs_t ts2 = 0;
nsecs_t ts3 = 0;
@@ -105,6 +246,9 @@
EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+ mRefreshArgs.outputs = {mOutput1};
+ mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+
mEngine.preComposition(mRefreshArgs);
// Each of the onPreComposition calls should used the same refresh timestamp
@@ -113,24 +257,31 @@
EXPECT_EQ(ts3, mEngine.getLastFrameRefreshTimestamp());
}
-TEST_F(PreCompositionTest, preCompositionDefaultsToNoUpdateNeeded) {
+TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
mEngine.setNeedsAnotherUpdateForTest(true);
+ mRefreshArgs.outputs = {mOutput1};
+ mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+
mEngine.preComposition(mRefreshArgs);
// The call should have cleared the needsAnotherUpdate flag
EXPECT_FALSE(mEngine.needsAnotherUpdate());
}
-TEST_F(PreCompositionTest, preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
+TEST_F(CompositionTestPreComposition,
+ preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+ mRefreshArgs.outputs = {mOutput1};
+ mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+
mEngine.preComposition(mRefreshArgs);
EXPECT_TRUE(mEngine.needsAnotherUpdate());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 7fce520..09ddc60 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -38,6 +38,8 @@
namespace {
using testing::_;
+using testing::InSequence;
+using testing::Ref;
using testing::Return;
using testing::ReturnRef;
using testing::StrictMock;
@@ -49,6 +51,31 @@
const mat4 kNonIdentityHalf = mat4() * 0.5;
const mat4 kNonIdentityQuarter = mat4() * 0.25;
+struct OutputPartialMockBase : public impl::Output {
+ // compositionengine::Output overrides
+ const OutputCompositionState& getState() const override { return mState; }
+ OutputCompositionState& editState() override { return mState; }
+
+ // Use mocks for all the remaining virtual functions
+ // not implemented by the base implementation class.
+ MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, compositionengine::OutputLayer*(size_t));
+ MOCK_METHOD3(ensureOutputLayer,
+ compositionengine::OutputLayer*(std::optional<size_t>,
+ const std::shared_ptr<compositionengine::Layer>&,
+ const sp<LayerFE>&));
+ MOCK_METHOD0(finalizePendingOutputLayers, void());
+ MOCK_METHOD0(clearOutputLayers, void());
+ MOCK_CONST_METHOD1(dumpState, void(std::string&));
+ MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
+ MOCK_METHOD2(injectOutputLayerForTest,
+ compositionengine::OutputLayer*(const std::shared_ptr<compositionengine::Layer>&,
+ const sp<LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+
+ impl::OutputCompositionState mState;
+};
+
struct OutputTest : public testing::Test {
class Output : public impl::Output {
public:
@@ -483,6 +510,31 @@
}
/*
+ * Output::setReleasedLayers()
+ */
+
+using OutputSetReleasedLayersTest = OutputTest;
+
+TEST_F(OutputSetReleasedLayersTest, setReleasedLayersTakesGivenLayers) {
+ sp<StrictMock<mock::LayerFE>> layer1FE{new StrictMock<mock::LayerFE>()};
+ sp<StrictMock<mock::LayerFE>> layer2FE{new StrictMock<mock::LayerFE>()};
+ sp<StrictMock<mock::LayerFE>> layer3FE{new StrictMock<mock::LayerFE>()};
+
+ Output::ReleasedLayers layers;
+ layers.push_back(layer1FE);
+ layers.push_back(layer2FE);
+ layers.push_back(layer3FE);
+
+ mOutput->setReleasedLayers(std::move(layers));
+
+ const auto& setLayers = mOutput->getReleasedLayersForTest();
+ ASSERT_EQ(3u, setLayers.size());
+ ASSERT_EQ(layer1FE.get(), setLayers[0].promote().get());
+ ASSERT_EQ(layer2FE.get(), setLayers[1].promote().get());
+ ASSERT_EQ(layer3FE.get(), setLayers[2].promote().get());
+}
+
+/*
* Output::updateAndWriteCompositionState()
*/
@@ -512,33 +564,10 @@
*/
struct OutputPrepareFrameTest : public testing::Test {
- struct OutputPartialMock : public impl::Output {
+ struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by prepareFrame to use a mock
// implementations.
MOCK_METHOD0(chooseCompositionStrategy, void());
-
- // compositionengine::Output overrides
- const OutputCompositionState& getState() const override { return mState; }
- OutputCompositionState& editState() override { return mState; }
-
- // These need implementations though are not expected to be called.
- MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
- MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
- compositionengine::OutputLayer*(size_t));
- MOCK_METHOD3(ensureOutputLayer,
- compositionengine::OutputLayer*(
- std::optional<size_t>,
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
- MOCK_METHOD0(finalizePendingOutputLayers, void());
- MOCK_METHOD0(clearOutputLayers, void());
- MOCK_CONST_METHOD1(dumpState, void(std::string&));
- MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
- MOCK_METHOD2(injectOutputLayerForTest,
- compositionengine::OutputLayer*(
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
- MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
-
- impl::OutputCompositionState mState;
};
OutputPrepareFrameTest() {
@@ -586,6 +615,73 @@
}
/*
+ * Output::present()
+ */
+
+struct OutputPresentTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // All child helper functions Output::present() are defined as mocks,
+ // and those are tested separately, allowing the present() test to
+ // just cover the high level flow.
+ MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(updateAndWriteCompositionState,
+ void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(beginFrame, void());
+ MOCK_METHOD0(prepareFrame, void());
+ MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(postFramebuffer, void());
+ };
+
+ StrictMock<OutputPartialMock> mOutput;
+};
+
+TEST_F(OutputPresentTest, justInvokesChildFunctionsInSequence) {
+ CompositionRefreshArgs args;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
+ EXPECT_CALL(mOutput, updateAndWriteCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
+ EXPECT_CALL(mOutput, beginFrame());
+ EXPECT_CALL(mOutput, prepareFrame());
+ EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
+ EXPECT_CALL(mOutput, finishFrame(Ref(args)));
+ EXPECT_CALL(mOutput, postFramebuffer());
+
+ mOutput.present(args);
+}
+
+/*
+ * Output::updateColorProfile()
+ */
+
+// TODO(b/144060211) - Add coverage
+
+/*
+ * Output::beginFrame()
+ */
+
+/*
+ * Output::devOptRepaintFlash()
+ */
+
+// TODO(b/144060211) - Add coverage
+
+/*
+ * Output::finishFrame()
+ */
+
+// TODO(b/144060211) - Add coverage
+
+/*
+ * Output::postFramebuffer()
+ */
+
+// TODO(b/144060211) - Add coverage
+
+/*
* Output::composeSurfaces()
*/
@@ -598,7 +694,7 @@
static const Rect kDefaultOutputScissor;
static const mat4 kDefaultColorTransformMat;
- struct OutputPartialMock : public impl::Output {
+ struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by composeSurfaces to use a mock
// implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
@@ -607,29 +703,6 @@
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<renderengine::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
-
- // compositionengine::Output overrides
- const OutputCompositionState& getState() const override { return mState; }
- OutputCompositionState& editState() override { return mState; }
-
- // These need implementations though are not expected to be called.
- MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
- MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
- compositionengine::OutputLayer*(size_t));
- MOCK_METHOD3(ensureOutputLayer,
- compositionengine::OutputLayer*(
- std::optional<size_t>,
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
- MOCK_METHOD0(finalizePendingOutputLayers, void());
- MOCK_METHOD0(clearOutputLayers, void());
- MOCK_CONST_METHOD1(dumpState, void(std::string&));
- MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
- MOCK_METHOD2(injectOutputLayerForTest,
- compositionengine::OutputLayer*(
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
- MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
-
- impl::OutputCompositionState mState;
};
OutputComposeSurfacesTest() {
@@ -718,36 +791,13 @@
*/
struct GenerateClientCompositionRequestsTest : public testing::Test {
- struct OutputPartialMock : public impl::Output {
+ struct OutputPartialMock : public OutputPartialMockBase {
// compositionengine::Output overrides
-
std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, Region& clearRegion) override {
return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
clearRegion);
}
-
- const OutputCompositionState& getState() const override { return mState; }
- OutputCompositionState& editState() override { return mState; }
-
- // These need implementations though are not expected to be called.
- MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
- MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
- compositionengine::OutputLayer*(size_t));
- MOCK_METHOD3(ensureOutputLayer,
- compositionengine::OutputLayer*(
- std::optional<size_t>,
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
- MOCK_METHOD0(finalizePendingOutputLayers, void());
- MOCK_METHOD0(clearOutputLayers, void());
- MOCK_CONST_METHOD1(dumpState, void(std::string&));
- MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
- MOCK_METHOD2(injectOutputLayerForTest,
- compositionengine::OutputLayer*(
- const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
- MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
-
- impl::OutputCompositionState mState;
};
GenerateClientCompositionRequestsTest() {
@@ -805,6 +855,7 @@
EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
+ EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
@@ -813,6 +864,7 @@
EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
+ EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
@@ -865,6 +917,7 @@
EXPECT_CALL(outputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0);
+ EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)).WillRepeatedly(Return(&outputLayer));
@@ -930,6 +983,7 @@
EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
+ EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
@@ -938,6 +992,7 @@
EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
+ EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index 3a0408e..6f91843 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -44,52 +44,52 @@
FrameTracerDataSource::Register(dsd);
}
-void FrameTracer::traceNewLayer(int32_t layerID, const std::string& layerName) {
- FrameTracerDataSource::Trace([this, layerID, &layerName](FrameTracerDataSource::TraceContext) {
- if (mTraceTracker.find(layerID) == mTraceTracker.end()) {
+void FrameTracer::traceNewLayer(int32_t layerId, const std::string& layerName) {
+ FrameTracerDataSource::Trace([this, layerId, &layerName](FrameTracerDataSource::TraceContext) {
+ if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
std::lock_guard<std::mutex> lock(mTraceMutex);
- mTraceTracker[layerID].layerName = layerName;
+ mTraceTracker[layerId].layerName = layerName;
}
});
}
-void FrameTracer::traceTimestamp(int32_t layerID, uint64_t bufferID, uint64_t frameNumber,
+void FrameTracer::traceTimestamp(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
nsecs_t timestamp, FrameEvent::BufferEventType type,
nsecs_t duration) {
- FrameTracerDataSource::Trace([this, layerID, bufferID, frameNumber, timestamp, type,
+ FrameTracerDataSource::Trace([this, layerId, bufferID, frameNumber, timestamp, type,
duration](FrameTracerDataSource::TraceContext ctx) {
std::lock_guard<std::mutex> lock(mTraceMutex);
- if (mTraceTracker.find(layerID) == mTraceTracker.end()) {
+ if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
return;
}
// Handle any pending fences for this buffer.
- tracePendingFencesLocked(ctx, layerID, bufferID);
+ tracePendingFencesLocked(ctx, layerId, bufferID);
// Complete current trace.
- traceLocked(ctx, layerID, bufferID, frameNumber, timestamp, type, duration);
+ traceLocked(ctx, layerId, bufferID, frameNumber, timestamp, type, duration);
});
}
-void FrameTracer::traceFence(int32_t layerID, uint64_t bufferID, uint64_t frameNumber,
+void FrameTracer::traceFence(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& fence,
FrameEvent::BufferEventType type, nsecs_t startTime) {
- FrameTracerDataSource::Trace([this, layerID, bufferID, frameNumber, &fence, type,
+ FrameTracerDataSource::Trace([this, layerId, bufferID, frameNumber, &fence, type,
startTime](FrameTracerDataSource::TraceContext ctx) {
const nsecs_t signalTime = fence->getSignalTime();
if (signalTime != Fence::SIGNAL_TIME_INVALID) {
std::lock_guard<std::mutex> lock(mTraceMutex);
- if (mTraceTracker.find(layerID) == mTraceTracker.end()) {
+ if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
return;
}
// Handle any pending fences for this buffer.
- tracePendingFencesLocked(ctx, layerID, bufferID);
+ tracePendingFencesLocked(ctx, layerId, bufferID);
if (signalTime != Fence::SIGNAL_TIME_PENDING) {
- traceSpanLocked(ctx, layerID, bufferID, frameNumber, type, startTime, signalTime);
+ traceSpanLocked(ctx, layerId, bufferID, frameNumber, type, startTime, signalTime);
} else {
- mTraceTracker[layerID].pendingFences[bufferID].push_back(
+ mTraceTracker[layerId].pendingFences[bufferID].push_back(
{.frameNumber = frameNumber,
.type = type,
.fence = fence,
@@ -100,9 +100,9 @@
}
void FrameTracer::tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx,
- int32_t layerID, uint64_t bufferID) {
- if (mTraceTracker[layerID].pendingFences.count(bufferID)) {
- auto& pendingFences = mTraceTracker[layerID].pendingFences[bufferID];
+ int32_t layerId, uint64_t bufferID) {
+ if (mTraceTracker[layerId].pendingFences.count(bufferID)) {
+ auto& pendingFences = mTraceTracker[layerId].pendingFences[bufferID];
for (size_t i = 0; i < pendingFences.size(); ++i) {
auto& pendingFence = pendingFences[i];
@@ -116,7 +116,7 @@
if (signalTime != Fence::SIGNAL_TIME_INVALID &&
systemTime() - signalTime < kFenceSignallingDeadline) {
- traceSpanLocked(ctx, layerID, bufferID, pendingFence.frameNumber, pendingFence.type,
+ traceSpanLocked(ctx, layerId, bufferID, pendingFence.frameNumber, pendingFence.type,
pendingFence.startTime, signalTime);
}
@@ -126,7 +126,7 @@
}
}
-void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID,
+void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
FrameEvent::BufferEventType type, nsecs_t duration) {
auto packet = ctx.NewTracePacket();
@@ -138,9 +138,9 @@
}
event->set_type(type);
- if (mTraceTracker.find(layerID) != mTraceTracker.end() &&
- !mTraceTracker[layerID].layerName.empty()) {
- const std::string& layerName = mTraceTracker[layerID].layerName;
+ if (mTraceTracker.find(layerId) != mTraceTracker.end() &&
+ !mTraceTracker[layerId].layerName.empty()) {
+ const std::string& layerName = mTraceTracker[layerId].layerName;
event->set_layer_name(layerName.c_str(), layerName.size());
}
@@ -149,7 +149,7 @@
}
}
-void FrameTracer::traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID,
+void FrameTracer::traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
uint64_t bufferID, uint64_t frameNumber,
FrameEvent::BufferEventType type, nsecs_t startTime,
nsecs_t endTime) {
@@ -159,12 +159,12 @@
timestamp = startTime;
duration = endTime - startTime;
}
- traceLocked(ctx, layerID, bufferID, frameNumber, timestamp, type, duration);
+ traceLocked(ctx, layerId, bufferID, frameNumber, timestamp, type, duration);
}
-void FrameTracer::onDestroy(int32_t layerID) {
+void FrameTracer::onDestroy(int32_t layerId) {
std::lock_guard<std::mutex> traceLock(mTraceMutex);
- mTraceTracker.erase(layerID);
+ mTraceTracker.erase(layerId);
}
std::string FrameTracer::miniDump() {
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.h b/services/surfaceflinger/FrameTracer/FrameTracer.h
index e91a750..ef5df90 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.h
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.h
@@ -47,21 +47,21 @@
void registerDataSource();
// Starts tracking a new layer for tracing. Needs to be called once before traceTimestamp() or
// traceFence() for each layer.
- void traceNewLayer(int32_t layerID, const std::string& layerName);
+ void traceNewLayer(int32_t layerId, const std::string& layerName);
// Creates a trace point at the timestamp provided.
- void traceTimestamp(int32_t layerID, uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
+ void traceTimestamp(int32_t layerId, uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
FrameEvent::BufferEventType type, nsecs_t duration = 0);
// Creates a trace point after the provided fence has been signalled. If a startTime is provided
// the trace will have be timestamped from startTime until fence signalling time. If no
// startTime is provided, a durationless trace point will be created timestamped at fence
// signalling time. If the fence hasn't signalled yet, the trace point will be created the next
// time after signalling a trace call for this buffer occurs.
- void traceFence(int32_t layerID, uint64_t bufferID, uint64_t frameNumber,
+ void traceFence(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& fence, FrameEvent::BufferEventType type,
nsecs_t startTime = 0);
// Takes care of cleanup when a layer is destroyed.
- void onDestroy(int32_t layerID);
+ void onDestroy(int32_t layerId);
std::string miniDump();
@@ -88,15 +88,15 @@
// Checks if any pending fences for a layer and buffer have signalled and, if they have, creates
// trace points for them.
- void tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID,
+ void tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
uint64_t bufferID);
// Creates a trace point by translating a start time and an end time to a timestamp and
// duration. If startTime is later than end time it sets end time as the timestamp and the
// duration to 0. Used by traceFence().
- void traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID,
+ void traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
uint64_t bufferID, uint64_t frameNumber, FrameEvent::BufferEventType type,
nsecs_t startTime, nsecs_t endTime);
- void traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID, uint64_t bufferID,
+ void traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId, uint64_t bufferID,
uint64_t frameNumber, nsecs_t timestamp, FrameEvent::BufferEventType type,
nsecs_t duration = 0);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 00d932d..ce9aab5 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -109,6 +109,7 @@
mCurrentState.hasColorTransform = false;
mCurrentState.colorSpaceAgnostic = false;
mCurrentState.metadata = args.metadata;
+ mCurrentState.shadowRadius = 0.f;
// drawing state & current state are identical
mDrawingState = mCurrentState;
@@ -1359,9 +1360,9 @@
void Layer::onDisconnect() {
Mutex::Autolock lock(mFrameEventHistoryMutex);
mFrameEventHistory.onDisconnect();
- const int32_t layerID = getSequence();
- mFlinger->mTimeStats->onDestroy(layerID);
- mFlinger->mFrameTracer->onDestroy(layerID);
+ const int32_t layerId = getSequence();
+ mFlinger->mTimeStats->onDestroy(layerId);
+ mFlinger->mFrameTracer->onDestroy(layerId);
}
void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -1369,6 +1370,8 @@
if (newTimestamps) {
mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
getName().c_str(), newTimestamps->postedTime);
+ mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
+ newTimestamps->acquireFence);
}
Mutex::Autolock lock(mFrameEventHistoryMutex);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index bed5ad3..286311b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -531,7 +531,7 @@
* called after composition.
* returns true if the layer latched a new buffer this frame.
*/
- virtual bool onPostComposition(const std::optional<DisplayId>& /*displayId*/,
+ virtual bool onPostComposition(sp<const DisplayDevice> /*displayDevice*/,
const std::shared_ptr<FenceTime>& /*glDoneFence*/,
const std::shared_ptr<FenceTime>& /*presentFence*/,
const CompositorTiming& /*compositorTiming*/) {
@@ -601,6 +601,8 @@
virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
+ virtual uint64_t getCurrentFrameNumber() const { return mCurrentFrameNumber; }
+
/*
* Returns if a frame is ready
*/
@@ -950,7 +952,7 @@
// The inherited shadow radius after taking into account the layer hierarchy. This is the
// final shadow radius for this layer. If a shadow is specified for a layer, then effective
// shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
- float mEffectiveShadowRadius;
+ float mEffectiveShadowRadius = 0.f;
// Returns true if the layer can draw shadows on its border.
virtual bool canDrawShadows() const { return true; }
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 15ac8ca..bd9aca1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -28,6 +28,7 @@
namespace android {
class Layer;
+class TestableScheduler;
namespace scheduler {
@@ -59,6 +60,7 @@
private:
friend class LayerHistoryTest;
+ friend TestableScheduler;
using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
using LayerInfos = std::vector<LayerPair>;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index f3b0d56..6d9dd43 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -25,6 +25,8 @@
: mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+ lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
+
// Buffers can come with a present time far in the future. That keeps them relevant.
mLastUpdatedTime = std::max(lastPresentTime, now);
mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 4870a3b..a90d05e 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -17,6 +17,7 @@
#include "OneShotTimer.h"
#include <chrono>
+#include <sstream>
#include <thread>
namespace android {
@@ -98,7 +99,7 @@
mTimeoutCallback();
}
}
-} // namespace scheduler
+}
void OneShotTimer::reset() {
{
@@ -108,5 +109,11 @@
mCondition.notify_all();
}
+std::string OneShotTimer::dump() const {
+ std::ostringstream stream;
+ stream << mInterval.count() << " ms";
+ return stream.str();
+}
+
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 921631e..b005754 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -39,8 +39,6 @@
const TimeoutCallback& timeoutCallback);
~OneShotTimer();
- const Interval& interval() const { return mInterval; }
-
// Initializes and turns on the idle timer.
void start();
// Stops the idle timer and any held resources.
@@ -48,6 +46,8 @@
// Resets the wakeup time and fires the reset callback.
void reset();
+ std::string dump() const;
+
private:
// Enum to track in what state is the timer.
enum class TimerState {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 71b3500..55fd603 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,6 +20,7 @@
#include "Scheduler.h"
+#include <android-base/stringprintf.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
@@ -66,9 +67,11 @@
mRefreshRateConfigs(refreshRateConfig) {
using namespace sysprop;
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.set_idle_timer_ms", value, "0");
- const int setIdleTimerMs = atoi(value);
+ if (property_get_bool("debug.sf.use_smart_90_for_video", 0) || use_smart_90_for_video(false)) {
+ mLayerHistory.emplace();
+ }
+
+ const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
@@ -327,26 +330,28 @@
}
void Scheduler::registerLayer(Layer* layer) {
- uint32_t defaultFps, performanceFps;
- if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
- defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
- const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
- ? RefreshRateType::DEFAULT
- : RefreshRateType::PERFORMANCE;
- performanceFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
- } else {
- defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
- performanceFps = defaultFps;
- }
- mLayerHistory.registerLayer(layer, defaultFps, performanceFps);
+ if (!mLayerHistory) return;
+
+ const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+ ? RefreshRateType::DEFAULT
+ : RefreshRateType::PERFORMANCE;
+
+ const auto lowFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
+ const auto highFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
+
+ mLayerHistory->registerLayer(layer, lowFps, highFps);
}
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, bool isHDR) {
- mLayerHistory.record(layer, presentTime, isHDR, systemTime());
+ if (mLayerHistory) {
+ mLayerHistory->record(layer, presentTime, isHDR, systemTime());
+ }
}
-void Scheduler::updateFpsBasedOnContent() {
- auto [refreshRate, isHDR] = mLayerHistory.summarize(systemTime());
+void Scheduler::chooseRefreshRateForContent() {
+ if (!mLayerHistory) return;
+
+ auto [refreshRate, isHDR] = mLayerHistory->summarize(systemTime());
const uint32_t refreshRateRound = std::round(refreshRate);
RefreshRateType newRefreshRateType;
{
@@ -393,7 +398,9 @@
// Touch event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- mLayerHistory.clear();
+ if (mLayerHistory) {
+ mLayerHistory->clear();
+ }
}
void Scheduler::setDisplayPowerState(bool normal) {
@@ -408,7 +415,9 @@
// Display Power event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- mLayerHistory.clear();
+ if (mLayerHistory) {
+ mLayerHistory->clear();
+ }
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -446,15 +455,17 @@
}
void Scheduler::dump(std::string& result) const {
- std::ostringstream stream;
- if (mIdleTimer) {
- stream << "+ Idle timer interval: " << mIdleTimer->interval().count() << " ms\n";
- }
- if (mTouchTimer) {
- stream << "+ Touch timer interval: " << mTouchTimer->interval().count() << " ms\n";
- }
+ using base::StringAppendF;
+ const char* const states[] = {"off", "on"};
- result.append(stream.str());
+ const bool supported = mRefreshRateConfigs.refreshRateSwitchingSupported();
+ StringAppendF(&result, "+ Refresh rate switching: %s\n", states[supported]);
+ StringAppendF(&result, "+ Content detection: %s\n", states[mLayerHistory.has_value()]);
+
+ StringAppendF(&result, "+ Idle timer: %s\n",
+ mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+ StringAppendF(&result, "+ Touch timer: %s\n\n",
+ mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
}
template <class T>
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c983475..346896c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -105,8 +105,8 @@
void registerLayer(Layer*);
void recordLayerHistory(Layer*, nsecs_t presentTime, bool isHDR);
- // Updates FPS based on the most content presented.
- void updateFpsBasedOnContent();
+ // Detects content using layer history, and selects a matching refresh rate.
+ void chooseRefreshRateForContent();
// Called by Scheduler to change refresh rate.
void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&);
@@ -184,8 +184,8 @@
std::unique_ptr<DispSync> mPrimaryDispSync;
std::unique_ptr<EventControlThread> mEventControlThread;
- // Historical information about individual layers. Used for predicting the refresh rate.
- scheduler::LayerHistory mLayerHistory;
+ // Used to choose refresh rate if content detection is enabled.
+ std::optional<scheduler::LayerHistory> mLayerHistory;
// Whether to use idle timer callbacks that support the kernel timer.
const bool mSupportKernelTimer;
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
new file mode 100644
index 0000000..2394ed2
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerTimer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/unistd.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cstdint>
+
+#include "Timer.h"
+
+namespace android::scheduler {
+
+static constexpr size_t kReadPipe = 0;
+static constexpr size_t kWritePipe = 1;
+
+template <class T, size_t N>
+constexpr size_t arrayLen(T (&)[N]) {
+ return N;
+}
+
+Timer::Timer()
+ : mTimerFd(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)),
+ mEpollFd(epoll_create1(EPOLL_CLOEXEC)) {
+ if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
+ ALOGE("could not create TimerDispatch mPipes");
+ };
+
+ mDispatchThread = std::thread(std::bind(&Timer::dispatch, this));
+}
+
+Timer::~Timer() {
+ endDispatch();
+ mDispatchThread.join();
+
+ close(mPipes[kWritePipe]);
+ close(mPipes[kReadPipe]);
+ close(mEpollFd);
+ close(mTimerFd);
+}
+
+void Timer::endDispatch() {
+ static constexpr unsigned char end = 'e';
+ write(mPipes[kWritePipe], &end, sizeof(end));
+}
+
+nsecs_t Timer::now() const {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+constexpr char const* timerTraceTag = "AlarmInNs";
+void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ ATRACE_INT64(timerTraceTag, fireIn);
+
+ using namespace std::literals;
+ static constexpr int ns_per_s =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+
+ mCallback = cb;
+
+ struct itimerspec old_timer;
+ struct itimerspec new_timer {
+ .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+ .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
+ .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
+ };
+
+ if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+ ALOGW("Failed to set timerfd");
+ }
+}
+
+void Timer::alarmCancel() {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ ATRACE_INT64(timerTraceTag, 0);
+
+ struct itimerspec old_timer;
+ struct itimerspec new_timer {
+ .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+ .it_value = {
+ .tv_sec = 0,
+ .tv_nsec = 0,
+ },
+ };
+
+ if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+ ALOGW("Failed to disarm timerfd");
+ }
+}
+
+void Timer::dispatch() {
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) {
+ ALOGW("Failed to set SCHED_FIFO on dispatch thread");
+ }
+
+ if (pthread_setname_np(pthread_self(), "TimerDispatch")) {
+ ALOGW("Failed to set thread name on dispatch thread");
+ }
+
+ enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE };
+ epoll_event timerEvent;
+ timerEvent.events = EPOLLIN;
+ timerEvent.data.u32 = DispatchType::TIMER;
+ if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) {
+ ALOGE("Error adding timer fd to epoll dispatch loop");
+ return;
+ }
+
+ epoll_event terminateEvent;
+ terminateEvent.events = EPOLLIN;
+ terminateEvent.data.u32 = DispatchType::TERMINATE;
+ if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) {
+ ALOGE("Error adding control fd to dispatch loop");
+ return;
+ }
+
+ uint64_t iteration = 0;
+ char const traceNamePrefix[] = "TimerIteration #";
+ static constexpr size_t max64print = std::numeric_limits<decltype(iteration)>::digits10;
+ static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print;
+ std::array<char, maxlen> str_buffer;
+ auto timing = true;
+ while (timing) {
+ epoll_event events[DispatchType::MAX_DISPATCH_TYPE];
+ int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
+
+ if (ATRACE_ENABLED()) {
+ snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix,
+ iteration++);
+ ATRACE_NAME(str_buffer.data());
+ }
+
+ if (nfds == -1) {
+ if (errno != EINTR) {
+ timing = false;
+ continue;
+ }
+ }
+
+ for (auto i = 0; i < nfds; i++) {
+ if (events[i].data.u32 == DispatchType::TIMER) {
+ static uint64_t mIgnored = 0;
+ read(mTimerFd, &mIgnored, sizeof(mIgnored));
+ std::function<void()> cb;
+ {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ cb = mCallback;
+ }
+ if (cb) {
+ cb();
+ }
+ }
+ if (events[i].data.u32 == DispatchType::TERMINATE) {
+ timing = false;
+ }
+ }
+ }
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
new file mode 100644
index 0000000..0ae82c8
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "TimeKeeper.h"
+
+#include <android-base/thread_annotations.h>
+#include <array>
+#include <thread>
+
+namespace android::scheduler {
+
+class Timer : public TimeKeeper {
+public:
+ Timer();
+ ~Timer();
+ nsecs_t now() const final;
+
+ // NB: alarmIn and alarmCancel are threadsafe; with the last-returning function being effectual
+ // Most users will want to serialize thes calls so as to be aware of the timer state.
+ void alarmIn(std::function<void()> const& cb, nsecs_t fireIn) final;
+ void alarmCancel() final;
+
+private:
+ int const mTimerFd;
+ int const mEpollFd;
+ std::array<int, 2> mPipes;
+
+ std::thread mDispatchThread;
+ void dispatch();
+ void endDispatch();
+
+ std::mutex mMutex;
+ std::function<void()> mCallback GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
new file mode 100644
index 0000000..643c5d2
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+#include "VSyncPredictor.h"
+#include <android-base/logging.h>
+#include <cutils/compiler.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <algorithm>
+#include <chrono>
+#include "SchedulerUtils.h"
+
+namespace android::scheduler {
+static auto constexpr kNeedsSamplesTag = "SamplesRequested";
+static auto constexpr kMaxPercent = 100u;
+
+VSyncPredictor::~VSyncPredictor() = default;
+
+VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize,
+ size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
+ : kHistorySize(historySize),
+ kMinimumSamplesForPrediction(minimumSamplesForPrediction),
+ kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
+ mIdealPeriod(idealPeriod) {
+ mRateMap[mIdealPeriod] = {idealPeriod, 0};
+}
+
+inline size_t VSyncPredictor::next(int i) const {
+ return (i + 1) % timestamps.size();
+}
+
+bool VSyncPredictor::validate(nsecs_t timestamp) const {
+ if (lastTimestampIndex < 0 || timestamps.empty()) {
+ return true;
+ }
+
+ auto const aValidTimestamp = timestamps[lastTimestampIndex];
+ auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod;
+ return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
+}
+
+void VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+ std::lock_guard<std::mutex> lk(mMutex);
+
+ if (!validate(timestamp)) {
+ ALOGW("timestamp was too far off the last known timestamp");
+ return;
+ }
+
+ if (timestamps.size() != kHistorySize) {
+ timestamps.push_back(timestamp);
+ lastTimestampIndex = next(lastTimestampIndex);
+ } else {
+ lastTimestampIndex = next(lastTimestampIndex);
+ timestamps[lastTimestampIndex] = timestamp;
+ }
+
+ if (timestamps.size() < kMinimumSamplesForPrediction) {
+ mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+ return;
+ }
+
+ // This is a 'simple linear regression' calculation of Y over X, with Y being the
+ // vsync timestamps, and X being the ordinal of vsync count.
+ // The calculated slope is the vsync period.
+ // Formula for reference:
+ // Sigma_i: means sum over all timestamps.
+ // mean(variable): statistical mean of variable.
+ // X: snapped ordinal of the timestamp
+ // Y: vsync timestamp
+ //
+ // Sigma_i( (X_i - mean(X)) * (Y_i - mean(Y) )
+ // slope = -------------------------------------------
+ // Sigma_i ( X_i - mean(X) ) ^ 2
+ //
+ // intercept = mean(Y) - slope * mean(X)
+ //
+ std::vector<nsecs_t> vsyncTS(timestamps.size());
+ std::vector<nsecs_t> ordinals(timestamps.size());
+
+ // normalizing to the oldest timestamp cuts down on error in calculating the intercept.
+ auto const oldest_ts = *std::min_element(timestamps.begin(), timestamps.end());
+ auto it = mRateMap.find(mIdealPeriod);
+ auto const currentPeriod = std::get<0>(it->second);
+ // TODO (b/144707443): its important that there's some precision in the mean of the ordinals
+ // for the intercept calculation, so scale the ordinals by 10 to continue
+ // fixed point calculation. Explore expanding
+ // scheduler::utils::calculate_mean to have a fixed point fractional part.
+ static constexpr int kScalingFactor = 10;
+
+ for (auto i = 0u; i < timestamps.size(); i++) {
+ vsyncTS[i] = timestamps[i] - oldest_ts;
+ ordinals[i] = ((vsyncTS[i] + (currentPeriod / 2)) / currentPeriod) * kScalingFactor;
+ }
+
+ auto meanTS = scheduler::calculate_mean(vsyncTS);
+ auto meanOrdinal = scheduler::calculate_mean(ordinals);
+ for (auto i = 0; i < vsyncTS.size(); i++) {
+ vsyncTS[i] -= meanTS;
+ ordinals[i] -= meanOrdinal;
+ }
+
+ auto top = 0ll;
+ auto bottom = 0ll;
+ for (auto i = 0; i < vsyncTS.size(); i++) {
+ top += vsyncTS[i] * ordinals[i];
+ bottom += ordinals[i] * ordinals[i];
+ }
+
+ if (CC_UNLIKELY(bottom == 0)) {
+ it->second = {mIdealPeriod, 0};
+ return;
+ }
+
+ nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
+ nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
+
+ it->second = {anticipatedPeriod, intercept};
+
+ ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
+ anticipatedPeriod, intercept);
+}
+
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
+ std::lock_guard<std::mutex> lk(mMutex);
+
+ auto const [slope, intercept] = getVSyncPredictionModel(lk);
+
+ if (timestamps.empty()) {
+ auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
+ auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
+ return knownTimestamp + numPeriodsOut * mIdealPeriod;
+ }
+
+ auto const oldest = *std::min_element(timestamps.begin(), timestamps.end());
+ auto const ordinalRequest = (timePoint - oldest + slope) / slope;
+ auto const prediction = (ordinalRequest * slope) + intercept + oldest;
+
+ ALOGV("prediction made from: %" PRId64 " prediction: %" PRId64 " (+%" PRId64 ") slope: %" PRId64
+ " intercept: %" PRId64,
+ timePoint, prediction, prediction - timePoint, slope, intercept);
+ return prediction;
+}
+
+std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ return VSyncPredictor::getVSyncPredictionModel(lk);
+}
+
+std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
+ std::lock_guard<std::mutex> const&) const {
+ return mRateMap.find(mIdealPeriod)->second;
+}
+
+void VSyncPredictor::setPeriod(nsecs_t period) {
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lk(mMutex);
+ static constexpr size_t kSizeLimit = 30;
+ if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
+ mRateMap.erase(mRateMap.begin());
+ }
+
+ mIdealPeriod = period;
+ if (mRateMap.find(period) == mRateMap.end()) {
+ mRateMap[mIdealPeriod] = {period, 0};
+ }
+
+ if (!timestamps.empty()) {
+ mKnownTimestamp = *std::max_element(timestamps.begin(), timestamps.end());
+ timestamps.clear();
+ lastTimestampIndex = 0;
+ }
+}
+
+bool VSyncPredictor::needsMoreSamples(nsecs_t now) const {
+ using namespace std::literals::chrono_literals;
+ std::lock_guard<std::mutex> lk(mMutex);
+ bool needsMoreSamples = true;
+ if (timestamps.size() >= kMinimumSamplesForPrediction) {
+ nsecs_t constexpr aLongTime =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
+ if (!(lastTimestampIndex < 0 || timestamps.empty())) {
+ auto const lastTimestamp = timestamps[lastTimestampIndex];
+ needsMoreSamples = !((lastTimestamp + aLongTime) > now);
+ }
+ }
+
+ ATRACE_INT(kNeedsSamplesTag, needsMoreSamples);
+ return needsMoreSamples;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
new file mode 100644
index 0000000..1590f49
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+class VSyncPredictor : public VSyncTracker {
+public:
+ /*
+ * \param [in] idealPeriod The initial ideal period to use.
+ * \param [in] historySize The internal amount of entries to store in the model.
+ * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
+ * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
+ * samples that fall outlierTolerancePercent from an anticipated vsync event.
+ */
+ VSyncPredictor(nsecs_t idealPeriod, size_t historySize, size_t minimumSamplesForPrediction,
+ uint32_t outlierTolerancePercent);
+ ~VSyncPredictor();
+
+ void addVsyncTimestamp(nsecs_t timestamp) final;
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
+
+ /*
+ * Inform the model that the period is anticipated to change to a new value.
+ * model will use the period parameter to predict vsync events until enough
+ * timestamps with the new period have been collected.
+ *
+ * \param [in] period The new period that should be used.
+ */
+ void setPeriod(nsecs_t period);
+
+ /* Query if the model is in need of more samples to make a prediction at timePoint.
+ * \param [in] timePoint The timePoint to inquire of.
+ * \return True, if model would benefit from more samples, False if not.
+ */
+ bool needsMoreSamples(nsecs_t timePoint) const;
+
+ std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+
+private:
+ VSyncPredictor(VSyncPredictor const&) = delete;
+ VSyncPredictor& operator=(VSyncPredictor const&) = delete;
+
+ size_t const kHistorySize;
+ size_t const kMinimumSamplesForPrediction;
+ size_t const kOutlierTolerancePercent;
+
+ std::mutex mutable mMutex;
+ size_t next(int i) const REQUIRES(mMutex);
+ bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
+ std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
+ REQUIRES(mMutex);
+
+ nsecs_t mIdealPeriod GUARDED_BY(mMutex);
+ std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
+
+ std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
+
+ int lastTimestampIndex GUARDED_BY(mMutex) = 0;
+ std::vector<nsecs_t> timestamps GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 01daa3e..14a2ab1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -356,14 +356,6 @@
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
- mUseSmart90ForVideo = use_smart_90_for_video(false);
- property_get("debug.sf.use_smart_90_for_video", value, "0");
-
- int int_value = atoi(value);
- if (int_value) {
- mUseSmart90ForVideo = true;
- }
-
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
@@ -1643,11 +1635,7 @@
mGpuFrameMissedCount++;
}
- if (mUseSmart90ForVideo) {
- // This call is made each time SF wakes up and creates a new frame. It is part
- // of video detection feature.
- mScheduler->updateFpsBasedOnContent();
- }
+ mScheduler->chooseRefreshRateForContent();
if (performSetActiveConfig()) {
break;
@@ -1727,7 +1715,12 @@
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (sp<Layer> layer : mLayersWithQueuedFrames) {
auto compositionLayer = layer->getCompositionLayer();
- if (compositionLayer) refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
+ if (compositionLayer) {
+ refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
+ mFrameTracer->traceTimestamp(layer->getSequence(), layer->getCurrentBufferId(),
+ layer->getCurrentFrameNumber(), systemTime(),
+ FrameTracer::FrameEvent::HWC_COMPOSITION_QUEUED);
+ }
}
refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
@@ -1908,9 +1901,8 @@
}
mDrawingState.traverseInZOrder([&](Layer* layer) {
- bool frameLatched =
- layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
- presentFenceTime, compositorTiming);
+ bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
+ presentFenceTime, compositorTiming);
if (frameLatched) {
recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
}
@@ -3726,6 +3718,8 @@
if (currentMode == HWC_POWER_MODE_OFF) {
// Turn on the display
+ // TODO: @vhau temp fix only! See b/141111965
+ mTransactionCompletedThread.clearAllPending();
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
@@ -3947,9 +3941,6 @@
void SurfaceFlinger::dumpVSync(std::string& result) const {
mScheduler->dump(result);
- StringAppendF(&result, "+ Refresh rate switching: %s\n",
- mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off");
- StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
mRefreshRateStats->dump(result);
result.append("\n");
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e6ebb35..50b3ae4 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -503,9 +503,9 @@
using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
struct ActiveConfigInfo {
- RefreshRateType type;
- int configId;
- Scheduler::ConfigEvent event;
+ RefreshRateType type = RefreshRateType::DEFAULT;
+ int configId = 0;
+ Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
bool operator!=(const ActiveConfigInfo& other) const {
return type != other.type || configId != other.configId || event != other.event;
@@ -1093,7 +1093,6 @@
/* ------------------------------------------------------------------------
* Scheduler
*/
- bool mUseSmart90ForVideo = false;
std::unique_ptr<Scheduler> mScheduler;
scheduler::ConnectionHandle mAppConnectionHandle;
scheduler::ConnectionHandle mSfConnectionHandle;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 3e47ec6..611afce 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -113,9 +113,9 @@
mTimeStats.clientCompositionFrames++;
}
-bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) {
+bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
if (!timeRecord->ready) {
- ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerId,
timeRecord->frameTime.frameNumber);
return false;
}
@@ -128,7 +128,7 @@
timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime();
timeRecord->acquireFence = nullptr;
} else {
- ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerId,
timeRecord->frameTime.frameNumber);
}
}
@@ -141,7 +141,7 @@
timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime();
timeRecord->presentFence = nullptr;
} else {
- ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerId,
timeRecord->frameTime.frameNumber);
}
}
@@ -155,15 +155,15 @@
return static_cast<int32_t>(delta);
}
-void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) {
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) {
ATRACE_CALL();
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
while (!timeRecords.empty()) {
- if (!recordReadyLocked(layerID, &timeRecords[0])) break;
- ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
+ if (!recordReadyLocked(layerId, &timeRecords[0])) break;
+ ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
if (prevTimeRecord.ready) {
@@ -178,37 +178,37 @@
const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
timeRecords[0].frameTime.acquireTime);
- ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerId,
timeRecords[0].frameTime.frameNumber, postToAcquireMs);
timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs);
const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerId,
timeRecords[0].frameTime.frameNumber, postToPresentMs);
timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerId,
timeRecords[0].frameTime.frameNumber, acquireToPresentMs);
timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerId,
timeRecords[0].frameTime.frameNumber, latchToPresentMs);
timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerId,
timeRecords[0].frameTime.frameNumber, desiredToPresentMs);
timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID,
+ ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
timeRecords[0].frameTime.frameNumber, presentToPresentMs);
timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
}
@@ -227,28 +227,28 @@
layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
}
-void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerID, frameNumber, layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerId, frameNumber, layerName.c_str(),
postTime);
std::lock_guard<std::mutex> lock(mMutex);
if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
return;
}
- if (!mTimeStatsTracker.count(layerID) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
+ if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
layerNameIsValid(layerName)) {
- mTimeStatsTracker[layerID].layerName = layerName;
+ mTimeStatsTracker[layerId].layerName = layerName;
}
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.",
- layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
- mTimeStatsTracker.erase(layerID);
+ layerId, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
+ mTimeStatsTracker.erase(layerId);
return;
}
// For most media content, the acquireFence is invalid because the buffer is
@@ -270,15 +270,15 @@
layerRecord.waitData = layerRecord.timeRecords.size() - 1;
}
-void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) {
+void TimeStats::setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime);
+ ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerId, frameNumber, latchTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
@@ -288,15 +288,15 @@
}
}
-void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) {
+void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime);
+ ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerId, frameNumber, desiredTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
@@ -306,15 +306,15 @@
}
}
-void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) {
+void TimeStats::setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime);
+ ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerId, frameNumber, acquireTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
@@ -324,17 +324,17 @@
}
}
-void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber,
+void TimeStats::setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber,
+ ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerId, frameNumber,
acquireFence->getSignalTime());
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
@@ -344,15 +344,15 @@
}
}
-void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime);
+ ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerId, frameNumber, presentTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
@@ -363,20 +363,20 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerID);
+ flushAvailableRecordsToStatsLocked(layerId);
}
-void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber,
+void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber,
+ ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerId, frameNumber,
presentFence->getSignalTime());
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
if (layerRecord.waitData < 0 ||
layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
return;
@@ -387,25 +387,25 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerID);
+ flushAvailableRecordsToStatsLocked(layerId);
}
-void TimeStats::onDestroy(int32_t layerID) {
+void TimeStats::onDestroy(int32_t layerId) {
ATRACE_CALL();
- ALOGV("[%d]-onDestroy", layerID);
+ ALOGV("[%d]-onDestroy", layerId);
std::lock_guard<std::mutex> lock(mMutex);
- mTimeStatsTracker.erase(layerID);
+ mTimeStatsTracker.erase(layerId);
}
-void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
+void TimeStats::removeTimeRecord(int32_t layerId, uint64_t frameNumber) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber);
+ ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerId, frameNumber);
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+ if (!mTimeStatsTracker.count(layerId)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerId];
size_t removeAt = 0;
for (const TimeRecord& record : layerRecord.timeRecords) {
if (record.frameTime.frameNumber == frameNumber) break;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 1313132..6e71f5a 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -44,20 +44,20 @@
virtual void incrementMissedFrames() = 0;
virtual void incrementClientCompositionFrames() = 0;
- virtual void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+ virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) = 0;
- virtual void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) = 0;
- virtual void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) = 0;
- virtual void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) = 0;
- virtual void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+ virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
+ virtual void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) = 0;
+ virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
+ virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) = 0;
- virtual void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) = 0;
- virtual void setPresentFence(int32_t layerID, uint64_t frameNumber,
+ virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
+ virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) = 0;
// Clean up the layer record
- virtual void onDestroy(int32_t layerID) = 0;
+ virtual void onDestroy(int32_t layerId) = 0;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
- virtual void removeTimeRecord(int32_t layerID, uint64_t frameNumber) = 0;
+ virtual void removeTimeRecord(int32_t layerId, uint64_t frameNumber) = 0;
virtual void setPowerMode(int32_t powerMode) = 0;
// Source of truth is RefrehRateStats.
@@ -116,20 +116,20 @@
void incrementMissedFrames() override;
void incrementClientCompositionFrames() override;
- void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+ void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) override;
- void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) override;
- void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) override;
- void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) override;
- void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+ void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
+ void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) override;
+ void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
+ void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) override;
- void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) override;
- void setPresentFence(int32_t layerID, uint64_t frameNumber,
+ void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
+ void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) override;
// Clean up the layer record
- void onDestroy(int32_t layerID) override;
+ void onDestroy(int32_t layerId) override;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
- void removeTimeRecord(int32_t layerID, uint64_t frameNumber) override;
+ void removeTimeRecord(int32_t layerId, uint64_t frameNumber) override;
void setPowerMode(int32_t powerMode) override;
// Source of truth is RefrehRateStats.
@@ -139,8 +139,8 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
- void flushAvailableRecordsToStatsLocked(int32_t layerID);
+ bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
+ void flushAvailableRecordsToStatsLocked(int32_t layerId);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
@@ -152,7 +152,7 @@
std::atomic<bool> mEnabled = false;
std::mutex mMutex;
TimeStatsHelper::TimeStatsGlobal mTimeStats;
- // Hashmap for LayerRecord with layerID as the hash key
+ // Hashmap for LayerRecord with layerId as the hash key
std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
PowerTime mPowerTime;
GlobalRecord mGlobalRecord;
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 8db03db..c15355d 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -189,6 +189,15 @@
return NO_ERROR;
}
+void TransactionCompletedThread::clearAllPending() {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ return;
+ }
+ mPendingTransactions.clear();
+ mConditionVariable.notify_all();
+}
+
status_t TransactionCompletedThread::registerUnpresentedCallbackHandle(
const sp<CallbackHandle>& handle) {
std::lock_guard lock(mMutex);
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index 12ea8fe..cd95bfb 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -70,6 +70,8 @@
// Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+ void clearAllPending();
+
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 78114a1..0c4a752 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -55,6 +55,8 @@
"TransactionApplicationTest.cpp",
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
+ "VSyncDispatchRealtimeTest.cpp",
+ "VSyncPredictorTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 6d38373..32f997f 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -810,6 +810,9 @@
sp<L> layer = factory();
+ // Layer should be registered with scheduler.
+ EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize());
+
Mock::VerifyAndClear(test->mComposer);
Mock::VerifyAndClear(test->mRenderEngine);
Mock::VerifyAndClear(test->mMessageQueue);
@@ -845,6 +848,10 @@
test->mDisplay->getCompositionDisplay()->clearOutputLayers();
test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+
+ // Layer should be unregistered with scheduler.
+ test->mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+ EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize());
}
};
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index b5af591..c334bcf 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -82,8 +82,8 @@
"FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
const std::string layerName = "co.layername#0";
- const int32_t layerID = 5;
- mFrameTracer->traceNewLayer(layerID, layerName);
+ const int32_t layerId = 5;
+ mFrameTracer->traceNewLayer(layerId, layerName);
EXPECT_EQ(mFrameTracer->miniDump(),
"FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
@@ -92,7 +92,7 @@
tracingSession->StartBlocking();
EXPECT_EQ(mFrameTracer->miniDump(),
"FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
- mFrameTracer->traceNewLayer(layerID, layerName);
+ mFrameTracer->traceNewLayer(layerId, layerName);
EXPECT_EQ(mFrameTracer->miniDump(),
"FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
tracingSession->StopBlocking();
@@ -103,31 +103,31 @@
"FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
const std::string layerName = "co.layername#0";
- const int32_t layerID = 5;
- const int32_t secondLayerID = 6;
+ const int32_t layerId = 5;
+ const int32_t secondlayerId = 6;
auto tracingSession = getTracingSessionForTest();
tracingSession->StartBlocking();
- mFrameTracer->traceNewLayer(layerID, layerName);
- mFrameTracer->traceNewLayer(secondLayerID, layerName);
+ mFrameTracer->traceNewLayer(layerId, layerName);
+ mFrameTracer->traceNewLayer(secondlayerId, layerName);
EXPECT_EQ(mFrameTracer->miniDump(),
"FrameTracer miniDump:\nNumber of layers currently being traced is 2\n");
tracingSession->StopBlocking();
- mFrameTracer->onDestroy(layerID);
+ mFrameTracer->onDestroy(layerId);
EXPECT_EQ(mFrameTracer->miniDump(),
"FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
- mFrameTracer->onDestroy(layerID);
+ mFrameTracer->onDestroy(layerId);
EXPECT_EQ(mFrameTracer->miniDump(),
"FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
- mFrameTracer->onDestroy(secondLayerID);
+ mFrameTracer->onDestroy(secondlayerId);
EXPECT_EQ(mFrameTracer->miniDump(),
"FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
}
TEST_F(FrameTracerTest, canTraceAfterAddingLayer) {
const std::string layerName = "co.layername#0";
- const int32_t layerID = 1;
+ const int32_t layerId = 1;
const uint32_t bufferID = 2;
const uint64_t frameNumber = 3;
const nsecs_t timestamp = 4;
@@ -141,9 +141,9 @@
// Clean up irrelevant traces.
tracingSession->ReadTraceBlocking();
- mFrameTracer->traceTimestamp(layerID, bufferID, frameNumber, timestamp, type, duration);
+ mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
// Create second trace packet to finalize the previous one.
- mFrameTracer->traceTimestamp(layerID, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+ mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -157,10 +157,10 @@
// Clean up irrelevant traces.
tracingSession->ReadTraceBlocking();
- mFrameTracer->traceNewLayer(layerID, layerName);
- mFrameTracer->traceTimestamp(layerID, bufferID, frameNumber, timestamp, type, duration);
+ mFrameTracer->traceNewLayer(layerId, layerName);
+ mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
// Create second trace packet to finalize the previous one.
- mFrameTracer->traceTimestamp(layerID, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+ mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -191,7 +191,7 @@
TEST_F(FrameTracerTest, traceFenceTriggersOnNextTraceAfterFenceFired) {
const std::string layerName = "co.layername#0";
- const int32_t layerID = 5;
+ const int32_t layerId = 5;
const uint32_t bufferID = 4;
const uint64_t frameNumber = 3;
const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
@@ -204,10 +204,10 @@
// Clean up irrelevant traces.
tracingSession->ReadTraceBlocking();
// Trace.
- mFrameTracer->traceNewLayer(layerID, layerName);
- mFrameTracer->traceFence(layerID, bufferID, frameNumber, fenceTime, type);
+ mFrameTracer->traceNewLayer(layerId, layerName);
+ mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
// Create extra trace packet to (hopefully not) trigger and finalize the fence packet.
- mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+ mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
EXPECT_EQ(raw_trace.size(), 0);
@@ -219,12 +219,12 @@
tracingSession->StartBlocking();
// Clean up irrelevant traces.
tracingSession->ReadTraceBlocking();
- mFrameTracer->traceNewLayer(layerID, layerName);
- mFrameTracer->traceFence(layerID, bufferID, frameNumber, fenceTime, type);
+ mFrameTracer->traceNewLayer(layerId, layerName);
+ mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
const nsecs_t timestamp = systemTime();
fenceFactory.signalAllForTest(Fence::NO_FENCE, timestamp);
// Create extra trace packet to trigger and finalize fence trace packets.
- mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+ mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -254,7 +254,7 @@
TEST_F(FrameTracerTest, traceFenceWithStartTimeAfterSignalTime_ShouldHaveNoDuration) {
const std::string layerName = "co.layername#0";
- const int32_t layerID = 5;
+ const int32_t layerId = 5;
const uint32_t bufferID = 4;
const uint64_t frameNumber = 3;
const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
@@ -264,24 +264,24 @@
tracingSession->StartBlocking();
// Clean up irrelevant traces.
tracingSession->ReadTraceBlocking();
- mFrameTracer->traceNewLayer(layerID, layerName);
+ mFrameTracer->traceNewLayer(layerId, layerName);
// traceFence called after fence signalled.
const nsecs_t signalTime1 = systemTime();
const nsecs_t startTime1 = signalTime1 + 100000;
auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1);
- mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence1, type, startTime1);
+ mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence1, type, startTime1);
// traceFence called before fence signalled.
const nsecs_t signalTime2 = systemTime();
const nsecs_t startTime2 = signalTime2 + 100000;
auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence2, type, startTime2);
+ mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence2, type, startTime2);
fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2);
// Create extra trace packet to trigger and finalize fence trace packets.
- mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+ mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -309,7 +309,7 @@
TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) {
const std::string layerName = "co.layername#0";
- const int32_t layerID = 5;
+ const int32_t layerId = 5;
const uint32_t bufferID = 4;
const uint64_t frameNumber = 3;
const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
@@ -321,11 +321,11 @@
tracingSession->StartBlocking();
// Clean up irrelevant traces.
tracingSession->ReadTraceBlocking();
- mFrameTracer->traceNewLayer(layerID, layerName);
- mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence, type);
+ mFrameTracer->traceNewLayer(layerId, layerName);
+ mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence, type);
fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime);
// Create extra trace packet to trigger and finalize any previous fence packets.
- mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+ mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -334,7 +334,7 @@
TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
const std::string layerName = "co.layername#0";
- const int32_t layerID = 5;
+ const int32_t layerId = 5;
const uint32_t bufferID = 4;
const uint64_t frameNumber = 3;
const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
@@ -345,24 +345,24 @@
tracingSession->StartBlocking();
// Clean up irrelevant traces.
tracingSession->ReadTraceBlocking();
- mFrameTracer->traceNewLayer(layerID, layerName);
+ mFrameTracer->traceNewLayer(layerId, layerName);
// traceFence called after fence signalled.
const nsecs_t signalTime1 = systemTime();
const nsecs_t startTime1 = signalTime1 - duration;
auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1);
- mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence1, type, startTime1);
+ mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence1, type, startTime1);
// traceFence called before fence signalled.
const nsecs_t signalTime2 = systemTime();
const nsecs_t startTime2 = signalTime2 - duration;
auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence2, type, startTime2);
+ mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence2, type, startTime2);
fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2);
// Create extra trace packet to trigger and finalize fence trace packets.
- mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+ mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
tracingSession->StopBlocking();
std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 9a962bc..e93d31e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -29,10 +29,10 @@
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
- LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
- const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
+ LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
- size_t layerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mLayerInfos.size(); }
+ size_t layerCount() const { return mScheduler->layerHistorySize(); }
size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ae6aa89..40c00c4 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -21,6 +21,7 @@
#include "Scheduler/DispSync.h"
#include "Scheduler/EventThread.h"
+#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
namespace android {
@@ -28,18 +29,26 @@
class TestableScheduler : public Scheduler {
public:
explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
- : Scheduler([](bool) {}, configs) {}
+ : Scheduler([](bool) {}, configs) {
+ mLayerHistory.emplace();
+ }
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs)
- : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {}
+ : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {
+ mLayerHistory.emplace();
+ }
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
return Scheduler::createConnection(std::move(eventThread));
}
+ size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
+ return mLayerHistory->mLayerInfos.size();
+ }
+
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 4eb9ec3..7b60fa2 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -170,8 +170,8 @@
return result;
}
-static std::string genLayerName(int32_t layerID) {
- return (layerID < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerID);
+static std::string genLayerName(int32_t layerId) {
+ return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerId);
}
void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
@@ -560,22 +560,22 @@
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
for (size_t i = 0; i < 10000000; ++i) {
- const int32_t layerID = genRandomInt32(-1, 10);
+ const int32_t layerId = genRandomInt32(-1, 10);
const int32_t frameNumber = genRandomInt32(1, 10);
switch (genRandomInt32(0, 100)) {
case 0:
ALOGV("removeTimeRecord");
- ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerID, frameNumber));
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerId, frameNumber));
continue;
case 1:
ALOGV("onDestroy");
- ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerId));
continue;
}
TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
const int32_t ts = genRandomInt32(1, 1000000000);
- ALOGV("type[%d], layerID[%d], frameNumber[%d], ts[%d]", type, layerID, frameNumber, ts);
- setTimeStamp(type, layerID, frameNumber, ts);
+ ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
+ setTimeStamp(type, layerId, frameNumber, ts);
}
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
new file mode 100644
index 0000000..c012616
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/Timer.h"
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+using namespace testing;
+using namespace std::literals;
+
+namespace android::scheduler {
+
+template <typename Rep, typename Per>
+constexpr nsecs_t toNs(std::chrono::duration<Rep, Per> const& tp) {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(tp).count();
+}
+
+class FixedRateIdealStubTracker : public VSyncTracker {
+public:
+ FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
+
+ void addVsyncTimestamp(nsecs_t) final {}
+
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+ auto const floor = timePoint % mPeriod;
+ if (floor == 0) {
+ return timePoint;
+ }
+ return timePoint - floor + mPeriod;
+ }
+
+private:
+ nsecs_t const mPeriod;
+};
+
+class VRRStubTracker : public VSyncTracker {
+public:
+ VRRStubTracker(nsecs_t period) : mPeriod{period} {}
+
+ void addVsyncTimestamp(nsecs_t) final {}
+
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ auto const normalized_to_base = time_point - mBase;
+ auto const floor = (normalized_to_base) % mPeriod;
+ if (floor == 0) {
+ return time_point;
+ }
+ return normalized_to_base - floor + mPeriod + mBase;
+ }
+
+ void set_interval(nsecs_t interval, nsecs_t last_known) {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ mPeriod = interval;
+ mBase = last_known;
+ }
+
+private:
+ std::mutex mutable mMutex;
+ nsecs_t mPeriod;
+ nsecs_t mBase = 0;
+};
+
+struct VSyncDispatchRealtimeTest : testing::Test {
+ static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
+ static size_t constexpr mIterations = 20;
+};
+
+class RepeatingCallbackReceiver {
+public:
+ RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl)
+ : mWorkload(wl),
+ mCallback(
+ dispatch, [&](auto time) { callback_called(time); }, "repeat0") {}
+
+ void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
+ mCallbackTimes.reserve(iterations);
+ mCallback.schedule(mWorkload, systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload);
+
+ for (auto i = 0u; i < iterations - 1; i++) {
+ std::unique_lock<decltype(mMutex)> lk(mMutex);
+ mCv.wait(lk, [&] { return mCalled; });
+ mCalled = false;
+ auto last = mLastTarget;
+ lk.unlock();
+
+ onEachFrame(last);
+
+ mCallback.schedule(mWorkload, last + mWorkload);
+ }
+
+ // wait for the last callback.
+ std::unique_lock<decltype(mMutex)> lk(mMutex);
+ mCv.wait(lk, [&] { return mCalled; });
+ }
+
+ void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
+ fn(mCallbackTimes);
+ }
+
+private:
+ void callback_called(nsecs_t time) {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ mCallbackTimes.push_back(time);
+ mCalled = true;
+ mLastTarget = time;
+ mCv.notify_all();
+ }
+
+ nsecs_t const mWorkload;
+ VSyncCallbackRegistration mCallback;
+
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ bool mCalled = false;
+ nsecs_t mLastTarget = 0;
+ std::vector<nsecs_t> mCallbackTimes;
+};
+
+TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
+ FixedRateIdealStubTracker tracker;
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+
+ static size_t constexpr num_clients = 3;
+ std::array<RepeatingCallbackReceiver, num_clients>
+ cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us)),
+ RepeatingCallbackReceiver(dispatch, toNs(0h)),
+ RepeatingCallbackReceiver(dispatch, toNs(1ms))};
+
+ auto const on_each_frame = [](nsecs_t) {};
+ std::array<std::thread, num_clients> threads{
+ std::thread([&] { cb_receiver[0].repeatedly_schedule(mIterations, on_each_frame); }),
+ std::thread([&] { cb_receiver[1].repeatedly_schedule(mIterations, on_each_frame); }),
+ std::thread([&] { cb_receiver[2].repeatedly_schedule(mIterations, on_each_frame); }),
+ };
+
+ for (auto it = threads.rbegin(); it != threads.rend(); it++) {
+ it->join();
+ }
+
+ for (auto const& cbs : cb_receiver) {
+ cbs.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+ }
+}
+
+// starts at 333hz, slides down to 43hz
+TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
+ auto next_vsync_interval = toNs(3ms);
+ VRRStubTracker tracker(next_vsync_interval);
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+
+ RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+
+ auto const on_each_frame = [&](nsecs_t last_known) {
+ tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
+ };
+
+ std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
+ eventThread.join();
+
+ cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+}
+
+// starts at 333hz, jumps to 200hz at frame 10
+TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
+ VRRStubTracker tracker(toNs(3ms));
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+
+ RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+
+ auto jump_frame_counter = 0u;
+ auto constexpr jump_frame_at = 10u;
+ auto const on_each_frame = [&](nsecs_t last_known) {
+ if (jump_frame_counter++ == jump_frame_at) {
+ tracker.set_interval(toNs(5ms), last_known);
+ }
+ };
+ std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
+ eventThread.join();
+
+ cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
new file mode 100644
index 0000000..d0c8090
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/VSyncPredictor.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <chrono>
+#include <utility>
+
+using namespace testing;
+using namespace std::literals;
+
+namespace android::scheduler {
+
+MATCHER_P2(IsCloseTo, value, tolerance, "is within tolerance") {
+ return arg <= value + tolerance && value >= value - tolerance;
+}
+
+std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs_t bias) {
+ std::vector<nsecs_t> vsyncs(count);
+ std::generate(vsyncs.begin(), vsyncs.end(),
+ [&, n = 0]() mutable { return n++ * period + bias; });
+ return vsyncs;
+}
+
+struct VSyncPredictorTest : testing::Test {
+ nsecs_t mNow = 0;
+ nsecs_t mPeriod = 1000;
+ static constexpr size_t kHistorySize = 10;
+ static constexpr size_t kMinimumSamplesForPrediction = 6;
+ static constexpr size_t kOutlierTolerancePercent = 25;
+ static constexpr nsecs_t mMaxRoundingError = 100;
+
+ VSyncPredictor tracker{mPeriod, kHistorySize, kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent};
+};
+
+TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+
+ EXPECT_THAT(slope, Eq(mPeriod));
+ EXPECT_THAT(intercept, Eq(0));
+
+ auto const changedPeriod = 2000;
+ tracker.setPeriod(changedPeriod);
+ std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, Eq(changedPeriod));
+ EXPECT_THAT(intercept, Eq(0));
+}
+
+TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_TRUE(tracker.needsMoreSamples(mNow += mPeriod));
+ tracker.addVsyncTimestamp(mNow);
+ }
+ EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+}
+
+TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) {
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ tracker.addVsyncTimestamp(mNow += mPeriod);
+ }
+ EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+
+ auto const changedPeriod = mPeriod * 2;
+ tracker.setPeriod(changedPeriod);
+ EXPECT_TRUE(tracker.needsMoreSamples(mNow));
+
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_TRUE(tracker.needsMoreSamples(mNow += changedPeriod));
+ tracker.addVsyncTimestamp(mNow);
+ }
+ EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+}
+
+TEST_F(VSyncPredictorTest, transitionsToModelledPointsAfterSynthetic) {
+ auto last = mNow;
+ auto const bias = 10;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod - bias;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ mNow += bias;
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
+}
+
+TEST_F(VSyncPredictorTest, uponNotifiedOfInaccuracyUsesSynthetic) {
+ auto const slightlyLessPeriod = mPeriod - 10;
+ auto const changedPeriod = mPeriod - 1;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ tracker.addVsyncTimestamp(mNow += slightlyLessPeriod);
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyLessPeriod));
+ tracker.setPeriod(changedPeriod);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + changedPeriod));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_60hzHighVariance) {
+ // these are precomputed simulated 16.6s vsyncs with uniform distribution +/- 1.6ms error
+ std::vector<nsecs_t> const simulatedVsyncs{
+ 15492949, 32325658, 49534984, 67496129, 84652891,
+ 100332564, 117737004, 132125931, 149291099, 165199602,
+ };
+ auto constexpr idealPeriod = 16600000;
+ auto constexpr expectedPeriod = 16639242;
+ auto constexpr expectedIntercept = 1049341;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_90hzLowVariance) {
+ // these are precomputed simulated 11.1 vsyncs with uniform distribution +/- 1ms error
+ std::vector<nsecs_t> const simulatedVsyncs{
+ 11167047, 22603464, 32538479, 44938134, 56321268,
+ 66730346, 78062637, 88171429, 99707843, 111397621,
+ };
+ auto idealPeriod = 11110000;
+ auto expectedPeriod = 11089413;
+ auto expectedIntercept = 94421;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelinesDiscontinuous_22hzLowVariance) {
+ // these are 11.1s vsyncs with low variance, randomly computed, between -1 and 1ms
+ std::vector<nsecs_t> const simulatedVsyncs{
+ 45259463, // 0
+ 91511026, // 1
+ 136307650, // 2
+ 1864501714, // 40
+ 1908641034, // 41
+ 1955278544, // 42
+ 4590180096, // 100
+ 4681594994, // 102
+ 5499224734, // 120
+ 5591378272, // 122
+ };
+ auto idealPeriod = 45454545;
+ auto expectedPeriod = 45450152;
+ auto expectedIntercept = 469647;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, againstOutliersDiscontinuous_500hzLowVariance) {
+ std::vector<nsecs_t> const simulatedVsyncs{
+ 1992548, // 0
+ 4078038, // 1
+ 6165794, // 2
+ 7958171, // 3
+ 10193537, // 4
+ 2401840200, // 1200
+ 2403000000, // an outlier that should be excluded (1201 and a half)
+ 2405803629, // 1202
+ 2408028599, // 1203
+ 2410121051, // 1204
+ };
+ auto idealPeriod = 2000000;
+ auto expectedPeriod = 1999892;
+ auto expectedIntercept = 175409;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, handlesVsyncChange) {
+ auto const fastPeriod = 100;
+ auto const fastTimeBase = 100;
+ auto const slowPeriod = 400;
+ auto const slowTimeBase = 800;
+ auto const simulatedVsyncsFast =
+ generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod, fastTimeBase);
+ auto const simulatedVsyncsSlow =
+ generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase);
+
+ tracker.setPeriod(fastPeriod);
+ for (auto const& timestamp : simulatedVsyncsFast) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+
+ auto const mMaxRoundingError = 100;
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+
+ tracker.setPeriod(slowPeriod);
+ for (auto const& timestamp : simulatedVsyncsSlow) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+ std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) {
+ auto const fastPeriod = 101000;
+ auto const fastTimeBase = fastPeriod - 500;
+ auto const fastPeriod2 = 99000;
+
+ auto const slowPeriod = 400000;
+ auto const slowTimeBase = 800000 - 201;
+ auto const simulatedVsyncsFast =
+ generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod, fastTimeBase);
+ auto const simulatedVsyncsSlow =
+ generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase);
+ auto const simulatedVsyncsFast2 =
+ generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod2, fastTimeBase);
+
+ auto idealPeriod = 100000;
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncsFast) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, Eq(fastPeriod));
+ EXPECT_THAT(intercept, Eq(0));
+
+ tracker.setPeriod(slowPeriod);
+ for (auto const& timestamp : simulatedVsyncsSlow) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+
+ // we had a model for 100ns mPeriod before, use that until the new samples are
+ // sufficiently built up
+ tracker.setPeriod(idealPeriod);
+ std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, Eq(fastPeriod));
+ EXPECT_THAT(intercept, Eq(0));
+
+ for (auto const& timestamp : simulatedVsyncsFast2) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+ std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, Eq(fastPeriod2));
+ EXPECT_THAT(intercept, Eq(0));
+}
+
+TEST_F(VSyncPredictorTest, willBecomeInaccurateAfterA_longTimeWithNoSamples) {
+ auto const simulatedVsyncs = generateVsyncTimestamps(kMinimumSamplesForPrediction, mPeriod, 0);
+
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+ auto const mNow = *simulatedVsyncs.rbegin();
+ EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+
+ // TODO: would be better to decay this as a result of the variance of the samples
+ static auto constexpr aLongTimeOut = 1000000000;
+ EXPECT_TRUE(tracker.needsMoreSamples(mNow + aLongTimeOut));
+}
+
+TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
+ auto const simulatedVsyncs =
+ generateVsyncTimestamps(kMinimumSamplesForPrediction + 1, mPeriod, 0);
+ nsecs_t const mNow = 0;
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mPeriod));
+
+ nsecs_t const aBitOfTime = 422;
+
+ for (auto i = 0; i < kMinimumSamplesForPrediction; i++) {
+ tracker.addVsyncTimestamp(simulatedVsyncs[i]);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(simulatedVsyncs[i] + aBitOfTime),
+ Eq(mPeriod + simulatedVsyncs[i]));
+ }
+
+ for (auto i = kMinimumSamplesForPrediction; i < simulatedVsyncs.size(); i++) {
+ tracker.addVsyncTimestamp(simulatedVsyncs[i]);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(simulatedVsyncs[i] + aBitOfTime),
+ Eq(mPeriod + simulatedVsyncs[i]));
+ }
+}
+
+} // namespace android::scheduler