Merge "opengl: Fix wrong comments"
diff --git a/.gitignore b/.gitignore
index 0d20b64..685e379 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
+*.iml
*.pyc
+.idea/
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0473bb8..a686dfb 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/idlcli/
include/input/
+ include/powermanager/
libs/binder/fuzzer/
libs/binder/ndk/
libs/binderthreadstate/
@@ -18,7 +19,9 @@
opengl/libs/
services/bufferhub/
services/inputflinger/
+ services/powermanager/
services/surfaceflinger/
+ services/vibratorservice/
services/vr/
vulkan/
diff --git a/cmds/bugreport/bugreport.cpp b/cmds/bugreport/bugreport.cpp
index 840ae47..e5c52d8 100644
--- a/cmds/bugreport/bugreport.cpp
+++ b/cmds/bugreport/bugreport.cpp
@@ -27,12 +27,20 @@
// dumpstate, then connect to the dumpstate local client to read the
// output. All of the dumpstate output is written to stdout, including
// any errors encountered while reading/writing the output.
-int main() {
-
+int main(int argc, char* /*argv*/[]) {
fprintf(stderr, "=============================================================================\n");
- fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport <zip_file> instead\n");
+ fprintf(stderr, "WARNING: Flat (text file, non-zipped) bugreports are deprecated.\n");
+ fprintf(stderr, "WARNING: Please generate zipped bugreports instead.\n");
+ fprintf(stderr, "WARNING: On the host use: adb bugreport filename.zip\n");
+ fprintf(stderr, "WARNING: On the device use: bugreportz\n");
+ fprintf(stderr, "WARNING: bugreportz will output the filename to use with adb pull.\n");
fprintf(stderr, "=============================================================================\n\n\n");
+ if (argc != 1) {
+ fprintf(stderr, "usage: bugreport\n");
+ exit(1);
+ }
+
// Start the dumpstate service.
property_set("ctl.start", "dumpstate");
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index 40346be..1d48e08 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -30,7 +30,7 @@
static void show_usage() {
fprintf(stderr,
- "usage: bugreportz [-h | -v]\n"
+ "usage: bugreportz [-hpv]\n"
" -h: to display this help message\n"
" -p: display progress\n"
" -v: to display the version\n"
@@ -64,6 +64,12 @@
}
}
+ // We don't support any non-option arguments.
+ if (optind != argc) {
+ show_usage();
+ return EXIT_FAILURE;
+ }
+
// TODO: code below was copy-and-pasted from bugreport.cpp (except by the
// timeout value);
// should be reused instead.
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index acca11a..ead491e 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -110,29 +110,20 @@
],
required: [
"atrace",
- "df",
- "getprop",
"ip",
"iptables",
- "ip6tables",
- "kill",
"librank",
"logcat",
"lpdump",
"lpdumpd",
- "lsmod",
- "lsof",
- "netstat",
- "printenv",
"procrank",
"screencap",
"showmap",
"ss",
"storaged",
- "top",
- "uptime",
+ "toolbox",
+ "toybox",
"vdc",
- "vril-dump",
],
init_rc: ["dumpstate.rc"],
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 942c16c..581d3de 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -209,6 +209,10 @@
return fd;
}
+static int OpenForWrite(std::string path) {
+ return Open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+}
static int OpenForRead(std::string path) {
return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
@@ -274,6 +278,27 @@
return version_code;
}
+static bool PathExists(const std::string& path) {
+ struct stat sb;
+ return stat(path.c_str(), &sb) == 0;
+}
+
+static bool CopyFileToFile(const std::string& input_file, const std::string& output_file) {
+ if (input_file == output_file) {
+ MYLOGD("Skipping copying bugreport file since the destination is the same (%s)\n",
+ output_file.c_str());
+ return false;
+ }
+ else if (PathExists(output_file)) {
+ MYLOGD("Cannot overwrite an existing file (%s)\n", output_file.c_str());
+ return false;
+ }
+
+ MYLOGD("Going to copy bugreport file (%s) to %s\n", input_file.c_str(), output_file.c_str());
+ android::base::unique_fd out_fd(OpenForWrite(output_file));
+ return CopyFileToFd(input_file, out_fd.get());
+}
+
} // namespace
} // namespace os
} // namespace android
@@ -2092,11 +2117,12 @@
static void ShowUsage() {
fprintf(stderr,
- "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] "
+ "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] "
"[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
" -h: display this help message\n"
" -b: play sound file instead of vibrate, at beginning of job\n"
" -e: play sound file instead of vibrate, at end of job\n"
+ " -o: write to custom directory (only in limited mode)\n"
" -d: append date to filename\n"
" -p: capture screenshot to filename.png\n"
" -z: generate zipped file\n"
@@ -2267,6 +2293,13 @@
do_text_file = false;
}
}
+
+ std::string final_path = ds.path_;
+ if (ds.options_->OutputToCustomFile()) {
+ final_path = ds.GetPath(ds.options_->out_dir, ".zip");
+ android::os::CopyFileToFile(ds.path_, final_path);
+ }
+
if (ds.options_->use_control_socket) {
if (do_text_file) {
dprintf(ds.control_socket_fd_,
@@ -2274,7 +2307,7 @@
"for more details\n",
ds.log_path_.c_str());
} else {
- dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
+ dprintf(ds.control_socket_fd_, "OK:%s\n", final_path.c_str());
}
}
}
@@ -2384,6 +2417,7 @@
// clang-format off
case 'd': do_add_date = true; break;
case 'z': do_zip_file = true; break;
+ case 'o': out_dir = optarg; break;
case 's': use_socket = true; break;
case 'S': use_control_socket = true; break;
case 'v': show_header_only = true; break;
@@ -2505,8 +2539,8 @@
* If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also
* gets added to the archive.
*
- * Bugreports are first generated in a local directory and later copied to the caller's fd if
- * supplied.
+ * Bugreports are first generated in a local directory and later copied to the caller's fd
+ * or directory if supplied.
*/
Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
const std::string& calling_package) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index dc0848a..0d25d30 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -386,10 +386,12 @@
// The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode =
::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT;
- // File descriptor to output zip file.
+ // File descriptor to output zip file. Takes precedence over out_dir.
android::base::unique_fd bugreport_fd;
// File descriptor to screenshot file.
android::base::unique_fd screenshot_fd;
+ // Custom output directory.
+ std::string out_dir;
// Bugreport mode of the bugreport.
std::string bugreport_mode;
// Command-line arguments as string
@@ -415,6 +417,12 @@
// specified, it is preferred. If not bugreport is written to /bugreports.
return !use_socket;
}
+
+ /* Returns if options specified require writing to custom file location */
+ bool OutputToCustomFile() {
+ // Custom location is only honored in limited mode.
+ return limited_only && !out_dir.empty() && bugreport_fd.get() == -1;
+ }
};
// TODO: initialize fields on constructor
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index e94e51c..c7df1bb 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -172,6 +172,7 @@
EXPECT_FALSE(options_.do_add_date);
EXPECT_FALSE(options_.do_zip_file);
+ EXPECT_EQ("", options_.out_dir);
EXPECT_FALSE(options_.use_socket);
EXPECT_FALSE(options_.use_control_socket);
EXPECT_FALSE(options_.show_header_only);
@@ -352,7 +353,8 @@
const_cast<char*>("-d"),
const_cast<char*>("-z"),
const_cast<char*>("-q"),
- const_cast<char*>("-L")
+ const_cast<char*>("-L"),
+ const_cast<char*>("-o abc")
};
// clang-format on
@@ -364,6 +366,7 @@
EXPECT_TRUE(options_.use_control_socket);
EXPECT_FALSE(options_.do_vibrate);
EXPECT_TRUE(options_.limited_only);
+ EXPECT_EQ(" abc", std::string(options_.out_dir));
// Other options retain default values
EXPECT_FALSE(options_.show_header_only);
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index a427c8d..1327cfd 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -230,7 +230,7 @@
}
const size_t N = services.size();
- if (N > 1) {
+ if (N > 1 || showListOnly) {
// first print a list of the current services
std::cout << "Currently running services:" << std::endl;
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index b9395ba..3467898 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -206,10 +206,7 @@
}
void AssertRunningServices(const std::vector<std::string>& services) {
- std::string expected;
- if (services.size() > 1) {
- expected.append("Currently running services:\n");
- }
+ std::string expected = "Currently running services:\n";
for (const std::string& service : services) {
expected.append(" ").append(service).append("\n");
}
@@ -263,6 +260,21 @@
AssertRunningServices({"Locksmith", "Valet"});
}
+TEST_F(DumpsysTest, ListServicesOneRegistered) {
+ ExpectListServices({"Locksmith"});
+ ExpectCheckService("Locksmith");
+
+ CallMain({"-l"});
+
+ AssertRunningServices({"Locksmith"});
+}
+
+TEST_F(DumpsysTest, ListServicesEmpty) {
+ CallMain({"-l"});
+
+ AssertRunningServices({});
+}
+
// Tests 'dumpsys -l' when a service is not running
TEST_F(DumpsysTest, ListRunningServices) {
ExpectListServices({"Locksmith", "Valet"});
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 402767a..64bfdf9 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -37,10 +37,16 @@
defaults: ["idlcli-defaults"],
srcs: [
"CommandVibrator.cpp",
+ "vibrator/CommandAlwaysOnDisable.cpp",
+ "vibrator/CommandAlwaysOnEnable.cpp",
"vibrator/CommandCompose.cpp",
"vibrator/CommandGetCapabilities.cpp",
"vibrator/CommandGetCompositionDelayMax.cpp",
"vibrator/CommandGetCompositionSizeMax.cpp",
+ "vibrator/CommandGetPrimitiveDuration.cpp",
+ "vibrator/CommandGetSupportedAlwaysOnEffects.cpp",
+ "vibrator/CommandGetSupportedEffects.cpp",
+ "vibrator/CommandGetSupportedPrimitives.cpp",
"vibrator/CommandOff.cpp",
"vibrator/CommandOn.cpp",
"vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index a8e5954..b874455 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -17,6 +17,7 @@
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
+#include <android/binder_enums.h>
#include <hidl/HidlSupport.h>
#include <iomanip>
@@ -66,7 +67,7 @@
} // namespace overrides
-template <typename T, typename R = hardware::hidl_enum_range<T>>
+template <typename T, typename R = ndk::enum_range<T>>
inline std::istream &operator>>(std::istream &stream, T &out) {
using overrides::operator>>;
auto validRange = R();
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index ca5142d..6c30a9e 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -16,8 +16,12 @@
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#include <future>
+
+#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
#include "utils.h"
@@ -101,6 +105,18 @@
namespace V1_3 = ::android::hardware::vibrator::V1_3;
namespace aidl = ::aidl::android::hardware::vibrator;
+class VibratorCallback : public aidl::BnVibratorCallback {
+public:
+ ndk::ScopedAStatus onComplete() override {
+ mPromise.set_value();
+ return ndk::ScopedAStatus::ok();
+ }
+ void waitForComplete() { mPromise.get_future().wait(); }
+
+private:
+ std::promise<void> mPromise;
+};
+
} // namespace vibrator
} // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
new file mode 100644
index 0000000..9afa300
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 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 CommandAlwaysOnDisable : public Command {
+ std::string getDescription() const override { return "Disarm always-on haptic source."; }
+
+ std::string getUsageSummary() const override { return "<id>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<id>", {"Source ID (device-specific)."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto id = args.pop<decltype(mId)>()) {
+ mId = *id;
+ std::cout << "Source ID: " << mId << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Source ID!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId);
+
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ int32_t mId;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnDisable>("alwaysOnDisable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
new file mode 100644
index 0000000..bb7f9f2
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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 {
+
+using aidl::Effect;
+using aidl::EffectStrength;
+
+class CommandAlwaysOnEnable : public Command {
+ std::string getDescription() const override {
+ return "Arm always-on haptic source with an effect.";
+ }
+
+ std::string getUsageSummary() const override { return "<id> <effect> <strength>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<id>", {"Source ID (device-specific)."}},
+ {"<effect>", {"Effect ID."}},
+ {"<strength>", {"0-2."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto id = args.pop<decltype(mId)>()) {
+ mId = *id;
+ std::cout << "Source ID: " << mId << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Source ID!" << std::endl;
+ return USAGE;
+ }
+ if (auto effect = args.pop<decltype(mEffect)>()) {
+ mEffect = *effect;
+ std::cout << "Effect: " << toString(mEffect) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Effect!" << std::endl;
+ return USAGE;
+ }
+ if (auto strength = args.pop<decltype(mStrength)>()) {
+ mStrength = *strength;
+ std::cout << "Strength: " << toString(mStrength) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Strength!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength);
+
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ int32_t mId;
+ Effect mEffect;
+ EffectStrength mStrength;
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnEnable>("alwaysOnEnable");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 4721a5f..97c057f 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -28,10 +28,13 @@
class CommandCompose : public Command {
std::string getDescription() const override { return "Compose vibration."; }
- std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+ std::string getUsageSummary() const override {
+ return "[options] <delay> <primitive> <scale> ...";
+ }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<delay>", {"In milliseconds"}},
{"<primitive>", {"Primitive ID."}},
{"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
@@ -41,6 +44,17 @@
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
while (!args.empty()) {
CompositeEffect effect;
if (auto delay = args.pop<decltype(effect.delayMs)>()) {
@@ -50,9 +64,8 @@
std::cerr << "Missing or Invalid Delay!" << std::endl;
return USAGE;
}
- // TODO: Use range validation when supported by AIDL
- if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) {
- effect.primitive = static_cast<decltype(effect.primitive)>(*primitive);
+ if (auto primitive = args.pop<decltype(effect.primitive)>()) {
+ effect.primitive = *primitive;
std::cout << "Primitive: " << toString(effect.primitive) << std::endl;
} else {
std::cerr << "Missing or Invalid Primitive!" << std::endl;
@@ -76,21 +89,33 @@
}
Status doMain(Args && /*args*/) override {
- std::string statusStr;
- Status ret;
- if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
- statusStr = status.getDescription();
- ret = status.isOk() ? OK : ERROR;
- } else {
+ auto hal = getHal<aidl::IVibrator>();
+
+ if (!hal) {
return UNAVAILABLE;
}
- std::cout << "Status: " << statusStr << std::endl;
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
- return ret;
+ std::shared_ptr<VibratorCallback> callback;
+
+ if (mBlocking) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback);
+
+ if (status.isOk() && callback) {
+ callback->waitForComplete();
+ }
+
+ std::cout << "Status: " << status.getDescription() << std::endl;
+
+ return status.isOk() ? OK : ERROR;
}
+ bool mBlocking;
std::vector<CompositeEffect> mComposite;
};
diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
new file mode 100644
index 0000000..460d39e
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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 <future>
+
+#include "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositePrimitive;
+
+class CommandGetPrimitiveDuration : public Command {
+ std::string getDescription() const override {
+ return "Retrieve effect primitive's duration in milliseconds.";
+ }
+
+ std::string getUsageSummary() const override { return "<primitive>"; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<primitive>", {"Primitive ID."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (auto primitive = args.pop<decltype(mPrimitive)>()) {
+ mPrimitive = *primitive;
+ std::cout << "Primitive: " << toString(mPrimitive) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Primitive!" << std::endl;
+ return USAGE;
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t duration;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Duration: " << duration << std::endl;
+
+ return ret;
+ }
+
+ CompositePrimitive mPrimitive;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetPrimitiveDuration>(
+ "getPrimitiveDuration");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
new file mode 100644
index 0000000..edfcd91
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 {
+
+using aidl::Effect;
+
+class CommandGetSupportedAlwaysOnEffects : public Command {
+ std::string getDescription() const override { return "List of supported always-on effects."; }
+
+ 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;
+ std::vector<Effect> effects;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Effects:" << std::endl;
+ for (auto &e : effects) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedAlwaysOnEffects>(
+ "getSupportedAlwaysOnEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
new file mode 100644
index 0000000..7658f22
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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 {
+
+using aidl::Effect;
+
+class CommandGetSupportedEffects : public Command {
+ std::string getDescription() const override { return "List supported effects."; }
+
+ 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;
+ std::vector<Effect> effects;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Effects:" << std::endl;
+ for (auto &e : effects) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetSupportedEffects>(
+ "getSupportedEffects");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
new file mode 100644
index 0000000..d101681
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 {
+
+using aidl::CompositePrimitive;
+
+class CommandGetSupportedPrimitives : public Command {
+ std::string getDescription() const override { return "List of supported effect primitive."; }
+
+ 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;
+ std::vector<CompositePrimitive> primitives;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives);
+ statusStr = status.getDescription();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Primitives:" << std::endl;
+ for (auto &e : primitives) {
+ std::cout << " " << toString(e) << std::endl;
+ }
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetSupportedPrimitives>(
+ "getSupportedPrimitives");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 4e7e493..8212fc1 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -13,9 +13,14 @@
* limitations under the License.
*/
+#include <thread>
+
#include "utils.h"
#include "vibrator.h"
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
namespace android {
namespace idlcli {
@@ -26,16 +31,28 @@
class CommandOn : public Command {
std::string getDescription() const override { return "Turn on vibrator."; }
- std::string getUsageSummary() const override { return "<duration>"; }
+ std::string getUsageSummary() const override { return "[options] <duration>"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<duration>", {"In milliseconds."}},
};
return details;
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
if (auto duration = args.pop<decltype(mDuration)>()) {
mDuration = *duration;
} else {
@@ -52,9 +69,21 @@
Status doMain(Args && /*args*/) override {
std::string statusStr;
Status ret;
+ std::shared_ptr<VibratorCallback> callback;
if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
+ auto status = hal->call(&aidl::IVibrator::on, mDuration, callback);
+
statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
@@ -65,11 +94,20 @@
return UNAVAILABLE;
}
+ if (ret == OK && mBlocking) {
+ if (callback) {
+ callback->waitForComplete();
+ } else {
+ sleep_for(milliseconds(mDuration));
+ }
+ }
+
std::cout << "Status: " << statusStr << std::endl;
return ret;
}
+ bool mBlocking;
uint32_t mDuration;
};
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 69c7e37..c897686 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -13,9 +13,14 @@
* limitations under the License.
*/
+#include <thread>
+
#include "utils.h"
#include "vibrator.h"
+using std::chrono::milliseconds;
+using std::this_thread::sleep_for;
+
namespace android {
namespace idlcli {
@@ -51,16 +56,17 @@
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;
+using aidl::Effect;
+using aidl::EffectStrength;
class CommandPerform : public Command {
std::string getDescription() const override { return "Perform vibration effect."; }
- std::string getUsageSummary() const override { return "<effect> <strength>"; }
+ std::string getUsageSummary() const override { return "[options] <effect> <strength>"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-b", {"Block for duration of vibration."}},
{"<effect>", {"Effect ID."}},
{"<strength>", {"0-2."}},
};
@@ -68,6 +74,17 @@
}
Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-b") {
+ mBlocking = true;
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
if (auto effect = args.pop<decltype(mEffect)>()) {
mEffect = *effect;
std::cout << "Effect: " << toString(mEffect) << std::endl;
@@ -93,12 +110,23 @@
std::string statusStr;
uint32_t lengthMs;
Status ret;
+ std::shared_ptr<VibratorCallback> callback;
if (auto hal = getHal<aidl::IVibrator>()) {
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ int32_t cap;
+ hal->call(&aidl::IVibrator::getCapabilities, &cap);
+
+ if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) {
+ callback = ndk::SharedRefBase::make<VibratorCallback>();
+ }
+
int32_t aidlLengthMs;
- auto status =
- hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
- static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
+ auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback,
+ &aidlLengthMs);
+
statusStr = status.getDescription();
lengthMs = static_cast<uint32_t>(aidlLengthMs);
ret = status.isOk() ? OK : ERROR;
@@ -111,17 +139,20 @@
};
if (auto hal = getHal<V1_3::IVibrator>()) {
- hidlRet = hal->call(&V1_3::IVibrator::perform_1_3,
- static_cast<V1_3::Effect>(mEffect), mStrength, callback);
+ hidlRet =
+ hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
+ static_cast<V1_0::EffectStrength>(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);
+ hidlRet =
+ hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
+ static_cast<V1_0::EffectStrength>(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);
+ static_cast<V1_1::Effect_1_1>(mEffect),
+ static_cast<V1_0::EffectStrength>(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);
+ static_cast<V1_0::EffectStrength>(mStrength), callback);
} else {
return UNAVAILABLE;
}
@@ -130,12 +161,21 @@
ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
}
+ if (ret == OK && mBlocking) {
+ if (callback) {
+ callback->waitForComplete();
+ } else {
+ sleep_for(milliseconds(lengthMs));
+ }
+ }
+
std::cout << "Status: " << statusStr << std::endl;
std::cout << "Length: " << lengthMs << std::endl;
return ret;
}
+ bool mBlocking;
Effect mEffect;
EffectStrength mStrength;
};
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
index 6e079eb..b17cba1 100644
--- a/cmds/installd/CrateManager.cpp
+++ b/cmds/installd/CrateManager.cpp
@@ -86,7 +86,7 @@
}
void CrateManager::traverseAllPackagesForUser(
- const std::unique_ptr<std::string>& uuid, userid_t userId,
+ const std::optional<std::string>& uuid, userid_t userId,
std::function<void(FTSENT*)>& onHandlingPackage) {
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
@@ -96,21 +96,21 @@
void CrateManager::createCrate(
CratedFolder cratedFolder,
- std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+ std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate) {
const char* path = cratedFolder->fts_path;
if (path == nullptr || *path == '\0') {
return;
}
- std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>();
- crateMetadata->uid = cratedFolder->fts_statp->st_uid;
- crateMetadata->packageName = mPackageName;
- crateMetadata->id = getValidatedCratedPath(path);
+ CrateMetadata crateMetadata;
+ crateMetadata.uid = cratedFolder->fts_statp->st_uid;
+ crateMetadata.packageName = mPackageName;
+ crateMetadata.id = getValidatedCratedPath(path);
- onCreateCrate(cratedFolder, crateMetadata);
+ onCreateCrate(cratedFolder, std::move(crateMetadata));
}
-void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+void CrateManager::traverseAllCrates(std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate) {
std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void {
createCrate(cratedFolder, onCreateCrate);
};
@@ -118,11 +118,11 @@
}
#if CRATE_DEBUG
-void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) {
+void CrateManager::dump(const CrateMetadata& CrateMetadata) {
LOG(DEBUG) << "CrateMetadata = {"
- << "uid : \"" << CrateMetadata->uid
- << "\", packageName : \"" << CrateMetadata->packageName
- << "\", id : \"" << CrateMetadata->id
+ << "uid : \"" << CrateMetadata.uid
+ << "\", packageName : \"" << CrateMetadata.packageName
+ << "\", id : \"" << CrateMetadata.id
<< "\"}";
}
#endif
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
index 4332d4c..1f30b5d 100644
--- a/cmds/installd/CrateManager.h
+++ b/cmds/installd/CrateManager.h
@@ -25,7 +25,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -55,18 +55,18 @@
CrateManager(const char* uuid, userid_t userId, const std::string& packageName);
~CrateManager();
- void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+ void traverseAllCrates(std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate);
static void traverseChildDir(const std::string& targetDir,
std::function<void(FTSENT*)>& onVisitChildDir);
static void traverseAllPackagesForUser(
- const std::unique_ptr<std::string>& uuid,
+ const std::optional<std::string>& uuid,
userid_t userId,
std::function<void(FTSENT*)>& onHandlingPackage);
#if CRATE_DEBUG
- static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata);
+ static void dump(const CrateMetadata& CrateMetadata);
#endif
private:
std::string mRoot;
@@ -75,7 +75,7 @@
void createCrate(
CratedFolder cratedFolder,
- std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+ std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate);
};
} // namespace installd
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b9c1add..e7014c8 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -166,7 +166,7 @@
}
}
-binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) {
+binder::Status checkArgumentUuid(const std::optional<std::string>& uuid) {
if (!uuid || is_valid_filename(*uuid)) {
return ok();
} else {
@@ -175,7 +175,7 @@
}
}
-binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) {
+binder::Status checkArgumentUuidTestOrNull(const std::optional<std::string>& uuid) {
if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) {
return ok();
} else {
@@ -214,7 +214,7 @@
return ok();
}
-binder::Status checkArgumentPath(const std::unique_ptr<std::string>& path) {
+binder::Status checkArgumentPath(const std::optional<std::string>& path) {
if (path) {
return checkArgumentPath(*path);
} else {
@@ -421,8 +421,8 @@
}
binder::Status InstalldNativeService::createAppDataBatched(
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& uuids,
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& packageNames,
+ const std::optional<std::vector<std::optional<std::string>>>& uuids,
+ const std::optional<std::vector<std::optional<std::string>>>& packageNames,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
int64_t* _aidl_return) {
@@ -432,10 +432,11 @@
ATRACE_BEGIN("createAppDataBatched");
binder::Status ret;
for (size_t i = 0; i < uuids->size(); i++) {
- if (!packageNames->at(i)) {
+ std::optional<std::string> packageName = packageNames->at(i);
+ if (!packageName) {
continue;
}
- ret = createAppData(uuids->at(i), *packageNames->at(i), userId, flags, appIds[i],
+ ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i],
seInfos[i], targetSdkVersions[i], _aidl_return);
if (!ret.isOk()) {
ATRACE_END();
@@ -446,7 +447,7 @@
return ok();
}
-binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
@@ -527,7 +528,7 @@
return ok();
}
-binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -588,7 +589,7 @@
return res;
}
-binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::clearAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -708,7 +709,7 @@
return res;
}
-binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -780,7 +781,7 @@
return (gid != -1) ? gid : uid;
}
-binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::fixupAppData(const std::optional<std::string>& uuid,
int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -900,7 +901,7 @@
}
binder::Status InstalldNativeService::snapshotAppData(
- const std::unique_ptr<std::string>& volumeUuid,
+ const std::optional<std::string>& volumeUuid,
const std::string& packageName, int32_t user, int32_t snapshotId,
int32_t storageFlags, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
@@ -1027,7 +1028,7 @@
}
binder::Status InstalldNativeService::restoreAppDataSnapshot(
- const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName,
+ const std::optional<std::string>& volumeUuid, const std::string& packageName,
const int32_t appId, const std::string& seInfo, const int32_t user,
const int32_t snapshotId, int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
@@ -1099,7 +1100,7 @@
}
binder::Status InstalldNativeService::destroyAppDataSnapshot(
- const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName,
+ const std::optional<std::string> &volumeUuid, const std::string& packageName,
const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId,
int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
@@ -1132,7 +1133,7 @@
}
binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified(
- const std::unique_ptr<std::string> &volumeUuid, const int32_t userId,
+ const std::optional<std::string> &volumeUuid, const int32_t user,
const std::vector<int32_t>& retainSnapshotIds) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
@@ -1140,7 +1141,7 @@
const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
- auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId);
+ auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user);
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir);
if (!dir) {
@@ -1159,7 +1160,7 @@
std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(),
snapshot_id) == retainSnapshotIds.end()) {
auto rollback_path = create_data_misc_ce_rollback_path(
- volume_uuid, userId, snapshot_id);
+ volume_uuid, user, snapshot_id);
int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */);
if (res != 0) {
return error(res, "Failed clearing snapshot " + rollback_path);
@@ -1169,8 +1170,8 @@
return ok();
}
-binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
- const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::string>& fromUuid,
+ const std::optional<std::string>& toUuid, const std::string& packageName,
int32_t appId, const std::string& seInfo,
int32_t targetSdkVersion, const std::string& fromCodePath) {
ENFORCE_UID(AID_SYSTEM);
@@ -1277,7 +1278,7 @@
return res;
}
-binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::createUserData(const std::optional<std::string>& uuid,
int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -1295,7 +1296,7 @@
return ok();
}
-binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::destroyUserData(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -1332,13 +1333,13 @@
return res;
}
-binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::freeCache(const std::optional<std::string>& uuid,
int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
std::lock_guard<std::recursive_mutex> lock(mLock);
- auto uuidString = uuid ? *uuid : "";
+ auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto data_path = create_data_path(uuid_);
auto noop = (flags & FLAG_FREE_CACHE_NOOP);
@@ -1736,7 +1737,7 @@
fts_close(fts);
}
-binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getAppSize(const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
int32_t appId, const std::vector<int64_t>& ceDataInodes,
const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
@@ -1776,7 +1777,7 @@
memset(&stats, 0, sizeof(stats));
memset(&extStats, 0, sizeof(extStats));
- auto uuidString = uuid ? *uuid : "";
+ auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
if (!IsQuotaSupported(uuidString)) {
@@ -1963,7 +1964,7 @@
return sizes;
}
-binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getUserSize(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
@@ -1983,7 +1984,7 @@
memset(&stats, 0, sizeof(stats));
memset(&extStats, 0, sizeof(extStats));
- auto uuidString = uuid ? *uuid : "";
+ auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
if (!IsQuotaSupported(uuidString)) {
@@ -2095,7 +2096,7 @@
return ok();
}
-binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::getExternalSize(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
@@ -2110,7 +2111,7 @@
LOG(INFO) << "Measuring external " << userId;
#endif
- auto uuidString = uuid ? *uuid : "";
+ auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
int64_t totalSize = 0;
@@ -2212,9 +2213,9 @@
}
binder::Status InstalldNativeService::getAppCrates(
- const std::unique_ptr<std::string>& uuid,
+ const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId,
- std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+ std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
for (const auto& packageName : packageNames) {
@@ -2223,15 +2224,15 @@
#ifdef ENABLE_STORAGE_CRATES
std::lock_guard<std::recursive_mutex> lock(mLock);
- auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+ auto retVector = std::vector<std::optional<CrateMetadata>>();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
- std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
- [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+ std::function<void(CratedFolder, CrateMetadata&&)> onCreateCrate =
+ [&](CratedFolder cratedFolder, CrateMetadata&& crateMetadata) -> void {
if (cratedFolder == nullptr) {
return;
}
- retVector->push_back(std::move(crateMetadata));
+ retVector.push_back(std::move(crateMetadata));
};
for (const auto& packageName : packageNames) {
@@ -2243,15 +2244,15 @@
}
#if CRATE_DEBUG
- LOG(WARNING) << "retVector->size() =" << retVector->size();
- for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
- CrateManager::dump(*iter);
+ LOG(WARNING) << "retVector.size() =" << retVector.size();
+ for (auto& item : retVector) {
+ CrateManager::dump(*item);
}
#endif
*_aidl_return = std::move(retVector);
#else // ENABLE_STORAGE_CRATES
- *_aidl_return = nullptr;
+ _aidl_return->reset();
/* prevent compile warning fail */
if (userId < 0) {
@@ -2262,22 +2263,22 @@
}
binder::Status InstalldNativeService::getUserCrates(
- const std::unique_ptr<std::string>& uuid, int32_t userId,
- std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+ const std::optional<std::string>& uuid, int32_t userId,
+ std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
#ifdef ENABLE_STORAGE_CRATES
std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
- auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+ auto retVector = std::vector<std::optional<CrateMetadata>>();
- std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
- [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+ std::function<void(CratedFolder, CrateMetadata&&)> onCreateCrate =
+ [&](CratedFolder cratedFolder, CrateMetadata&& crateMetadata) -> void {
if (cratedFolder == nullptr) {
return;
}
- retVector->push_back(std::move(crateMetadata));
+ retVector.push_back(std::move(crateMetadata));
};
std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
@@ -2287,15 +2288,15 @@
CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage);
#if CRATE_DEBUG
- LOG(DEBUG) << "retVector->size() =" << retVector->size();
- for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
- CrateManager::dump(*iter);
+ LOG(DEBUG) << "retVector.size() =" << retVector.size();
+ for (auto& item : retVector) {
+ CrateManager::dump(*item);
}
#endif
*_aidl_return = std::move(retVector);
#else // ENABLE_STORAGE_CRATES
- *_aidl_return = nullptr;
+ _aidl_return->reset();
/* prevent compile warning fail */
if (userId < 0) {
@@ -2305,7 +2306,7 @@
return ok();
}
-binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::setAppQuota(const std::optional<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -2376,19 +2377,19 @@
return ok();
}
-static const char* getCStr(const std::unique_ptr<std::string>& data,
+static const char* getCStr(const std::optional<std::string>& data,
const char* default_value = nullptr) {
- return data == nullptr ? default_value : data->c_str();
+ return data ? data->c_str() : default_value;
}
binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
- const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
- int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
- const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
- const std::unique_ptr<std::string>& classLoaderContext,
- const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
- const std::unique_ptr<std::string>& profileName,
- const std::unique_ptr<std::string>& dexMetadataPath,
- const std::unique_ptr<std::string>& compilationReason) {
+ const std::optional<std::string>& packageName, const std::string& instructionSet,
+ int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::optional<std::string>& uuid,
+ const std::optional<std::string>& classLoaderContext,
+ const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
+ const std::optional<std::string>& profileName,
+ const std::optional<std::string>& dexMetadataPath,
+ const std::optional<std::string>& compilationReason) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PATH(apkPath);
@@ -2434,7 +2435,7 @@
}
binder::Status InstalldNativeService::linkNativeLibraryDirectory(
- const std::unique_ptr<std::string>& uuid, const std::string& packageName,
+ const std::optional<std::string>& uuid, const std::string& packageName,
const std::string& nativeLibPath32, int32_t userId) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
@@ -2525,7 +2526,7 @@
return res;
}
-binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
+binder::Status InstalldNativeService::restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo) {
ENFORCE_UID(AID_SYSTEM);
@@ -2643,7 +2644,7 @@
}
binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
- const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath) {
+ const std::string& instructionSet, const std::optional<std::string>& outputPath) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PATH(apkPath);
CHECK_ARGUMENT_PATH(outputPath);
@@ -2795,7 +2796,7 @@
binder::Status InstalldNativeService::reconcileSecondaryDexFile(
const std::string& dexPath, const std::string& packageName, int32_t uid,
- const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
+ const std::vector<std::string>& isas, const std::optional<std::string>& volumeUuid,
int32_t storage_flag, bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(volumeUuid);
@@ -2810,7 +2811,7 @@
binder::Status InstalldNativeService::hashSecondaryDexFile(
const std::string& dexPath, const std::string& packageName, int32_t uid,
- const std::unique_ptr<std::string>& volumeUuid, int32_t storageFlag,
+ const std::optional<std::string>& volumeUuid, int32_t storageFlag,
std::vector<uint8_t>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(volumeUuid);
@@ -2869,7 +2870,7 @@
// Mount volume's CE and DE storage to mirror
binder::Status InstalldNativeService::tryMountDataMirror(
- const std::unique_ptr<std::string>& uuid) {
+ const std::optional<std::string>& uuid) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
if (!sAppDataIsolationEnabled) {
@@ -2933,7 +2934,7 @@
// Unmount volume's CE and DE storage from mirror
binder::Status InstalldNativeService::onPrivateVolumeRemoved(
- const std::unique_ptr<std::string>& uuid) {
+ const std::optional<std::string>& uuid) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
if (!sAppDataIsolationEnabled) {
@@ -2977,7 +2978,7 @@
}
std::string InstalldNativeService::findDataMediaPath(
- const std::unique_ptr<std::string>& uuid, userid_t userid) {
+ const std::optional<std::string>& uuid, userid_t userid) {
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
@@ -2990,15 +2991,14 @@
}
binder::Status InstalldNativeService::isQuotaSupported(
- const std::unique_ptr<std::string>& uuid, bool* _aidl_return) {
- auto uuidString = uuid ? *uuid : "";
- *_aidl_return = IsQuotaSupported(uuidString);
+ const std::optional<std::string>& uuid, bool* _aidl_return) {
+ *_aidl_return = IsQuotaSupported(uuid.value_or(""));
return ok();
}
binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName,
int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath,
- const std::unique_ptr<std::string>& dexMetadata, bool* _aidl_return) {
+ const std::optional<std::string>& dexMetadata, bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(codePath);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 8e7d98b..9819327 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -40,81 +40,81 @@
static char const* getServiceName() { return "installd"; }
virtual status_t dump(int fd, const Vector<String16> &args) override;
- binder::Status createUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ binder::Status createUserData(const std::optional<std::string>& uuid, int32_t userId,
int32_t userSerial, int32_t flags);
- binder::Status destroyUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId,
int32_t flags);
binder::Status createAppDataBatched(
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& uuids,
- const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& packageNames,
+ const std::optional<std::vector<std::optional<std::string>>>& uuids,
+ const std::optional<std::vector<std::optional<std::string>>>& packageNames,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions,
int64_t* _aidl_return);
- binder::Status createAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
- binder::Status restoreconAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo);
- binder::Status migrateAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags);
- binder::Status clearAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status clearAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
- binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid,
+ binder::Status destroyAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
- binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
+ binder::Status fixupAppData(const std::optional<std::string>& uuid, int32_t flags);
- binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid,
+ binder::Status snapshotAppData(const std::optional<std::string>& volumeUuid,
const std::string& packageName, const int32_t user, const int32_t snapshotId,
int32_t storageFlags, int64_t* _aidl_return);
- binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid,
+ binder::Status restoreAppDataSnapshot(const std::optional<std::string>& volumeUuid,
const std::string& packageName, const int32_t appId, const std::string& seInfo,
const int32_t user, const int32_t snapshotId, int32_t storageFlags);
- binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid,
+ binder::Status destroyAppDataSnapshot(const std::optional<std::string> &volumeUuid,
const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode,
const int32_t snapshotId, int32_t storageFlags);
- binder::Status destroyCeSnapshotsNotSpecified(const std::unique_ptr<std::string> &volumeUuid,
- const int32_t userId, const std::vector<int32_t>& retainSnapshotIds);
+ binder::Status destroyCeSnapshotsNotSpecified(const std::optional<std::string> &volumeUuid,
+ const int32_t user, const std::vector<int32_t>& retainSnapshotIds);
- binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
+ binder::Status getAppSize(const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
int32_t appId, const std::vector<int64_t>& ceDataInodes,
const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return);
- binder::Status getUserSize(const std::unique_ptr<std::string>& uuid,
+ binder::Status getUserSize(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return);
- binder::Status getExternalSize(const std::unique_ptr<std::string>& uuid,
+ binder::Status getExternalSize(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return);
- binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid,
+ binder::Status getAppCrates(const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames,
int32_t userId,
- std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+ std::optional<std::vector<std::optional<android::os::storage::CrateMetadata>>>*
_aidl_return);
binder::Status getUserCrates(
- const std::unique_ptr<std::string>& uuid, int32_t userId,
- std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+ const std::optional<std::string>& uuid, int32_t userId,
+ std::optional<std::vector<std::optional<android::os::storage::CrateMetadata>>>*
_aidl_return);
- binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
+ binder::Status setAppQuota(const std::optional<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota);
- binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
- const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+ binder::Status moveCompleteApp(const std::optional<std::string>& fromUuid,
+ const std::optional<std::string>& toUuid, const std::string& packageName,
int32_t appId, const std::string& seInfo,
int32_t targetSdkVersion, const std::string& fromCodePath);
binder::Status dexopt(const std::string& apkPath, int32_t uid,
- const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
- int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
- const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
- const std::unique_ptr<std::string>& classLoaderContext,
- const std::unique_ptr<std::string>& seInfo, bool downgrade,
- int32_t targetSdkVersion, const std::unique_ptr<std::string>& profileName,
- const std::unique_ptr<std::string>& dexMetadataPath,
- const std::unique_ptr<std::string>& compilationReason);
+ const std::optional<std::string>& packageName, const std::string& instructionSet,
+ int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::optional<std::string>& uuid,
+ const std::optional<std::string>& classLoaderContext,
+ const std::optional<std::string>& seInfo, bool downgrade,
+ int32_t targetSdkVersion, const std::optional<std::string>& profileName,
+ const std::optional<std::string>& dexMetadataPath,
+ const std::optional<std::string>& compilationReason);
binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName,
const std::string& outDexFile, int uid, bool* _aidl_return);
@@ -137,9 +137,9 @@
const std::string& profileName);
binder::Status rmPackageDir(const std::string& packageDir);
- binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
+ binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes,
int64_t cacheReservedBytes, int32_t flags);
- binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
+ binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid,
const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
@@ -147,27 +147,27 @@
binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
const std::string& outputPath);
binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
- const std::unique_ptr<std::string>& outputPath);
+ const std::optional<std::string>& outputPath);
binder::Status installApkVerity(const std::string& filePath,
android::base::unique_fd verityInput, int32_t contentSize);
binder::Status assertFsverityRootHashMatches(const std::string& filePath,
const std::vector<uint8_t>& expectedHash);
binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
- const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
+ const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
binder::Status hashSecondaryDexFile(const std::string& dexPath,
- const std::string& packageName, int32_t uid, const std::unique_ptr<std::string>& volumeUuid,
+ const std::string& packageName, int32_t uid, const std::optional<std::string>& volumeUuid,
int32_t storageFlag, std::vector<uint8_t>* _aidl_return);
binder::Status invalidateMounts();
- binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
+ binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid,
bool* _aidl_return);
- binder::Status tryMountDataMirror(const std::unique_ptr<std::string>& volumeUuid);
- binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid);
+ binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid);
+ binder::Status onPrivateVolumeRemoved(const std::optional<std::string>& volumeUuid);
binder::Status prepareAppProfile(const std::string& packageName,
int32_t userId, int32_t appId, const std::string& profileName,
- const std::string& codePath, const std::unique_ptr<std::string>& dexMetadata,
+ const std::string& codePath, const std::optional<std::string>& dexMetadata,
bool* _aidl_return);
binder::Status migrateLegacyObbData();
@@ -184,7 +184,7 @@
/* Map from UID to cache quota size */
std::unordered_map<uid_t, int64_t> mCacheQuotas;
- std::string findDataMediaPath(const std::unique_ptr<std::string>& uuid, userid_t userid);
+ std::string findDataMediaPath(const std::optional<std::string>& uuid, userid_t userid);
};
} // namespace installd
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index ffa8724..82be007 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -2325,7 +2325,7 @@
// out_secondary_dex_exists will be set to false.
bool reconcile_secondary_dex_file(const std::string& dex_path,
const std::string& pkgname, int uid, const std::vector<std::string>& isas,
- const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+ const std::optional<std::string>& volume_uuid, int storage_flag,
/*out*/bool* out_secondary_dex_exists) {
*out_secondary_dex_exists = false; // start by assuming the file does not exist.
if (isas.size() == 0) {
@@ -2346,7 +2346,7 @@
/* child -- drop privileges before continuing */
drop_capabilities(uid);
- const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+ const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
uid, storage_flag)) {
LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
@@ -2447,11 +2447,11 @@
// the app.
// For any other errors (e.g. if any of the parameters are invalid) returns false.
bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid,
- const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+ const std::optional<std::string>& volume_uuid, int storage_flag,
std::vector<uint8_t>* out_secondary_dex_hash) {
out_secondary_dex_hash->clear();
- const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+ const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) {
LOG(ERROR) << "hash_secondary_dex_file called with invalid storage_flag: "
@@ -2978,7 +2978,7 @@
appid_t app_id,
const std::string& profile_name,
const std::string& code_path,
- const std::unique_ptr<std::string>& dex_metadata) {
+ const std::optional<std::string>& dex_metadata) {
// Prepare the current profile.
std::string cur_profile = create_current_profile_path(user_id, package_name, profile_name,
/*is_secondary_dex*/ false);
@@ -2989,7 +2989,7 @@
}
// Check if we need to install the profile from the dex metadata.
- if (dex_metadata == nullptr) {
+ if (!dex_metadata) {
return true;
}
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index cc44873..d35953c 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -21,6 +21,8 @@
#include <sys/types.h>
+#include <optional>
+
#include <cutils/multiuser.h>
namespace android {
@@ -100,17 +102,17 @@
appid_t app_id,
const std::string& profile_name,
const std::string& code_path,
- const std::unique_ptr<std::string>& dex_metadata);
+ const std::optional<std::string>& dex_metadata);
bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
bool reconcile_secondary_dex_file(const std::string& dex_path,
const std::string& pkgname, int uid, const std::vector<std::string>& isas,
- const std::unique_ptr<std::string>& volumeUuid, int storage_flag,
+ const std::optional<std::string>& volumeUuid, int storage_flag,
/*out*/bool* out_secondary_dex_exists);
bool hash_secondary_dex_file(const std::string& dex_path,
- const std::string& pkgname, int uid, const std::unique_ptr<std::string>& volume_uuid,
+ const std::string& pkgname, int uid, const std::optional<std::string>& volume_uuid,
int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash);
int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 5a5cb53..863cdfe 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -114,15 +114,14 @@
class CacheTest : public testing::Test {
protected:
InstalldNativeService* service;
- std::unique_ptr<std::string> testUuid;
+ std::optional<std::string> testUuid;
virtual void SetUp() {
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(nullptr);
service = new InstalldNativeService();
- testUuid = std::make_unique<std::string>();
- *testUuid = std::string(kTestUuid);
+ testUuid = kTestUuid;
system("mkdir -p /data/local/tmp/user/0");
}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 16e4055..96f5e44 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -193,7 +193,7 @@
const uid_t kTestAppGid = multiuser_get_shared_gid(kTestUserId, kTestAppId);
InstalldNativeService* service_;
- std::unique_ptr<std::string> volume_uuid_;
+ std::optional<std::string> volume_uuid_;
std::string package_name_;
std::string apk_path_;
std::string empty_dm_file_;
@@ -221,7 +221,7 @@
ASSERT_TRUE(init_selinux());
service_ = new InstalldNativeService();
- volume_uuid_ = nullptr;
+ volume_uuid_ = std::nullopt;
package_name_ = "com.installd.test.dexopt";
se_info_ = "default";
app_apk_dir_ = android_app_dir + package_name_;
@@ -294,7 +294,7 @@
}
// Create a secondary dex file on CE storage
- const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str();
+ const char* volume_uuid_cstr = volume_uuid_ ? volume_uuid_->c_str() : nullptr;
app_private_dir_ce_ = create_data_user_ce_package_path(
volume_uuid_cstr, kTestUserId, package_name_.c_str());
secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar";
@@ -353,36 +353,32 @@
if (class_loader_context == nullptr) {
class_loader_context = "&";
}
- std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
int32_t dexopt_needed = 0; // does not matter;
- std::unique_ptr<std::string> out_path = nullptr; // does not matter
+ std::optional<std::string> out_path; // does not matter
int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag;
std::string compiler_filter = "speed-profile";
- std::unique_ptr<std::string> class_loader_context_ptr(
- new std::string(class_loader_context));
- std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
bool downgrade = false;
int32_t target_sdk_version = 0; // default
- std::unique_ptr<std::string> profile_name_ptr = nullptr;
- std::unique_ptr<std::string> dm_path_ptr = nullptr;
- std::unique_ptr<std::string> compilation_reason_ptr = nullptr;
+ std::optional<std::string> profile_name;
+ std::optional<std::string> dm_path;
+ std::optional<std::string> compilation_reason;
binder::Status result = service_->dexopt(path,
uid,
- package_name_ptr,
+ package_name_,
kRuntimeIsa,
dexopt_needed,
out_path,
dex_flags,
compiler_filter,
volume_uuid_,
- class_loader_context_ptr,
- se_info_ptr,
+ class_loader_context,
+ se_info_,
downgrade,
target_sdk_version,
- profile_name_ptr,
- dm_path_ptr,
- compilation_reason_ptr);
+ profile_name,
+ dm_path,
+ compilation_reason);
ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
int expected_access = should_dex_be_compiled ? 0 : -1;
std::string odex = GetSecondaryDexArtifact(path, "odex");
@@ -481,41 +477,35 @@
bool downgrade,
bool should_binder_call_succeed,
/*out */ binder::Status* binder_result) {
- std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
- std::unique_ptr<std::string> out_path(
- oat_dir == nullptr ? nullptr : new std::string(oat_dir));
- std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
- std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
+ std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt;
+ std::string class_loader_context = "&";
int32_t target_sdk_version = 0; // default
- std::unique_ptr<std::string> profile_name_ptr(new std::string("primary.prof"));
- std::unique_ptr<std::string> dm_path_ptr = nullptr;
- if (dm_path != nullptr) {
- dm_path_ptr.reset(new std::string(dm_path));
- }
- std::unique_ptr<std::string> compilation_reason_ptr(new std::string("test-reason"));
+ std::string profile_name = "primary.prof";
+ std::optional<std::string> dm_path_opt = dm_path ? std::make_optional<std::string>(dm_path) : std::nullopt;
+ std::string compilation_reason = "test-reason";
bool prof_result;
ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
- package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_,
- dm_path_ptr, &prof_result));
+ package_name_, kTestUserId, kTestAppId, profile_name, apk_path_,
+ dm_path_opt, &prof_result));
ASSERT_TRUE(prof_result);
binder::Status result = service_->dexopt(apk_path_,
uid,
- package_name_ptr,
+ package_name_,
kRuntimeIsa,
dexopt_needed,
out_path,
dex_flags,
compiler_filter,
volume_uuid_,
- class_loader_context_ptr,
- se_info_ptr,
+ class_loader_context,
+ se_info_,
downgrade,
target_sdk_version,
- profile_name_ptr,
- dm_path_ptr,
- compilation_reason_ptr);
+ profile_name,
+ dm_path_opt,
+ compilation_reason);
ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
if (!should_binder_call_succeed) {
@@ -992,7 +982,7 @@
bool result;
ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
package_name, kTestUserId, kTestAppId, profile_name, apk_path_,
- /*dex_metadata*/ nullptr, &result));
+ /*dex_metadata*/ {}, &result));
ASSERT_EQ(expected_result, result);
if (!expected_result) {
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 0fb62ae..1e7559d 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -99,15 +99,14 @@
class ServiceTest : public testing::Test {
protected:
InstalldNativeService* service;
- std::unique_ptr<std::string> testUuid;
+ std::optional<std::string> testUuid;
virtual void SetUp() {
setenv("ANDROID_LOG_TAGS", "*:v", 1);
android::base::InitLogging(nullptr);
service = new InstalldNativeService();
- testUuid = std::make_unique<std::string>();
- *testUuid = std::string(kTestUuid);
+ testUuid = kTestUuid;
system("mkdir -p /data/local/tmp/user/0");
init_globals_from_data_and_root();
@@ -322,7 +321,7 @@
// Request a snapshot of the CE content but not the DE content.
int64_t ce_snapshot_inode;
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 37, FLAG_STORAGE_CE, &ce_snapshot_inode));
struct stat buf;
memset(&buf, 0, sizeof(buf));
@@ -344,7 +343,7 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request a snapshot of the DE content but not the CE content.
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 37, FLAG_STORAGE_DE, &ce_snapshot_inode));
// Only DE content snapshot was requested.
ASSERT_EQ(ce_snapshot_inode, 0);
@@ -365,7 +364,7 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request a snapshot of both the CE as well as the DE content.
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 37, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
ASSERT_TRUE(android::base::ReadFileToString(
@@ -407,10 +406,10 @@
0700, 10000, 20000, false /* follow_symlinks */));
// Request snapshot for the package com.foo.
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
// Now request snapshot with the same id for the package com.bar
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.bar", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
// Check that both snapshots have correct data in them.
@@ -439,9 +438,9 @@
ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_de_path, true));
int64_t ce_snapshot_inode;
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 73, FLAG_STORAGE_CE, &ce_snapshot_inode));
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 73, FLAG_STORAGE_DE, nullptr));
// No CE content snapshot was performed.
ASSERT_EQ(ce_snapshot_inode, 0);
@@ -476,7 +475,7 @@
"TEST_CONTENT_2_DE", fake_package_de_path + "/file2",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 13, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
// Previous snapshot (with data for file1) must be cleared.
@@ -497,7 +496,7 @@
ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
- EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"),
+ EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_optional<std::string>("FOO"),
"com.foo", 0, 17, FLAG_STORAGE_DE, nullptr));
}
@@ -524,7 +523,7 @@
ASSERT_TRUE(android::base::WriteStringToFile(
"TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 23, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr));
// The snapshot call must clear cache.
struct stat sb;
@@ -558,7 +557,7 @@
"TEST_CONTENT_DE", fake_package_de_path + "/file1",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_optional<std::string>("TEST"),
"com.foo", 10000, "", 0, 239, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
std::string ce_content, de_content;
@@ -584,7 +583,7 @@
int64_t ce_snapshot_inode;
// Request a snapshot of both the CE as well as the DE content.
- ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ ASSERT_TRUE(service->snapshotAppData(std::make_optional<std::string>("TEST"),
"com.foo", 0, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
// Because CE data snapshot was requested, ce_snapshot_inode can't be null.
ASSERT_NE(0, ce_snapshot_inode);
@@ -594,7 +593,7 @@
ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
- ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
"com.foo", 0, ce_snapshot_inode, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
// Check snapshot is deleted.
ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
@@ -615,7 +614,7 @@
"DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1",
0700, 10000, 20000, false /* follow_symlinks */));
- ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
"com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
// Check snapshot is deleted.
@@ -624,7 +623,7 @@
ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
// Check that deleting already deleted snapshot is no-op.
- ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
+ ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"),
"com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
}
@@ -637,7 +636,7 @@
ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
- ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"),
+ ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_optional<std::string>("BAR"),
"com.foo", 0, 0, 43, FLAG_STORAGE_DE).isOk());
}
@@ -669,7 +668,7 @@
0700, 10000, 20000, false /* follow_symlinks */));
ASSERT_TRUE(service->destroyCeSnapshotsNotSpecified(
- std::make_unique<std::string>("TEST"), 0, { 1543, 77 }).isOk());
+ std::make_optional<std::string>("TEST"), 0, { 1543, 77 }).isOk());
// Check only snapshots not specified are deleted.
struct stat sb;
@@ -690,7 +689,7 @@
ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
- EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"),
+ EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_optional<std::string>("BAR"),
"com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE));
}
diff --git a/docs/Doxyfile b/docs/Doxyfile
index efa639d..a1bd960 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -1621,7 +1621,23 @@
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
-PREDEFINED = __attribute__(x)=
+PREDEFINED = \
+ "__ANDROID_API__=10000" \
+ "__BEGIN_DECLS=" \
+ "__END_DECLS=" \
+ "__INTRODUCED_IN(x)=" \
+ "__INTRODUCED_IN_32(x)=" \
+ "__INTRODUCED_IN_64(x)=" \
+ "__RENAME(x)=" \
+ "__RENAME_LDBL(x,y,z)=" \
+ "__printflike(x,y)=" \
+ "__attribute__(x)=" \
+ "__wur=" \
+ "__mallocfunc=" \
+ "__attribute_pure__=" \
+ "__attribute__(x)=" \
+ __ANDROID__ \
+ __BIONIC__ \
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
diff --git a/headers/Android.bp b/headers/Android.bp
index 5337235..8f41c2b 100644
--- a/headers/Android.bp
+++ b/headers/Android.bp
@@ -18,4 +18,11 @@
"libstagefright_foundation_headers",
],
min_sdk_version: "29",
+
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 2631b14..f195399 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -125,6 +125,8 @@
* Note that {@link ADataSpace} only exposes a few values. This may return
* {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no
* corresponding ADataSpace.
+ *
+ * Available since API level 30.
*/
int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30);
@@ -189,6 +191,8 @@
/**
* User-defined function for writing the output of compression.
*
+ * Available since API level 30.
+ *
* @param userContext Pointer to user-defined data passed to
* {@link AndroidBitmap_compress}.
* @param data Compressed data of |size| bytes to write.
@@ -202,6 +206,8 @@
/**
* Compress |pixels| as described by |info|.
*
+ * Available since API level 30.
+ *
* @param info Description of the pixels to compress.
* @param dataspace {@link ADataSpace} describing the color space of the
* pixels.
@@ -234,6 +240,8 @@
*
* Client must not modify it while a Bitmap is wrapping it.
*
+ * Available since API level 30.
+ *
* @param bitmap Handle to an android.graphics.Bitmap.
* @param outBuffer On success, is set to a pointer to the
* {@link AHardwareBuffer} associated with bitmap. This acquires
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index c1c4a72..bdf11e4 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -129,9 +129,12 @@
*
* This api is thread-safe. Any thread is allowed to register a new refresh
* rate callback for the choreographer instance.
+ *
+ * Available since API level 30.
*/
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback, void* data);
+ AChoreographer_refreshRateCallback, void* data)
+ __INTRODUCED_IN(30);
/**
* Unregisters a callback to be run when the display refresh rate changes, along
@@ -144,9 +147,12 @@
* callback and associated data pointer are unregistered, then there is a
* guarantee that when the unregistration completes that that callback will not
* be run with the data pointer passed.
+ *
+ * Available since API level 30.
*/
void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
- AChoreographer_refreshRateCallback, void* data);
+ AChoreographer_refreshRateCallback, void* data)
+ __INTRODUCED_IN(30);
#endif /* __ANDROID_API__ >= 30 */
__END_DECLS
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 05f4340..ccf3e59 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -58,13 +58,13 @@
ACONFIGURATION_ORIENTATION_ANY = 0x0000,
/**
* Orientation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
+ * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
* resource qualifier.
*/
ACONFIGURATION_ORIENTATION_PORT = 0x0001,
/**
* Orientation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
+ * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
* resource qualifier.
*/
ACONFIGURATION_ORIENTATION_LAND = 0x0002,
@@ -75,7 +75,7 @@
ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000,
/**
* Touchscreen: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
+ * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
* resource qualifier.
*/
ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001,
@@ -83,7 +83,7 @@
ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002,
/**
* Touchscreen: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
+ * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
* resource qualifier.
*/
ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003,
@@ -92,43 +92,43 @@
ACONFIGURATION_DENSITY_DEFAULT = 0,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_LOW = 120,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_MEDIUM = 160,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_TV = 213,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_HIGH = 240,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_XHIGH = 320,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_XXHIGH = 480,
/**
* Density: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a>
* resource qualifier.
*/
ACONFIGURATION_DENSITY_XXXHIGH = 640,
@@ -141,19 +141,19 @@
ACONFIGURATION_KEYBOARD_ANY = 0x0000,
/**
* Keyboard: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
* resource qualifier.
*/
ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001,
/**
* Keyboard: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
* resource qualifier.
*/
ACONFIGURATION_KEYBOARD_QWERTY = 0x0002,
/**
* Keyboard: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
* resource qualifier.
*/
ACONFIGURATION_KEYBOARD_12KEY = 0x0003,
@@ -162,25 +162,25 @@
ACONFIGURATION_NAVIGATION_ANY = 0x0000,
/**
* Navigation: value corresponding to the
- * <a href="@@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
+ * <a href="@/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
* resource qualifier.
*/
ACONFIGURATION_NAVIGATION_NONAV = 0x0001,
/**
* Navigation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
* resource qualifier.
*/
ACONFIGURATION_NAVIGATION_DPAD = 0x0002,
/**
* Navigation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
* resource qualifier.
*/
ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003,
/**
* Navigation: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
* resource qualifier.
*/
ACONFIGURATION_NAVIGATION_WHEEL = 0x0004,
@@ -189,19 +189,19 @@
ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000,
/**
* Keyboard availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
+ * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
* resource qualifier.
*/
ACONFIGURATION_KEYSHIDDEN_NO = 0x0001,
/**
* Keyboard availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
+ * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
* resource qualifier.
*/
ACONFIGURATION_KEYSHIDDEN_YES = 0x0002,
/**
* Keyboard availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a>
+ * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a>
* resource qualifier.
*/
ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003,
@@ -210,13 +210,13 @@
ACONFIGURATION_NAVHIDDEN_ANY = 0x0000,
/**
* Navigation availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
* resource qualifier.
*/
ACONFIGURATION_NAVHIDDEN_NO = 0x0001,
/**
* Navigation availability: value corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
* resource qualifier.
*/
ACONFIGURATION_NAVHIDDEN_YES = 0x0002,
@@ -226,28 +226,28 @@
/**
* Screen size: value indicating the screen is at least
* approximately 320x426 dp units, corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENSIZE_SMALL = 0x01,
/**
* Screen size: value indicating the screen is at least
* approximately 320x470 dp units, corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENSIZE_NORMAL = 0x02,
/**
* Screen size: value indicating the screen is at least
* approximately 480x640 dp units, corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENSIZE_LARGE = 0x03,
/**
* Screen size: value indicating the screen is at least
* approximately 720x960 dp units, corresponding to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENSIZE_XLARGE = 0x04,
@@ -256,13 +256,13 @@
ACONFIGURATION_SCREENLONG_ANY = 0x00,
/**
* Screen layout: value that corresponds to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENLONG_NO = 0x1,
/**
* Screen layout: value that corresponds to the
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
* resource qualifier.
*/
ACONFIGURATION_SCREENLONG_YES = 0x2,
@@ -275,13 +275,13 @@
ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00,
/**
* Wide color gamut: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
+ * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
* nowidecg</a> resource qualifier specified.
*/
ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1,
/**
* Wide color gamut: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
+ * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
* widecg</a> resource qualifier specified.
*/
ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2,
@@ -290,13 +290,13 @@
ACONFIGURATION_HDR_ANY = 0x00,
/**
* HDR: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+ * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">
* lowdr</a> resource qualifier specified.
*/
ACONFIGURATION_HDR_NO = 0x1,
/**
* HDR: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+ * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">
* highdr</a> resource qualifier specified.
*/
ACONFIGURATION_HDR_YES = 0x2,
@@ -305,38 +305,38 @@
ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">no
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">no
* UI mode type</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 0x05,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06,
/**
* UI mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07,
@@ -344,12 +344,12 @@
ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
/**
* UI night mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1,
/**
* UI night mode: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified.
*/
ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2,
@@ -366,78 +366,78 @@
ACONFIGURATION_LAYOUTDIR_ANY = 0x00,
/**
* Layout direction: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified.
*/
ACONFIGURATION_LAYOUTDIR_LTR = 0x01,
/**
* Layout direction: value that corresponds to
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified.
+ * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified.
*/
ACONFIGURATION_LAYOUTDIR_RTL = 0x02,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
+ * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
* configuration.
*/
ACONFIGURATION_MCC = 0x0001,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
+ * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
* configuration.
*/
ACONFIGURATION_MNC = 0x0002,
/**
* Bit mask for
- * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
+ * <a href="/guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
* configuration.
*/
ACONFIGURATION_LOCALE = 0x0004,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a>
+ * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a>
* configuration.
*/
ACONFIGURATION_TOUCHSCREEN = 0x0008,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a>
* configuration.
*/
ACONFIGURATION_KEYBOARD = 0x0010,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a>
+ * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a>
* configuration.
*/
ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a>
+ * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a>
* configuration.
*/
ACONFIGURATION_NAVIGATION = 0x0040,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a>
+ * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a>
* configuration.
*/
ACONFIGURATION_ORIENTATION = 0x0080,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
+ * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
* configuration.
*/
ACONFIGURATION_DENSITY = 0x0100,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a>
+ * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a>
* configuration.
*/
ACONFIGURATION_SCREEN_SIZE = 0x0200,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a>
+ * <a href="/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a>
* configuration.
*/
ACONFIGURATION_VERSION = 0x0400,
@@ -447,27 +447,27 @@
ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a>
+ * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a>
* configuration.
*/
ACONFIGURATION_UI_MODE = 0x1000,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a>
+ * <a href="/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a>
* configuration.
*/
ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a>
+ * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a>
* configuration.
*/
ACONFIGURATION_LAYOUTDIR = 0x4000,
ACONFIGURATION_SCREEN_ROUND = 0x8000,
/**
* Bit mask for
- * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
- * and <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
+ * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
+ * and <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
*/
ACONFIGURATION_COLOR_MODE = 0x10000,
/**
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 3a87da0..d7e6e41 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -133,6 +133,8 @@
/**
* Create a new {@link AImageDecoder} from an {@link AAsset}.
*
+ * Available since API level 30.
+ *
* @param asset {@link AAsset} containing encoded image data. Client is still
* responsible for calling {@link AAsset_close} on it, which may be
* done after deleting the returned {@link AImageDecoder}.
@@ -162,6 +164,8 @@
/**
* Create a new {@link AImageDecoder} from a file descriptor.
*
+ * Available since API level 30.
+ *
* @param fd Seekable, readable, open file descriptor for encoded data.
* Client is still responsible for closing it, which may be done
* after deleting the returned {@link AImageDecoder}.
@@ -190,6 +194,8 @@
/**
* Create a new AImageDecoder from a buffer.
*
+ * Available since API level 30.
+ *
* @param buffer Pointer to encoded data. Must be valid for the entire time
* the {@link AImageDecoder} is used.
* @param length Byte length of buffer.
@@ -217,12 +223,16 @@
/**
* Delete the AImageDecoder.
+ *
+ * Available since API level 30.
*/
void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
/**
* Choose the desired output format.
*
+ * Available since API level 30.
+ *
* @param format {@link AndroidBitmapFormat} to use for the output.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating the reason for the failure. On failure, the
@@ -247,6 +257,8 @@
* Pass true to this method to leave them unpremultiplied. This has no effect on an
* opaque image.
*
+ * Available since API level 30.
+ *
* @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied.
* @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
* indicating the reason for the failure.
@@ -267,6 +279,8 @@
* Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
* an {@link ADataSpace}.
*
+ * Available since API level 30.
+ *
* @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
* specifies how to interpret the colors. By default,
* AImageDecoder will decode into the ADataSpace specified by
@@ -292,6 +306,8 @@
* specified by width and height, and the output image will be the size of the
* crop rect.
*
+ * Available since API level 30.
+ *
* @param width Width of the output (prior to cropping).
* This will affect future calls to
* {@link AImageDecoder_getMinimumStride}, which will now return
@@ -319,6 +335,8 @@
* others. This computes the most efficient target size to use to reach a
* particular sampleSize.
*
+ * Available since API level 30.
+ *
* @param sampleSize A subsampling rate of the original image. Must be greater
* than or equal to 1. A sampleSize of 2 means to skip every
* other pixel/line, resulting in a width and height that are
@@ -344,6 +362,8 @@
* the specified {@link ARect}. Clients will only need to allocate enough memory
* for the cropped ARect.
*
+ * Available since API level 30.
+ *
* @param crop Rectangle describing a crop of the decode. It must be contained inside of
* the (possibly scaled, by {@link AImageDecoder_setTargetSize})
* image dimensions. This will affect future calls to
@@ -376,6 +396,8 @@
*
* This is owned by the {@link AImageDecoder} and will be destroyed when the
* AImageDecoder is destroyed via {@link AImageDecoder_delete}.
+ *
+ * Available since API level 30.
*/
const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
const AImageDecoder*) __INTRODUCED_IN(30);
@@ -385,6 +407,8 @@
* pixel width of the output, unless {@link AImageDecoder_setTargetSize} is
* used to choose a different size or {@link AImageDecoder_setCrop} is used to
* set a crop rect.
+ *
+ * Available since API level 30.
*/
int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -393,12 +417,16 @@
* pixel height of the output, unless {@link AImageDecoder_setTargetSize} is
* used to choose a different size or {@link AImageDecoder_setCrop} is used to
* set a crop rect.
+ *
+ * Available since API level 30.
*/
int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
* Report the mimeType of the encoded image.
*
+ * Available since API level 30.
+ *
* @return a string literal describing the mime type.
*/
const char* AImageDecoderHeaderInfo_getMimeType(
@@ -409,6 +437,8 @@
* by default. {@link AImageDecoder} will try to choose one that is sensible
* for the image and the system. Note that this does not indicate the
* encoded format of the image.
+ *
+ * Available since API level 30.
*/
int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -419,6 +449,8 @@
* {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha,
* this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because
* {@link AImageDecoder_decodeImage} will premultiply pixels by default.
+ *
+ * Available since API level 30.
*/
int AImageDecoderHeaderInfo_getAlphaFlags(
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
@@ -429,6 +461,8 @@
* By default, {@link AImageDecoder_decodeImage} will not do any color
* conversion.
*
+ * Available since API level 30.
+ *
* @return The {@link ADataSpace} representing the way the colors
* are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a
* corresponding ADataSpace). This specifies how to interpret the colors
@@ -452,12 +486,16 @@
*
* If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or
* cropped (via {@link AImageDecoder_setCrop}), this takes those into account.
+ *
+ * Available since API level 30.
*/
size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
/**
* Decode the image into pixels, using the settings of the {@link AImageDecoder}.
*
+ * Available since API level 30.
+ *
* @param decoder Opaque object representing the decoder.
* @param pixels On success, will be filled with the result
* of the decode. Must be large enough to hold |size| bytes.
diff --git a/include/android/input.h b/include/android/input.h
index dbfd61e..7c39234 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -55,6 +55,7 @@
#include <sys/types.h>
#include <android/keycodes.h>
#include <android/looper.h>
+#include <jni.h>
#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
@@ -931,6 +932,15 @@
/** Get the input event source. */
int32_t AInputEvent_getSource(const AInputEvent* event);
+/**
+ * Releases interface objects created by {@link AKeyEvent_fromJava()}
+ * and {@link AMotionEvent_fromJava()}.
+ * After returning, the specified AInputEvent* object becomes invalid and should no longer be used.
+ * The underlying Java object remains valid and does not change its state.
+ */
+
+void AInputEvent_release(const AInputEvent* event);
+
/*** Accessors for key events only. ***/
/** Get the key event action. */
@@ -977,6 +987,15 @@
*/
int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
+/**
+ * Creates a native AInputEvent* object associated with the specified Java android.view.KeyEvent.
+ * The result may be used with generic and KeyEvent-specific AInputEvent_* functions.
+ * The object returned by this function must be disposed using {@link AInputEvent_release()}.
+ * User must guarantee that lifetime for object referenced by keyEvent is prolongated
+ * up to release of returned AInputEvent*.
+ */
+const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
+
/*** Accessors for motion events only. ***/
/** Get the combined motion event action code and pointer index. */
@@ -1292,6 +1311,14 @@
float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event,
int32_t axis, size_t pointer_index, size_t history_index);
+/**
+ * Creates a native AInputEvent* object associated with the specified Java android.view.MotionEvent.
+ * The result may be used with generic and MotionEvent-specific AInputEvent_* functions.
+ * The object returned by this function must be disposed using {@link AInputEvent_release()}.
+ * User must guarantee that object referenced by motionEvent won't be recycled and
+ * its lifetime is prolongated up to release of returned AInputEvent*.
+ */
+const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
struct AInputQueue;
/**
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 59b1deb..c6d1c94 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -126,8 +126,8 @@
ANDROID_RESOLV_NO_RETRY = 1 << 0,
/**
- * Do not cache the result of the lookup. The lookup may return a result that is already
- * in the cache, unless the ANDROID_RESOLV_NO_CACHE_LOOKUP flag is also specified.
+ * Don't lookup this request in the cache, and don't cache the result of the lookup.
+ * This flag implies {@link #ANDROID_RESOLV_NO_CACHE_LOOKUP}.
*/
ANDROID_RESOLV_NO_CACHE_STORE = 1 << 1,
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 3247fa1..83582d6 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -60,8 +60,6 @@
extern "C" {
#endif
-#if __ANDROID_API__ >= 30
-
enum AThermalStatus {
/** Error in thermal status. */
ATHERMAL_STATUS_ERROR = -1,
@@ -111,36 +109,45 @@
*/
typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+#if __ANDROID_API__ >= 30
+
/**
* Acquire an instance of the thermal manager. This must be freed using
* {@link AThermal_releaseManager}.
*
+ * Available since API level 30.
+ *
* @return manager instance on success, nullptr on failure.
- */
-AThermalManager* AThermal_acquireManager();
+ */
+AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30);
/**
* Release the thermal manager pointer acquired via
* {@link AThermal_acquireManager}.
*
- * @param manager The manager to be released.
+ * Available since API level 30.
*
+ * @param manager The manager to be released.
*/
-void AThermal_releaseManager(AThermalManager *manager);
+void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30);
/**
* Gets the current thermal status.
*
+ * Available since API level 30.
+ *
* @param manager The manager instance to use to query the thermal status.
* Acquired via {@link AThermal_acquireManager}.
*
* @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
-*/
-AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager);
+ */
+AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30);
/**
* Register the thermal status listener for thermal status change.
*
+ * Available since API level 30.
+ *
* @param manager The manager instance to use to register.
* Acquired via {@link AThermal_acquireManager}.
* @param callback The callback function to be called when thermal status updated.
@@ -152,11 +159,13 @@
* EPIPE if communication with the system service has failed.
*/
int AThermal_registerThermalStatusListener(AThermalManager *manager,
- AThermal_StatusCallback callback, void *data);
+ AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
/**
* Unregister the thermal status listener previously resgistered.
*
+ * Available since API level 30.
+ *
* @param manager The manager instance to use to unregister.
* Acquired via {@link AThermal_acquireManager}.
* @param callback The callback function to be called when thermal status updated.
@@ -168,8 +177,7 @@
* EPIPE if communication with the system service has failed.
*/
int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
- AThermal_StatusCallback callback, void *data);
-
+ AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
#endif // __ANDROID_API__ >= 30
diff --git a/include/android/trace.h b/include/android/trace.h
index d59690a..dbad6f6 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -115,7 +115,7 @@
#endif /* __ANDROID_API__ >= 29 */
#ifdef __cplusplus
-};
+}
#endif
#endif // ANDROID_NATIVE_TRACE_H
diff --git a/include/input/Input.h b/include/input/Input.h
index 9e47318..ac901ae 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -280,9 +280,9 @@
public:
// Used to divide integer space to ensure no conflict among these sources./
enum class Source : int32_t {
- INPUT_READER = 0x0 << SOURCE_SHIFT,
- INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT,
- OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events
+ INPUT_READER = static_cast<int32_t>(0x0u << SOURCE_SHIFT),
+ INPUT_DISPATCHER = static_cast<int32_t>(0x1u << SOURCE_SHIFT),
+ OTHER = static_cast<int32_t>(0x3u << SOURCE_SHIFT), // E.g. app injected events
};
IdGenerator(Source source);
@@ -294,7 +294,7 @@
private:
const Source mSource;
- static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT;
+ static constexpr int32_t SOURCE_MASK = static_cast<int32_t>(0x3u << SOURCE_SHIFT);
};
/**
@@ -462,6 +462,8 @@
nsecs_t eventTime);
void initialize(const KeyEvent& from);
+ static const char* actionToString(int32_t action);
+
protected:
int32_t mAction;
int32_t mFlags;
@@ -725,6 +727,8 @@
static const char* getLabel(int32_t axis);
static int32_t getAxisFromLabel(const char* label);
+ static const char* actionToString(int32_t action);
+
protected:
int32_t mAction;
int32_t mActionButton;
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 2dac5b6..6740855 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -37,23 +37,23 @@
InputWindowInfo(const Parcel& from);
// Window flags from WindowManager.LayoutParams
- enum {
- FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
- FLAG_DIM_BEHIND = 0x00000002,
- FLAG_BLUR_BEHIND = 0x00000004,
- FLAG_NOT_FOCUSABLE = 0x00000008,
- FLAG_NOT_TOUCHABLE = 0x00000010,
- FLAG_NOT_TOUCH_MODAL = 0x00000020,
+ enum : uint32_t {
+ FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ FLAG_DIM_BEHIND = 0x00000002,
+ FLAG_BLUR_BEHIND = 0x00000004,
+ FLAG_NOT_FOCUSABLE = 0x00000008,
+ FLAG_NOT_TOUCHABLE = 0x00000010,
+ FLAG_NOT_TOUCH_MODAL = 0x00000020,
FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
- FLAG_KEEP_SCREEN_ON = 0x00000080,
- FLAG_LAYOUT_IN_SCREEN = 0x00000100,
- FLAG_LAYOUT_NO_LIMITS = 0x00000200,
- FLAG_FULLSCREEN = 0x00000400,
- FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
- FLAG_DITHER = 0x00001000,
- FLAG_SECURE = 0x00002000,
- FLAG_SCALED = 0x00004000,
- FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
+ FLAG_KEEP_SCREEN_ON = 0x00000080,
+ FLAG_LAYOUT_IN_SCREEN = 0x00000100,
+ FLAG_LAYOUT_NO_LIMITS = 0x00000200,
+ FLAG_FULLSCREEN = 0x00000400,
+ FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
+ FLAG_DITHER = 0x00001000,
+ FLAG_SECURE = 0x00002000,
+ FLAG_SCALED = 0x00004000,
+ FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
FLAG_LAYOUT_INSET_DECOR = 0x00010000,
FLAG_ALT_FOCUSABLE_IM = 0x00020000,
FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
@@ -62,7 +62,14 @@
FLAG_TURN_SCREEN_ON = 0x00200000,
FLAG_DISMISS_KEYGUARD = 0x00400000,
FLAG_SPLIT_TOUCH = 0x00800000,
+ FLAG_HARDWARE_ACCELERATED = 0x01000000,
+ FLAG_LAYOUT_IN_OVERSCAN = 0x02000000,
+ FLAG_TRANSLUCENT_STATUS = 0x04000000,
+ FLAG_TRANSLUCENT_NAVIGATION = 0x08000000,
+ FLAG_LOCAL_FOCUS_MODE = 0x10000000,
FLAG_SLIPPERY = 0x20000000,
+ FLAG_LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
+ FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
};
// Window types from WindowManager.LayoutParams
@@ -107,8 +114,8 @@
TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
+ TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
- TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42,
LAST_SYSTEM_WINDOW = 2999,
};
@@ -163,6 +170,12 @@
bool hasFocus = false;
bool hasWallpaper = false;
bool paused = false;
+ /* This flag is set when the window is of a trusted type that is allowed to silently
+ * overlay other windows for the purpose of implementing the secure views feature.
+ * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+ * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+ */
+ bool trustedOverlay = false;
int32_t ownerPid = -1;
int32_t ownerUid = -1;
int32_t inputFeatures = 0;
@@ -175,23 +188,19 @@
void addTouchableRegion(const Rect& region);
bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
- bool frameContainsPoint(int32_t x, int32_t y) const;
- /* Returns true if the window is of a trusted type that is allowed to silently
- * overlay other windows for the purpose of implementing the secure views feature.
- * Trusted overlays, such as IME windows, can partly obscure other windows without causing
- * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
- */
- bool isTrustedOverlay() const;
+ bool frameContainsPoint(int32_t x, int32_t y) const;
bool supportsSplitTouch() const;
bool overlaps(const InputWindowInfo* other) const;
status_t write(Parcel& output) const;
+
static InputWindowInfo read(const Parcel& from);
};
+std::string inputWindowFlagsToString(uint32_t flags);
/*
* Handle for a window that can receive input.
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 727865a..ee010a3 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -18,8 +18,8 @@
#define _LIBINPUT_VELOCITY_TRACKER_H
#include <input/Input.h>
-#include <utils/Timers.h>
#include <utils/BitSet.h>
+#include <utils/Timers.h>
namespace android {
@@ -30,6 +30,22 @@
*/
class VelocityTracker {
public:
+ enum class Strategy : int32_t {
+ DEFAULT = -1,
+ MIN = 0,
+ IMPULSE = 0,
+ LSQ1 = 1,
+ LSQ2 = 2,
+ LSQ3 = 3,
+ WLSQ2_DELTA = 4,
+ WLSQ2_CENTRAL = 5,
+ WLSQ2_RECENT = 6,
+ INT1 = 7,
+ INT2 = 8,
+ LEGACY = 9,
+ MAX = LEGACY,
+ };
+
struct Position {
float x, y;
};
@@ -62,8 +78,8 @@
};
// Creates a velocity tracker using the specified strategy.
- // If strategy is NULL, uses the default strategy for the platform.
- VelocityTracker(const char* strategy = nullptr);
+ // If strategy is not provided, uses the default strategy for the platform.
+ VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
~VelocityTracker();
@@ -102,16 +118,21 @@
inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
private:
- static const char* DEFAULT_STRATEGY;
+ // The default velocity tracker strategy.
+ // Although other strategies are available for testing and comparison purposes,
+ // this is the strategy that applications will actually use. Be very careful
+ // when adjusting the default strategy because it can dramatically affect
+ // (often in a bad way) the user experience.
+ static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
nsecs_t mLastEventTime;
BitSet32 mCurrentPointerIdBits;
int32_t mActivePointerId;
- VelocityTrackerStrategy* mStrategy;
+ std::unique_ptr<VelocityTrackerStrategy> mStrategy;
- bool configureStrategy(const char* strategy);
+ bool configureStrategy(const Strategy strategy);
- static VelocityTrackerStrategy* createStrategy(const char* strategy);
+ static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
};
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
deleted file mode 100644
index 853f0c9..0000000
--- a/include/powermanager/IPowerManager.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#ifndef ANDROID_IPOWERMANAGER_H
-#define ANDROID_IPOWERMANAGER_H
-
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <hardware/power.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IPowerManager : public IInterface
-{
-public:
- // These transaction IDs must be kept in sync with the method order from
- // IPowerManager.aidl.
- enum {
- ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION,
- ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1,
- RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2,
- UPDATE_WAKE_LOCK_UIDS = IBinder::FIRST_CALL_TRANSACTION + 3,
- POWER_HINT = IBinder::FIRST_CALL_TRANSACTION + 4,
- UPDATE_WAKE_LOCK_SOURCE = IBinder::FIRST_CALL_TRANSACTION + 5,
- IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6,
- USER_ACTIVITY = IBinder::FIRST_CALL_TRANSACTION + 7,
- WAKE_UP = IBinder::FIRST_CALL_TRANSACTION + 8,
- GO_TO_SLEEP = IBinder::FIRST_CALL_TRANSACTION + 9,
- NAP = IBinder::FIRST_CALL_TRANSACTION + 10,
- IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11,
- IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12,
- GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13,
- SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14,
- REBOOT = IBinder::FIRST_CALL_TRANSACTION + 17,
- REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 18,
- SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 19,
- CRASH = IBinder::FIRST_CALL_TRANSACTION + 20,
- };
-
- DECLARE_META_INTERFACE(PowerManager)
-
- // The parcels created by these methods must be kept in sync with the
- // corresponding methods from IPowerManager.aidl.
- // FIXME remove the bool isOneWay parameters as they are not oneway in the .aidl
- virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, bool isOneWay = false) = 0;
- virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, int uid, bool isOneWay = false) = 0;
- virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay = false) = 0;
- virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
- bool isOneWay = false) = 0;
- virtual status_t powerHint(int hintId, int data) = 0;
- virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0;
- virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0;
- virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0;
- virtual status_t crash(const String16& message) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPOWERMANAGER_H
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
new file mode 100644
index 0000000..dd34c0a
--- /dev/null
+++ b/include/powermanager/PowerHalController.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_POWERHALCONTROLLER_H
+#define ANDROID_POWERHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+// Connects to underlying Power HAL handles.
+class HalConnector {
+public:
+ HalConnector() = default;
+ virtual ~HalConnector() = default;
+
+ virtual std::unique_ptr<HalWrapper> connect();
+ virtual void reset();
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Controller for Power HAL handle.
+// This relies on HalConnector to connect to the underlying Power HAL
+// service and reconnects to it after each failed api call. This also ensures
+// connecting to the service is thread-safe.
+class PowerHalController : public HalWrapper {
+public:
+ PowerHalController() : PowerHalController(std::make_unique<HalConnector>()) {}
+ explicit PowerHalController(std::unique_ptr<HalConnector> connector)
+ : mHalConnector(std::move(connector)) {}
+ virtual ~PowerHalController() = default;
+
+ void init();
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+private:
+ std::mutex mConnectedHalMutex;
+ std::unique_ptr<HalConnector> mHalConnector;
+
+ // Shared pointers to keep global pointer and allow local copies to be used in
+ // different threads
+ std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr;
+ const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>();
+
+ std::shared_ptr<HalWrapper> initHal();
+ HalResult processHalResult(HalResult result, const char* functionName);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace power
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALCONTROLLER_H
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
new file mode 100644
index 0000000..ed6f6f3
--- /dev/null
+++ b/include/powermanager/PowerHalLoader.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_POWERHALLOADER_H
+#define ANDROID_POWERHALLOADER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+
+namespace android {
+
+namespace power {
+
+// Loads available Power HAL services.
+class PowerHalLoader {
+public:
+ static void unloadAll();
+ static sp<hardware::power::IPower> loadAidl();
+ static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
+ static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
+
+private:
+ static std::mutex gHalMutex;
+ static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
+
+ static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
+ EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+
+ PowerHalLoader() = delete;
+ ~PowerHalLoader() = delete;
+};
+
+}; // namespace power
+
+} // namespace android
+
+#endif // ANDROID_POWERHALLOADER_H
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
new file mode 100644
index 0000000..c3e7601
--- /dev/null
+++ b/include/powermanager/PowerHalWrapper.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_POWERHALWRAPPER_H
+#define ANDROID_POWERHALWRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+
+namespace android {
+
+namespace power {
+
+// State of Power HAL support for individual apis.
+enum class HalSupport {
+ UNKNOWN = 0,
+ ON = 1,
+ OFF = 2,
+};
+
+// State of the Power HAL api call result.
+enum class HalResult {
+ SUCCESSFUL = 0,
+ FAILED = 1,
+ UNSUPPORTED = 2,
+};
+
+// Wrapper for Power HAL handlers.
+class HalWrapper {
+public:
+ virtual ~HalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0;
+};
+
+// Empty Power HAL wrapper that ignores all api calls.
+class EmptyHalWrapper : public HalWrapper {
+public:
+ EmptyHalWrapper() = default;
+ ~EmptyHalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+};
+
+// Wrapper for the HIDL Power HAL v1.0.
+class HidlHalWrapperV1_0 : public HalWrapper {
+public:
+ explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal)
+ : mHandleV1_0(std::move(Hal)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+protected:
+ virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+
+private:
+ sp<hardware::power::V1_0::IPower> mHandleV1_0;
+ HalResult setInteractive(bool enabled);
+ HalResult setFeature(hardware::power::V1_0::Feature feature, bool enabled);
+};
+
+// Wrapper for the HIDL Power HAL v1.1.
+class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
+public:
+ HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0,
+ sp<hardware::power::V1_1::IPower> handleV1_1)
+ : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {}
+ virtual ~HidlHalWrapperV1_1() = default;
+
+protected:
+ virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+ uint32_t data) override;
+
+private:
+ sp<hardware::power::V1_1::IPower> mHandleV1_1;
+};
+
+// Wrapper for the AIDL Power HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+ explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
+ virtual ~AidlHalWrapper() = default;
+
+ virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+
+private:
+ // Control access to the boost and mode supported arrays.
+ std::mutex mBoostMutex;
+ std::mutex mModeMutex;
+ sp<hardware::power::IPower> mHandle;
+ // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
+ // Need to increase the array size if more boost supported.
+ std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+ mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
+ // Android framework only sends mode upto DISPLAY_INACTIVE.
+ // Need to increase the array if more mode supported.
+ std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1>
+ mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
+};
+
+}; // namespace power
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALWRAPPER_H
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index f66673f..258a4e3 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -29,6 +29,8 @@
name: "libarect",
host_supported: true,
vendor_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
windows: {
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..d005058 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,6 +98,15 @@
return PROCESS_STATE_UNKNOWN;
}
+bool ActivityManager::setSchedPolicyCgroup(const int32_t tid, const int32_t group)
+{
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->setSchedPolicyCgroup(tid, group);
+ }
+ return false;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 2174ce2..1c6b491 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -91,12 +91,12 @@
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
- return noteOp(op, uid, callingPackage, std::unique_ptr<String16>(),
+ return noteOp(op, uid, callingPackage, {},
String16("Legacy AppOpsManager.noteOp call"));
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& attributionTag, const String16& message) {
+ const std::optional<String16>& attributionTag, const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->noteOperation(op, uid, callingPackage, attributionTag,
@@ -108,12 +108,12 @@
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault) {
- return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, std::unique_ptr<String16>(),
+ return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, {},
String16("Legacy AppOpsManager.startOpNoThrow call"));
}
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
+ bool startIfModeDefault, const std::optional<String16>& attributionTag,
const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
@@ -125,11 +125,11 @@
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
- finishOp(op, uid, callingPackage, std::unique_ptr<String16>());
+ finishOp(op, uid, callingPackage, {});
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& attributionTag) {
+ const std::optional<String16>& attributionTag) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..a3021122 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,6 +104,17 @@
}
return reply.readInt32();
}
+
+ virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeInt32(tid);
+ data.writeInt32(group);
+ remote()->transact(SET_SCHED_POLICY_CGROUP_TRANSACTION, data, &reply);
+ if (reply.readExceptionCode() != 0) return false;
+ return reply.readBool();
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 0714723..cd78866 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -22,6 +22,8 @@
#include <binder/Parcel.h>
#include <utils/String8.h>
+#include <optional>
+
namespace android {
// ----------------------------------------------------------------------
@@ -47,7 +49,7 @@
}
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+ const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -64,7 +66,7 @@
}
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& attributionTag,
+ const String16& packageName, const std::optional<String16>& attributionTag,
bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -83,7 +85,7 @@
}
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& attributionTag) {
+ const String16& packageName, const std::optional<String16>& attributionTag) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
@@ -182,7 +184,7 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> attributionTag;
+ std::optional<String16> attributionTag;
data.readString16(&attributionTag);
bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
String16 message = data.readString16();
@@ -198,7 +200,7 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> attributionTag;
+ std::optional<String16> attributionTag;
data.readString16(&attributionTag);
bool startIfModeDefault = data.readInt32() == 1;
bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
@@ -215,7 +217,7 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> attributionTag;
+ std::optional<String16> attributionTag;
data.readString16(&attributionTag);
finishOperation(token, code, uid, packageName, attributionTag);
reply->writeNoException();
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index c2bb811..d8b44f9 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -82,10 +82,10 @@
explicit BpMemoryHeap(const sp<IBinder>& impl);
virtual ~BpMemoryHeap();
- virtual int getHeapID() const;
- virtual void* getBase() const;
- virtual size_t getSize() const;
- virtual uint32_t getFlags() const;
+ int getHeapID() const override;
+ void* getBase() const override;
+ size_t getSize() const override;
+ uint32_t getFlags() const override;
off_t getOffset() const override;
private:
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index a9f2d73..05f43e3 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -206,6 +206,10 @@
: mTheRealServiceManager(impl)
{}
+// This implementation could be simplified and made more efficient by delegating
+// to waitForService. However, this changes the threading structure in some
+// cases and could potentially break prebuilts. Once we have higher logistical
+// complexity, this could be attempted.
sp<IBinder> ServiceManagerShim::getService(const String16& name) const
{
static bool gSystemBootCompleted = false;
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 6f49aa1..325e204 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -45,25 +45,31 @@
Status onClients(const sp<IBinder>& service, bool clients) override;
private:
+ struct Service {
+ sp<IBinder> service;
+ bool allowIsolated;
+ int dumpFlags;
+
+ // whether, based on onClients calls, we know we have a client for this
+ // service or not
+ bool clients = false;
+ };
+
+ /**
+ * Looks up a service guaranteed to be registered (service from onClients).
+ */
+ std::map<std::string, Service>::iterator assertRegisteredService(const sp<IBinder>& service);
+
/**
* Unregisters all services that we can. If we can't unregister all, re-register other
* services.
*/
void tryShutdown();
- /**
- * Counter of the number of services that currently have at least one client.
- */
+ // count of services with clients
size_t mNumConnectedServices;
- struct Service {
- sp<IBinder> service;
- bool allowIsolated;
- int dumpFlags;
- };
- /**
- * Map of registered names and services
- */
+ // map of registered names and services
std::map<std::string, Service> mRegisteredServices;
bool mForcePersist;
@@ -89,12 +95,28 @@
}
// Only add this when a service is added for the first time, as it is not removed
- mRegisteredServices[name] = {service, allowIsolated, dumpFlags};
+ mRegisteredServices[name] = {
+ .service = service,
+ .allowIsolated = allowIsolated,
+ .dumpFlags = dumpFlags
+ };
}
return true;
}
+std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) {
+ LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service");
+ for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) {
+ auto const& [name, registered] = *it;
+ (void) name;
+ if (registered.service != service) continue;
+ return it;
+ }
+ LOG_ALWAYS_FATAL("Got callback on service which we did not register: %s", String8(service->getInterfaceDescriptor()).c_str());
+ __builtin_unreachable();
+}
+
void ClientCounterCallback::forcePersist(bool persist) {
mForcePersist = persist;
if(!mForcePersist) {
@@ -108,15 +130,25 @@
* invocations could occur on different threads however.
*/
Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
- if (clients) {
- mNumConnectedServices++;
- } else {
- mNumConnectedServices--;
+ auto & [name, registered] = *assertRegisteredService(service);
+ if (registered.clients == clients) {
+ LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
+ "notified has clients: %d", name.c_str(), registered.clients, clients);
+ }
+ registered.clients = clients;
+
+ // update cache count of clients
+ {
+ size_t numWithClients = 0;
+ for (const auto& [name, registered] : mRegisteredServices) {
+ (void) name;
+ if (registered.clients) numWithClients++;
+ }
+ mNumConnectedServices = numWithClients;
}
ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
- mNumConnectedServices, mRegisteredServices.size(),
- String8(service->getInterfaceDescriptor()).string(), clients);
+ mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients);
tryShutdown();
return Status::ok();
@@ -192,4 +224,4 @@
}
} // namespace hardware
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 9642a87..166c1d8 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -751,6 +751,13 @@
return NO_ERROR;
}
+status_t Parcel::writeUtf8AsUtf16(const std::optional<std::string>& str) {
+ if (!str) {
+ return writeInt32(-1);
+ }
+ return writeUtf8AsUtf16(*str);
+}
+
status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) {
if (!str) {
return writeInt32(-1);
@@ -775,6 +782,12 @@
return writeByteVectorInternal(val.data(), val.size());
}
+status_t Parcel::writeByteVector(const std::optional<std::vector<int8_t>>& val)
+{
+ if (!val) return writeInt32(-1);
+ return writeByteVectorInternal(val->data(), val->size());
+}
+
status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
{
if (!val) return writeInt32(-1);
@@ -785,6 +798,12 @@
return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size());
}
+status_t Parcel::writeByteVector(const std::optional<std::vector<uint8_t>>& val)
+{
+ if (!val) return writeInt32(-1);
+ return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
+}
+
status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val)
{
if (!val) return writeInt32(-1);
@@ -796,6 +815,11 @@
return writeTypedVector(val, &Parcel::writeInt32);
}
+status_t Parcel::writeInt32Vector(const std::optional<std::vector<int32_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeInt32);
+}
+
status_t Parcel::writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeInt32);
@@ -806,6 +830,11 @@
return writeTypedVector(val, &Parcel::writeInt64);
}
+status_t Parcel::writeInt64Vector(const std::optional<std::vector<int64_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeInt64);
+}
+
status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeInt64);
@@ -816,6 +845,11 @@
return writeTypedVector(val, &Parcel::writeUint64);
}
+status_t Parcel::writeUint64Vector(const std::optional<std::vector<uint64_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeUint64);
+}
+
status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeUint64);
@@ -826,6 +860,11 @@
return writeTypedVector(val, &Parcel::writeFloat);
}
+status_t Parcel::writeFloatVector(const std::optional<std::vector<float>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeFloat);
+}
+
status_t Parcel::writeFloatVector(const std::unique_ptr<std::vector<float>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeFloat);
@@ -836,6 +875,11 @@
return writeTypedVector(val, &Parcel::writeDouble);
}
+status_t Parcel::writeDoubleVector(const std::optional<std::vector<double>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeDouble);
+}
+
status_t Parcel::writeDoubleVector(const std::unique_ptr<std::vector<double>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeDouble);
@@ -846,6 +890,11 @@
return writeTypedVector(val, &Parcel::writeBool);
}
+status_t Parcel::writeBoolVector(const std::optional<std::vector<bool>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeBool);
+}
+
status_t Parcel::writeBoolVector(const std::unique_ptr<std::vector<bool>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeBool);
@@ -856,6 +905,11 @@
return writeTypedVector(val, &Parcel::writeChar);
}
+status_t Parcel::writeCharVector(const std::optional<std::vector<char16_t>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeChar);
+}
+
status_t Parcel::writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeChar);
@@ -867,12 +921,23 @@
}
status_t Parcel::writeString16Vector(
+ const std::optional<std::vector<std::optional<String16>>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeString16);
+}
+
+status_t Parcel::writeString16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeString16);
}
status_t Parcel::writeUtf8VectorAsUtf16Vector(
+ const std::optional<std::vector<std::optional<std::string>>>& val) {
+ return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
+}
+
+status_t Parcel::writeUtf8VectorAsUtf16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) {
return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
}
@@ -1007,6 +1072,15 @@
return err;
}
+status_t Parcel::writeString16(const std::optional<String16>& str)
+{
+ if (!str) {
+ return writeInt32(-1);
+ }
+
+ return writeString16(*str);
+}
+
status_t Parcel::writeString16(const std::unique_ptr<String16>& str)
{
if (!str) {
@@ -1049,11 +1123,20 @@
return writeTypedVector(val, &Parcel::writeStrongBinder);
}
+status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val)
+{
+ return writeNullableTypedVector(val, &Parcel::writeStrongBinder);
+}
+
status_t Parcel::writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val)
{
return writeNullableTypedVector(val, &Parcel::writeStrongBinder);
}
+status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
+}
+
status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const {
return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
}
@@ -1152,6 +1235,10 @@
return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor);
}
+status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) {
+ return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
+}
+
status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) {
return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
}
@@ -1485,6 +1572,17 @@
return readByteVectorInternal(val, size);
}
+status_t Parcel::readByteVector(std::optional<std::vector<int8_t>>* val) const {
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ if (!*val) {
+ // reserveOutVector does not create the out vector if size is < 0.
+ // This occurs when writing a null byte vector.
+ return OK;
+ }
+ return readByteVectorInternal(&**val, size);
+}
+
status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const {
size_t size;
if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1496,6 +1594,17 @@
return readByteVectorInternal(val->get(), size);
}
+status_t Parcel::readByteVector(std::optional<std::vector<uint8_t>>* val) const {
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ if (!*val) {
+ // reserveOutVector does not create the out vector if size is < 0.
+ // This occurs when writing a null byte vector.
+ return OK;
+ }
+ return readByteVectorInternal(&**val, size);
+}
+
status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const {
size_t size;
if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1507,6 +1616,10 @@
return readByteVectorInternal(val->get(), size);
}
+status_t Parcel::readInt32Vector(std::optional<std::vector<int32_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readInt32);
+}
+
status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const {
return readNullableTypedVector(val, &Parcel::readInt32);
}
@@ -1515,6 +1628,10 @@
return readTypedVector(val, &Parcel::readInt32);
}
+status_t Parcel::readInt64Vector(std::optional<std::vector<int64_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readInt64);
+}
+
status_t Parcel::readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const {
return readNullableTypedVector(val, &Parcel::readInt64);
}
@@ -1523,6 +1640,10 @@
return readTypedVector(val, &Parcel::readInt64);
}
+status_t Parcel::readUint64Vector(std::optional<std::vector<uint64_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readUint64);
+}
+
status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const {
return readNullableTypedVector(val, &Parcel::readUint64);
}
@@ -1531,6 +1652,10 @@
return readTypedVector(val, &Parcel::readUint64);
}
+status_t Parcel::readFloatVector(std::optional<std::vector<float>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readFloat);
+}
+
status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const {
return readNullableTypedVector(val, &Parcel::readFloat);
}
@@ -1539,6 +1664,10 @@
return readTypedVector(val, &Parcel::readFloat);
}
+status_t Parcel::readDoubleVector(std::optional<std::vector<double>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readDouble);
+}
+
status_t Parcel::readDoubleVector(std::unique_ptr<std::vector<double>>* val) const {
return readNullableTypedVector(val, &Parcel::readDouble);
}
@@ -1547,6 +1676,28 @@
return readTypedVector(val, &Parcel::readDouble);
}
+status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const {
+ const int32_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ val->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ val->emplace();
+
+ status = readBoolVector(&**val);
+
+ if (status != OK) {
+ val->reset();
+ }
+
+ return status;
+}
+
status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const {
const int32_t start = dataPosition();
int32_t size;
@@ -1599,6 +1750,10 @@
return OK;
}
+status_t Parcel::readCharVector(std::optional<std::vector<char16_t>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readChar);
+}
+
status_t Parcel::readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const {
return readNullableTypedVector(val, &Parcel::readChar);
}
@@ -1608,6 +1763,11 @@
}
status_t Parcel::readString16Vector(
+ std::optional<std::vector<std::optional<String16>>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readString16);
+}
+
+status_t Parcel::readString16Vector(
std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const {
return readNullableTypedVector(val, &Parcel::readString16);
}
@@ -1617,6 +1777,11 @@
}
status_t Parcel::readUtf8VectorFromUtf16Vector(
+ std::optional<std::vector<std::optional<std::string>>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
+}
+
+status_t Parcel::readUtf8VectorFromUtf16Vector(
std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const {
return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
}
@@ -1808,6 +1973,21 @@
return NO_ERROR;
}
+status_t Parcel::readUtf8FromUtf16(std::optional<std::string>* str) const {
+ const int32_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ str->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ str->emplace();
+ return readUtf8FromUtf16(&**str);
+}
+
status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const {
const int32_t start = dataPosition();
int32_t size;
@@ -1886,6 +2066,29 @@
return String16();
}
+status_t Parcel::readString16(std::optional<String16>* pArg) const
+{
+ const int32_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ pArg->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ pArg->emplace();
+
+ status = readString16(&**pArg);
+
+ if (status != OK) {
+ pArg->reset();
+ }
+
+ return status;
+}
+
status_t Parcel::readString16(std::unique_ptr<String16>* pArg) const
{
const int32_t start = dataPosition();
@@ -2091,6 +2294,10 @@
return OK;
}
+status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
+}
+
status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const {
return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
}
@@ -2436,7 +2643,7 @@
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
- : continueWrite(newSize);
+ : continueWrite(std::max(newSize, (size_t) 128));
}
status_t Parcel::restartWrite(size_t desired)
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4b773e8..f3861bb 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -328,6 +328,8 @@
}
status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) {
+ LOG_ALWAYS_FATAL_IF(mThreadPoolStarted && maxThreads < mMaxThreads,
+ "Binder threadpool cannot be shrunk after starting");
status_t result = NO_ERROR;
if (ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads) != -1) {
mMaxThreads = maxThreads;
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index 674f065..64ab7a9 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -193,13 +193,15 @@
}
status_t status = parcel->writeInt32(mException);
- if (status != OK) { return status; }
+ if (status != OK) return status;
if (mException == EX_NONE) {
// We have no more information to write.
return status;
}
status = parcel->writeString16(String16(mMessage));
+ if (status != OK) return status;
status = parcel->writeInt32(0); // Empty remote stack trace header
+ if (status != OK) return status;
if (mException == EX_SERVICE_SPECIFIC) {
status = parcel->writeInt32(mErrorCode);
} else if (mException == EX_PARCELABLE) {
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 9aa7651..c232283 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -7,6 +7,9 @@
"name": "binderVendorDoubleLoadTest"
},
{
+ "name": "binderAllocationLimits"
+ },
+ {
"name": "binderDriverInterfaceTest"
},
{
@@ -29,6 +32,17 @@
},
{
"name": "libbinderthreadstateutils_test"
+ },
+ {
+ "name": "CtsOsTestCases",
+ "options": [
+ {
+ "exclude-filter": "android.os.cts.BuildTest#testSdkInt"
+ },
+ {
+ "exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage"
+ }
+ ]
}
]
}
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
index 52c730c..8c0495c 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/fuzzer/binder.cpp
@@ -135,6 +135,7 @@
PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16),
+ PARCEL_READ_WITH_STATUS(std::optional<std::string>, readUtf8FromUtf16),
[] (const ::android::Parcel& p, uint8_t /*data*/) {
FUZZ_LOG() << "about to read c-str";
const char* str = p.readCString();
@@ -143,6 +144,7 @@
PARCEL_READ_OPT_STATUS(android::String8, readString8),
PARCEL_READ_OPT_STATUS(android::String16, readString16),
PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
+ PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16),
[] (const ::android::Parcel& p, uint8_t /*data*/) {
FUZZ_LOG() << "about to readString16Inplace";
size_t outLen = 0;
@@ -156,17 +158,22 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector),
// only reading one parcelable type for now
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector),
// PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
+ PARCEL_READ_WITH_STATUS(std::optional<ExampleParcelable>, readParcelable),
// only reading one binder type for now
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
@@ -174,30 +181,42 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+ // PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
// PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector),
// PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector),
// PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector),
// PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector),
// PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector),
// PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector),
// PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector),
// PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector),
// PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
// PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
[] (const android::Parcel& p, uint8_t /*len*/) {
@@ -234,6 +253,7 @@
// TODO(b/131868573): can force read of arbitrarily sized vector
// PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+ // PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
// PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
[] (const android::Parcel& p, uint8_t len) {
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..7043b17 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -77,7 +77,7 @@
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
-
+ bool setSchedPolicyCgroup(const int32_t tid, const int32_t group);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 6afcd77..6d04f13 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -21,6 +21,8 @@
#include <utils/threads.h>
+#include <optional>
+
#ifdef __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif
@@ -143,18 +145,18 @@
// const String16&) instead
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& attributionTag, const String16& message);
+ const std::optional<String16>& attributionTag, const String16& message);
// @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
// const String16&) instead
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault);
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
+ bool startIfModeDefault, const std::optional<String16>& attributionTag,
const String16& message);
// @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& attributionTag);
+ const std::optional<String16>& attributionTag);
void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback);
void stopWatchingMode(const sp<IAppOpsCallback>& callback);
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 8e871b8..378a911 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -137,7 +137,6 @@
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
ObjectManager mObjects;
- Parcel* mConstantData;
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..fe58a41 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,15 @@
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+ virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ SET_SCHED_POLICY_CGROUP_TRANSACTION
};
};
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 1ffb8de..a4a20c8 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -21,6 +21,8 @@
#include <binder/IAppOpsCallback.h>
#include <binder/IInterface.h>
+#include <optional>
+
#ifdef __ANDROID_VNDK__
#error "This header is not visible to vendors"
#endif
@@ -36,13 +38,13 @@
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+ const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& attributionTag,
+ const String16& packageName, const std::optional<String16>& attributionTag,
bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& attributionTag) = 0;
+ const String16& packageName, const std::optional<String16>& attributionTag) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 64604b7..eea0e89 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -173,6 +173,10 @@
* The @a cookie is optional -- if non-NULL, it should be a
* memory address that you own (that is, you know it is unique).
*
+ * @note When all references to the binder being linked to are dropped, the
+ * recipient is automatically unlinked. So, you must hold onto a binder in
+ * order to receive death notifications about it.
+ *
* @note You will only receive death notifications for remote binders,
* as local binders by definition can't die without you dying as well.
* Trying to use this function on a local binder will result in an
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 4818889..b4e4a42 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -39,12 +39,28 @@
status_t clearLastError();
+ /**
+ * Returns the PID of the process which has made the current binder
+ * call. If not in a binder call, this will return getpid. If the
+ * call is oneway, this will return 0.
+ */
pid_t getCallingPid() const;
- // nullptr if unavailable
- //
- // this can't be restored once it's cleared, and it does not return the
- // context of the current process when not in a binder call.
+
+ /**
+ * Returns the SELinux security identifier of the process which has
+ * made the current binder call. If not in a binder call this will
+ * return nullptr. If this isn't requested with
+ * IBinder::setRequestingSid, it will also return nullptr.
+ *
+ * This can't be restored once it's cleared, and it does not return the
+ * context of the current process when not in a binder call.
+ */
const char* getCallingSid() const;
+
+ /**
+ * Returns the UID of the process which has made the current binder
+ * call. If not in a binder call, this will return 0.
+ */
uid_t getCallingUid() const;
void setStrictModePolicy(int32_t policy);
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 3fccddc..edada3d 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -57,14 +57,14 @@
virtual ~MemoryHeapBase();
/* implement IMemoryHeap interface */
- virtual int getHeapID() const;
+ int getHeapID() const override;
/* virtual address of the heap. returns MAP_FAILED in case of error */
- virtual void* getBase() const;
+ void* getBase() const override;
- virtual size_t getSize() const;
- virtual uint32_t getFlags() const;
- off_t getOffset() const override;
+ size_t getSize() const override;
+ uint32_t getFlags() const override;
+ off_t getOffset() const override;
const char* getDevice() const;
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
index b605bd3..a98583d 100644
--- a/libs/binder/include/binder/Nullable.h
+++ b/libs/binder/include/binder/Nullable.h
@@ -15,7 +15,7 @@
*/
#pragma once
-#include <memory>
+#include <optional>
#include <utility>
namespace android {
@@ -32,11 +32,11 @@
// c = std::move(a);
template <typename T>
-using nullable = std::unique_ptr<T>;
+using nullable = std::optional<T>;
template <typename T, typename... Args>
inline nullable<T> make_nullable(Args&&... args) {
- return std::make_unique<T>(std::forward<Args>(args)...);
+ return std::make_optional<T>(std::forward<Args>(args)...);
}
} // namespace aidl
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index c1f64fb..b6cfb8e 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -121,6 +121,7 @@
status_t writeString8(const String8& str);
status_t writeString8(const char* str, size_t len);
status_t writeString16(const String16& str);
+ status_t writeString16(const std::optional<String16>& str);
status_t writeString16(const std::unique_ptr<String16>& str);
status_t writeString16(const char16_t* str, size_t len);
status_t writeStrongBinder(const sp<IBinder>& val);
@@ -132,33 +133,48 @@
// Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
status_t writeUtf8AsUtf16(const std::string& str);
+ status_t writeUtf8AsUtf16(const std::optional<std::string>& str);
status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
+ status_t writeByteVector(const std::optional<std::vector<int8_t>>& val);
status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
status_t writeByteVector(const std::vector<int8_t>& val);
+ status_t writeByteVector(const std::optional<std::vector<uint8_t>>& val);
status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
status_t writeByteVector(const std::vector<uint8_t>& val);
+ status_t writeInt32Vector(const std::optional<std::vector<int32_t>>& val);
status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
status_t writeInt32Vector(const std::vector<int32_t>& val);
+ status_t writeInt64Vector(const std::optional<std::vector<int64_t>>& val);
status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
status_t writeInt64Vector(const std::vector<int64_t>& val);
+ status_t writeUint64Vector(const std::optional<std::vector<uint64_t>>& val);
status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val);
status_t writeUint64Vector(const std::vector<uint64_t>& val);
+ status_t writeFloatVector(const std::optional<std::vector<float>>& val);
status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
status_t writeFloatVector(const std::vector<float>& val);
+ status_t writeDoubleVector(const std::optional<std::vector<double>>& val);
status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
status_t writeDoubleVector(const std::vector<double>& val);
+ status_t writeBoolVector(const std::optional<std::vector<bool>>& val);
status_t writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
status_t writeBoolVector(const std::vector<bool>& val);
+ status_t writeCharVector(const std::optional<std::vector<char16_t>>& val);
status_t writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
status_t writeCharVector(const std::vector<char16_t>& val);
status_t writeString16Vector(
+ const std::optional<std::vector<std::optional<String16>>>& val);
+ status_t writeString16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
status_t writeString16Vector(const std::vector<String16>& val);
status_t writeUtf8VectorAsUtf16Vector(
+ const std::optional<std::vector<std::optional<std::string>>>& val);
+ status_t writeUtf8VectorAsUtf16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
status_t writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
+ status_t writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val);
status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
@@ -167,14 +183,20 @@
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::vector<T>& val);
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+ status_t writeEnumVector(const std::optional<std::vector<T>>& val);
+ template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
// Write an Enum vector with underlying type != int8_t.
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::vector<T>& val);
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+ status_t writeEnumVector(const std::optional<std::vector<T>>& val);
+ template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
template<typename T>
+ status_t writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val);
+ template<typename T>
status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
template<typename T>
status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
@@ -182,6 +204,8 @@
status_t writeParcelableVector(const std::vector<T>& val);
template<typename T>
+ status_t writeNullableParcelable(const std::optional<T>& parcelable);
+ template<typename T>
status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable);
status_t writeParcelable(const Parcelable& parcelable);
@@ -195,6 +219,8 @@
template<typename T>
status_t writeVectorSize(const std::vector<T>& val);
template<typename T>
+ status_t writeVectorSize(const std::optional<std::vector<T>>& val);
+ template<typename T>
status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val);
// Place a native_handle into the parcel (the native_handle's file-
@@ -230,6 +256,8 @@
// Place a vector of file desciptors into the parcel. Each descriptor is
// dup'd as in writeDupFileDescriptor
status_t writeUniqueFileDescriptorVector(
+ const std::optional<std::vector<base::unique_fd>>& val);
+ status_t writeUniqueFileDescriptorVector(
const std::unique_ptr<std::vector<base::unique_fd>>& val);
status_t writeUniqueFileDescriptorVector(
const std::vector<base::unique_fd>& val);
@@ -279,6 +307,7 @@
// Read a UTF16 encoded string, convert to UTF8
status_t readUtf8FromUtf16(std::string* str) const;
+ status_t readUtf8FromUtf16(std::optional<std::string>* str) const;
status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
const char* readCString() const;
@@ -287,27 +316,34 @@
const char* readString8Inplace(size_t* outLen) const;
String16 readString16() const;
status_t readString16(String16* pArg) const;
+ status_t readString16(std::optional<String16>* pArg) const;
status_t readString16(std::unique_ptr<String16>* pArg) const;
const char16_t* readString16Inplace(size_t* outLen) const;
sp<IBinder> readStrongBinder() const;
status_t readStrongBinder(sp<IBinder>* val) const;
status_t readNullableStrongBinder(sp<IBinder>* val) const;
-
// Read an Enum vector with underlying type int8_t.
// Does not use padding; each byte is contiguous.
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::vector<T>* val) const;
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+ template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+ status_t readEnumVector(std::optional<std::vector<T>>* val) const;
// Read an Enum vector with underlying type != int8_t.
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::vector<T>* val) const;
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+ template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
+ status_t readEnumVector(std::optional<std::vector<T>>* val) const;
template<typename T>
status_t readParcelableVector(
+ std::optional<std::vector<std::optional<T>>>* val) const;
+ template<typename T>
+ status_t readParcelableVector(
std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
template<typename T>
status_t readParcelableVector(std::vector<T>* val) const;
@@ -315,6 +351,8 @@
status_t readParcelable(Parcelable* parcelable) const;
template<typename T>
+ status_t readParcelable(std::optional<T>* parcelable) const;
+ template<typename T>
status_t readParcelable(std::unique_ptr<T>* parcelable) const;
template<typename T>
@@ -323,31 +361,45 @@
template<typename T>
status_t readNullableStrongBinder(sp<T>* val) const;
+ status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
+ status_t readByteVector(std::optional<std::vector<int8_t>>* val) const;
status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
status_t readByteVector(std::vector<int8_t>* val) const;
+ status_t readByteVector(std::optional<std::vector<uint8_t>>* val) const;
status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
status_t readByteVector(std::vector<uint8_t>* val) const;
+ status_t readInt32Vector(std::optional<std::vector<int32_t>>* val) const;
status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
status_t readInt32Vector(std::vector<int32_t>* val) const;
+ status_t readInt64Vector(std::optional<std::vector<int64_t>>* val) const;
status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
status_t readInt64Vector(std::vector<int64_t>* val) const;
+ status_t readUint64Vector(std::optional<std::vector<uint64_t>>* val) const;
status_t readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const;
status_t readUint64Vector(std::vector<uint64_t>* val) const;
+ status_t readFloatVector(std::optional<std::vector<float>>* val) const;
status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
status_t readFloatVector(std::vector<float>* val) const;
+ status_t readDoubleVector(std::optional<std::vector<double>>* val) const;
status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
status_t readDoubleVector(std::vector<double>* val) const;
+ status_t readBoolVector(std::optional<std::vector<bool>>* val) const;
status_t readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
status_t readBoolVector(std::vector<bool>* val) const;
+ status_t readCharVector(std::optional<std::vector<char16_t>>* val) const;
status_t readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
status_t readCharVector(std::vector<char16_t>* val) const;
status_t readString16Vector(
+ std::optional<std::vector<std::optional<String16>>>* val) const;
+ status_t readString16Vector(
std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
status_t readString16Vector(std::vector<String16>* val) const;
status_t readUtf8VectorFromUtf16Vector(
+ std::optional<std::vector<std::optional<std::string>>>* val) const;
+ status_t readUtf8VectorFromUtf16Vector(
std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
@@ -360,10 +412,15 @@
template<typename T>
status_t resizeOutVector(std::vector<T>* val) const;
template<typename T>
+ status_t resizeOutVector(std::optional<std::vector<T>>* val) const;
+ template<typename T>
status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
template<typename T>
status_t reserveOutVector(std::vector<T>* val, size_t* size) const;
template<typename T>
+ status_t reserveOutVector(std::optional<std::vector<T>>* val,
+ size_t* size) const;
+ template<typename T>
status_t reserveOutVector(std::unique_ptr<std::vector<T>>* val,
size_t* size) const;
@@ -399,6 +456,8 @@
// Retrieve a vector of smart file descriptors from the parcel.
status_t readUniqueFileDescriptorVector(
+ std::optional<std::vector<base::unique_fd>>* val) const;
+ status_t readUniqueFileDescriptorVector(
std::unique_ptr<std::vector<base::unique_fd>>* val) const;
status_t readUniqueFileDescriptorVector(
std::vector<base::unique_fd>* val) const;
@@ -492,6 +551,9 @@
status_t unsafeReadTypedVector(std::vector<T>* val,
status_t(Parcel::*read_func)(U*) const) const;
template<typename T>
+ status_t readNullableTypedVector(std::optional<std::vector<T>>* val,
+ status_t(Parcel::*read_func)(T*) const) const;
+ template<typename T>
status_t readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
status_t(Parcel::*read_func)(T*) const) const;
template<typename T>
@@ -501,9 +563,15 @@
status_t unsafeWriteTypedVector(const std::vector<T>& val,
status_t(Parcel::*write_func)(U));
template<typename T>
+ status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(const T&));
+ template<typename T>
status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
status_t(Parcel::*write_func)(const T&));
template<typename T>
+ status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(T));
+ template<typename T>
status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
status_t(Parcel::*write_func)(T));
template<typename T>
@@ -691,6 +759,15 @@
}
template<typename T>
+status_t Parcel::writeVectorSize(const std::optional<std::vector<T>>& val) {
+ if (!val) {
+ return writeInt32(-1);
+ }
+
+ return writeVectorSize(*val);
+}
+
+template<typename T>
status_t Parcel::writeVectorSize(const std::unique_ptr<std::vector<T>>& val) {
if (!val) {
return writeInt32(-1);
@@ -715,6 +792,22 @@
}
template<typename T>
+status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
+ int32_t size;
+ status_t err = readInt32(&size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ val->reset();
+ if (size >= 0) {
+ val->emplace(size_t(size));
+ }
+
+ return OK;
+}
+
+template<typename T>
status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
int32_t size;
status_t err = readInt32(&size);
@@ -747,6 +840,25 @@
}
template<typename T>
+status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const {
+ int32_t read_size;
+ status_t err = readInt32(&read_size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (read_size >= 0) {
+ *size = static_cast<size_t>(read_size);
+ val->emplace();
+ (*val)->reserve(*size);
+ } else {
+ val->reset();
+ }
+
+ return OK;
+}
+
+template<typename T>
status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val,
size_t* size) const {
int32_t read_size;
@@ -841,6 +953,30 @@
}
template<typename T>
+status_t Parcel::readNullableTypedVector(std::optional<std::vector<T>>* val,
+ status_t(Parcel::*read_func)(T*) const) const {
+ const size_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ val->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ val->emplace();
+
+ status = unsafeReadTypedVector(&**val, read_func);
+
+ if (status != OK) {
+ val->reset();
+ }
+
+ return status;
+}
+
+template<typename T>
status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
status_t(Parcel::*read_func)(T*) const) const {
const size_t start = dataPosition();
@@ -901,6 +1037,16 @@
}
template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(const T&)) {
+ if (!val) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
status_t(Parcel::*write_func)(const T&)) {
if (val.get() == nullptr) {
@@ -911,6 +1057,16 @@
}
template<typename T>
+status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val,
+ status_t(Parcel::*write_func)(T)) {
+ if (!val) {
+ return this->writeInt32(-1);
+ }
+
+ return unsafeWriteTypedVector(*val, write_func);
+}
+
+template<typename T>
status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
status_t(Parcel::*write_func)(T)) {
if (val.get() == nullptr) {
@@ -926,6 +1082,30 @@
}
template<typename T>
+status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const {
+ const size_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ val->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ val->emplace();
+
+ using NullableT = std::optional<T>;
+ status = unsafeReadTypedVector<NullableT, NullableT>(&**val, &Parcel::readParcelable);
+
+ if (status != OK) {
+ val->reset();
+ }
+
+ return status;
+}
+
+template<typename T>
status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
const size_t start = dataPosition();
int32_t size;
@@ -939,7 +1119,8 @@
setDataPosition(start);
val->reset(new std::vector<std::unique_ptr<T>>());
- status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
+ using NullableT = std::unique_ptr<T>;
+ status = unsafeReadTypedVector<NullableT, NullableT>(val->get(), &Parcel::readParcelable);
if (status != OK) {
val->reset();
@@ -949,6 +1130,29 @@
}
template<typename T>
+status_t Parcel::readParcelable(std::optional<T>* parcelable) const {
+ const size_t start = dataPosition();
+ int32_t present;
+ status_t status = readInt32(&present);
+ parcelable->reset();
+
+ if (status != OK || !present) {
+ return status;
+ }
+
+ setDataPosition(start);
+ parcelable->emplace();
+
+ status = readParcelable(&**parcelable);
+
+ if (status != OK) {
+ parcelable->reset();
+ }
+
+ return status;
+}
+
+template<typename T>
status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const {
const size_t start = dataPosition();
int32_t present;
@@ -972,6 +1176,11 @@
}
template<typename T>
+status_t Parcel::writeNullableParcelable(const std::optional<T>& parcelable) {
+ return writeRawNullableParcelable(parcelable ? &*parcelable : nullptr);
+}
+
+template<typename T>
status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
return writeRawNullableParcelable(parcelable.get());
}
@@ -982,6 +1191,16 @@
}
template<typename T>
+status_t Parcel::writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val) {
+ if (!val) {
+ return this->writeInt32(-1);
+ }
+
+ using NullableT = std::optional<T>;
+ return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
+}
+
+template<typename T>
status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) {
if (val.get() == nullptr) {
return this->writeInt32(-1);
@@ -996,7 +1215,8 @@
return this->writeInt32(-1);
}
- return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
+ using NullableT = std::unique_ptr<T>;
+ return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
}
template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>>
@@ -1013,6 +1233,11 @@
return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size());
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) {
+ if (!val) return writeInt32(-1);
+ return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) {
if (!val) return writeInt32(-1);
return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
@@ -1022,6 +1247,10 @@
return writeTypedVector(val, &Parcel::writeEnum);
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) {
+ return writeNullableTypedVector(val, &Parcel::writeEnum);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) {
return writeNullableTypedVector(val, &Parcel::writeEnum);
}
@@ -1053,6 +1282,17 @@
return readByteVectorInternal(val, size);
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const {
+ size_t size;
+ if (status_t status = reserveOutVector(val, &size); status != OK) return status;
+ if (!*val) {
+ // reserveOutVector does not create the out vector if size is < 0.
+ // This occurs when writing a null Enum vector.
+ return OK;
+ }
+ return readByteVectorInternal(&**val, size);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
size_t size;
if (status_t status = reserveOutVector(val, &size); status != OK) return status;
@@ -1068,6 +1308,10 @@
return readTypedVector(val, &Parcel::readEnum);
}
template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
+status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readEnum);
+}
+template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
return readNullableTypedVector(val, &Parcel::readEnum);
}
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 4635ad8..71e1d3c 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -31,7 +31,8 @@
public:
ParcelFileDescriptor();
explicit ParcelFileDescriptor(android::base::unique_fd fd);
- ParcelFileDescriptor(ParcelFileDescriptor&& other) : mFd(std::move(other.mFd)) { }
+ ParcelFileDescriptor(ParcelFileDescriptor&& other) noexcept : mFd(std::move(other.mFd)) { }
+ ParcelFileDescriptor& operator=(ParcelFileDescriptor&& other) noexcept = default;
~ParcelFileDescriptor() override;
int get() const { return mFd.get(); }
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index a9166e2..c113279 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -52,6 +52,12 @@
//
// Returns android::OK on success and an appropriate error otherwise.
virtual status_t readFromParcel(const Parcel* parcel) = 0;
+
+ // 'Stable' means this parcelable is guaranteed to be stable for multiple years.
+ // It must be guaranteed by setting stability field in aidl_interface.
+ // WARNING: isStable() is only expected to be overridden by auto-generated code.
+ // Returns true if this parcelable is stable.
+ virtual bool isStable() const { return false; }
}; // class Parcelable
#if defined(__clang__)
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4560f22..13dbec1 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -26,6 +26,7 @@
#pragma once
+#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 86b75b8..a031e29 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,6 +26,7 @@
#pragma once
+#include <stdbool.h>
#include <stddef.h>
#include <sys/cdefs.h>
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 df5df13..09949ea 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -831,34 +831,34 @@
}
/**
- * Writes a vector of int8_t to the next location in a non-null parcel.
+ * Writes a vector of uint8_t to the next location in a non-null parcel.
*/
-inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int8_t>& vec) {
- return AParcel_writeByteArray(parcel, vec.data(), vec.size());
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint8_t>& vec) {
+ return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()), vec.size());
}
/**
- * Writes an optional vector of int8_t to the next location in a non-null parcel.
+ * Writes an optional vector of uint8_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel,
- const std::optional<std::vector<int8_t>>& vec) {
+ const std::optional<std::vector<uint8_t>>& vec) {
if (!vec) return AParcel_writeByteArray(parcel, nullptr, -1);
return AParcel_writeVector(parcel, *vec);
}
/**
- * Reads a vector of int8_t from the next location in a non-null parcel.
+ * Reads a vector of uint8_t from the next location in a non-null parcel.
*/
-inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int8_t>* vec) {
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<uint8_t>* vec) {
void* vectorData = static_cast<void*>(vec);
return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>);
}
/**
- * Reads an optional vector of int8_t from the next location in a non-null parcel.
+ * Reads an optional vector of uint8_t from the next location in a non-null parcel.
*/
inline binder_status_t AParcel_readVector(const AParcel* parcel,
- std::optional<std::vector<int8_t>>* vec) {
+ std::optional<std::vector<uint8_t>>* vec) {
void* vectorData = static_cast<void*>(vec);
return AParcel_readByteArray(parcel, vectorData, AParcel_nullableStdVectorAllocator<int8_t>);
}
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index ab9a144..3a55f94 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -26,6 +26,7 @@
#pragma once
#include <errno.h>
+#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/tests/Android.bp
similarity index 100%
rename from libs/binder/ndk/test/Android.bp
rename to libs/binder/ndk/tests/Android.bp
diff --git a/libs/binder/ndk/test/AndroidTest.xml b/libs/binder/ndk/tests/AndroidTest.xml
similarity index 100%
rename from libs/binder/ndk/test/AndroidTest.xml
rename to libs/binder/ndk/tests/AndroidTest.xml
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
similarity index 100%
rename from libs/binder/ndk/test/IBinderNdkUnitTest.aidl
rename to libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
diff --git a/libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl b/libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl
similarity index 100%
rename from libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl
rename to libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl
diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/tests/IEmpty.aidl
similarity index 100%
rename from libs/binder/ndk/test/IEmpty.aidl
rename to libs/binder/ndk/tests/IEmpty.aidl
diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
similarity index 100%
rename from libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
rename to libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
diff --git a/libs/binder/ndk/test/iface.cpp b/libs/binder/ndk/tests/iface.cpp
similarity index 100%
rename from libs/binder/ndk/test/iface.cpp
rename to libs/binder/ndk/tests/iface.cpp
diff --git a/libs/binder/ndk/test/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
similarity index 100%
rename from libs/binder/ndk/test/include/iface/iface.h
rename to libs/binder/ndk/tests/include/iface/iface.h
diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
similarity index 100%
rename from libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
rename to libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index c0da2cd..2680e84 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -169,3 +169,18 @@
test_suites: ["device-tests"],
require_root: true,
}
+
+cc_test {
+ name: "binderAllocationLimits",
+ defaults: ["binder_test_defaults"],
+ srcs: ["binderAllocationLimits.cpp"],
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ "libutilscallstack",
+ "libbase",
+ ],
+ test_suites: ["device-tests"],
+ require_root: true,
+}
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
new file mode 100644
index 0000000..e1f5ed5
--- /dev/null
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <binder/Parcel.h>
+#include <binder/IServiceManager.h>
+#include <gtest/gtest.h>
+#include <utils/CallStack.h>
+
+#include <malloc.h>
+#include <functional>
+#include <vector>
+
+struct DestructionAction {
+ DestructionAction(std::function<void()> f) : mF(std::move(f)) {}
+ ~DestructionAction() { mF(); };
+private:
+ std::function<void()> mF;
+};
+
+// Group of hooks
+struct MallocHooks {
+ decltype(__malloc_hook) malloc_hook;
+ decltype(__realloc_hook) realloc_hook;
+
+ static MallocHooks save() {
+ return {
+ .malloc_hook = __malloc_hook,
+ .realloc_hook = __realloc_hook,
+ };
+ }
+
+ void overwrite() const {
+ __malloc_hook = malloc_hook;
+ __realloc_hook = realloc_hook;
+ }
+};
+
+static const MallocHooks orig_malloc_hooks = MallocHooks::save();
+
+// When malloc is hit, executes lambda.
+namespace LambdaHooks {
+ using AllocationHook = std::function<void(size_t)>;
+ static std::vector<AllocationHook> lambdas = {};
+
+ static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg);
+ static void* lambda_malloc_hook(size_t bytes, const void* arg);
+
+ static const MallocHooks lambda_malloc_hooks = {
+ .malloc_hook = lambda_malloc_hook,
+ .realloc_hook = lambda_realloc_hook,
+ };
+
+ static void* lambda_malloc_hook(size_t bytes, const void* arg) {
+ {
+ orig_malloc_hooks.overwrite();
+ lambdas.at(lambdas.size() - 1)(bytes);
+ lambda_malloc_hooks.overwrite();
+ }
+ return orig_malloc_hooks.malloc_hook(bytes, arg);
+ }
+
+ static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg) {
+ {
+ orig_malloc_hooks.overwrite();
+ lambdas.at(lambdas.size() - 1)(bytes);
+ lambda_malloc_hooks.overwrite();
+ }
+ return orig_malloc_hooks.realloc_hook(ptr, bytes, arg);
+ }
+
+}
+
+// Action to execute when malloc is hit. Supports nesting. Malloc is not
+// restricted when the allocation hook is being processed.
+__attribute__((warn_unused_result))
+DestructionAction OnMalloc(LambdaHooks::AllocationHook f) {
+ MallocHooks before = MallocHooks::save();
+ LambdaHooks::lambdas.emplace_back(std::move(f));
+ LambdaHooks::lambda_malloc_hooks.overwrite();
+ return DestructionAction([before]() {
+ before.overwrite();
+ LambdaHooks::lambdas.pop_back();
+ });
+}
+
+// exported symbol, to force compiler not to optimize away pointers we set here
+const void* imaginary_use;
+
+TEST(TestTheTest, OnMalloc) {
+ size_t mallocs = 0;
+ {
+ const auto on_malloc = OnMalloc([&](size_t bytes) {
+ mallocs++;
+ EXPECT_EQ(bytes, 40);
+ });
+
+ imaginary_use = new int[10];
+ }
+ EXPECT_EQ(mallocs, 1);
+}
+
+
+__attribute__((warn_unused_result))
+DestructionAction ScopeDisallowMalloc() {
+ return OnMalloc([&](size_t bytes) {
+ ADD_FAILURE() << "Unexpected allocation: " << bytes;
+ using android::CallStack;
+ std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get())
+ << std::endl;
+ });
+}
+
+using android::IBinder;
+using android::Parcel;
+using android::String16;
+using android::defaultServiceManager;
+using android::sp;
+using android::IServiceManager;
+
+static sp<IBinder> GetRemoteBinder() {
+ // This gets binder representing the service manager
+ // the current IServiceManager API doesn't expose the binder, and
+ // I want to avoid adding usages of the AIDL generated interface it
+ // is using underneath, so to avoid people copying it.
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16("manager"));
+ EXPECT_NE(nullptr, binder);
+ return binder;
+}
+
+TEST(BinderAllocation, ParcelOnStack) {
+ const auto m = ScopeDisallowMalloc();
+ Parcel p;
+ imaginary_use = p.data();
+}
+
+TEST(BinderAllocation, GetServiceManager) {
+ defaultServiceManager(); // first call may alloc
+ const auto m = ScopeDisallowMalloc();
+ defaultServiceManager();
+}
+
+// note, ping does not include interface descriptor
+TEST(BinderAllocation, PingTransaction) {
+ sp<IBinder> a_binder = GetRemoteBinder();
+ const auto m = ScopeDisallowMalloc();
+ a_binder->pingBinder();
+}
+
+TEST(BinderAllocation, SmallTransaction) {
+ String16 empty_descriptor = String16("");
+ sp<IServiceManager> manager = defaultServiceManager();
+
+ size_t mallocs = 0;
+ const auto on_malloc = OnMalloc([&](size_t bytes) {
+ mallocs++;
+ // Parcel should allocate a small amount by default
+ EXPECT_EQ(bytes, 128);
+ });
+ manager->checkService(empty_descriptor);
+
+ EXPECT_EQ(mallocs, 1);
+}
+
+int main(int argc, char** argv) {
+ if (getenv("LIBC_HOOKS_ENABLE") == nullptr) {
+ CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/));
+ execv(argv[0], argv);
+ return 1;
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index b790997..3b1faa8 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -116,7 +116,7 @@
if (time > max_time_bucket) {
m_long_transactions++;
}
- m_buckets[min(time, max_time_bucket-1) / time_per_bucket] += 1;
+ m_buckets[min((uint32_t)(time / time_per_bucket), num_buckets - 1)] += 1;
m_best = min(time, m_best);
m_worst = max(time, m_worst);
m_transactions += 1;
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 0b77ab3..50f6289 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -88,16 +88,6 @@
return policyN1 - policyN2;
}
-static int bpf_obj_get_wronly(const char *pathname) {
- union bpf_attr attr;
-
- memset(&attr, 0, sizeof(attr));
- attr.pathname = ptr_to_u64((void *)pathname);
- attr.file_flags = BPF_F_WRONLY;
-
- return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
-}
-
static bool initGlobals() {
std::lock_guard<std::mutex> guard(gInitializedMutex);
if (gInitialized) return true;
@@ -156,7 +146,7 @@
static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) {
std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s",
eventType.c_str(), eventName.c_str());
- int prog_fd = bpfFdGet(path.c_str(), BPF_F_RDONLY);
+ int prog_fd = retrieveProgram(path.c_str());
if (prog_fd < 0) return false;
return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0;
}
@@ -183,7 +173,7 @@
if (!initGlobals()) return false;
if (gTracking) return true;
- unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
+ unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
if (cpuPolicyFd < 0) return false;
for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) {
@@ -192,7 +182,7 @@
}
}
- unique_fd freqToIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
+ unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
if (freqToIdxFd < 0) return false;
freq_idx_key_t key;
for (uint32_t i = 0; i < gNPolicies; ++i) {
@@ -207,23 +197,23 @@
}
}
- unique_fd cpuLastUpdateFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_last_update_map"));
+ unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_last_update_map"));
if (cpuLastUpdateFd < 0) return false;
std::vector<uint64_t> zeros(get_nprocs_conf(), 0);
uint32_t zero = 0;
if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false;
- unique_fd nrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_nr_active_map"));
+ unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_nr_active_map"));
if (nrActiveFd < 0) return false;
if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false;
- unique_fd policyNrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_nr_active_map"));
+ unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_nr_active_map"));
if (policyNrActiveFd < 0) return false;
for (uint32_t i = 0; i < gNPolicies; ++i) {
if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false;
}
- unique_fd policyFreqIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
+ unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
if (policyFreqIdxFd < 0) return false;
for (uint32_t i = 0; i < gNPolicies; ++i) {
auto freqIdx = getPolicyFreqIdx(i);
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index fd557b7..2e14408 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -56,6 +56,10 @@
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
+ "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
+ "android.hardware.automotive.audiocontrol@2.0::IAudioControl",
+ "android.hardware.automotive.evs@1.0::IEvsCamera",
+ "android.hardware.automotive.vehicle@2.0::IVehicle",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
"android.hardware.bluetooth@1.0::IBluetoothHci",
@@ -67,16 +71,12 @@
"android.hardware.media.c2@1.0::IComponentStore",
"android.hardware.media.omx@1.0::IOmx",
"android.hardware.media.omx@1.0::IOmxStore",
+ "android.hardware.neuralnetworks@1.0::IDevice",
"android.hardware.power@1.3::IPower",
"android.hardware.power.stats@1.0::IPowerStats",
"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.audiocontrol@2.0::IAudioControl",
- "android.hardware.automotive.vehicle@2.0::IVehicle",
- "android.hardware.automotive.evs@1.0::IEvsCamera",
- "android.hardware.neuralnetworks@1.0::IDevice",
NULL,
};
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index de32ff4..6909637 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -1,5 +1,6 @@
cc_defaults {
name: "fakeservicemanager_defaults",
+ host_supported: true,
srcs: [
"ServiceManager.cpp",
],
diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS
index 67743cd..4a95778 100644
--- a/libs/gralloc/OWNERS
+++ b/libs/gralloc/OWNERS
@@ -1,2 +1,2 @@
-marissaw@google.com
+chrisforbes@google.com
vhau@google.com
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4809c1f..119b3e0 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -29,7 +29,6 @@
#include <android-base/strings.h>
#include <android/dlext.h>
#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
#include <graphicsenv/IGpuService.h>
#include <log/log.h>
#include <nativeloader/dlext_namespaces.h>
@@ -40,6 +39,13 @@
#include <string>
#include <thread>
+// TODO(b/159240322): Extend this to x86 ABI.
+#if defined(__LP64__)
+#define UPDATABLE_DRIVER_ABI "arm64-v8a"
+#else
+#define UPDATABLE_DRIVER_ABI "armeabi-v7a"
+#endif // defined(__LP64__)
+
// TODO(ianelliott@): Get the following from an ANGLE header:
#define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting
// Version-2 API:
@@ -67,7 +73,7 @@
static std::string vndkVersionStr() {
#ifdef __BIONIC__
- return android::base::GetProperty("ro.vndk.version", "");
+ return base::GetProperty("ro.vndk.version", "");
#endif
return "";
}
@@ -338,10 +344,8 @@
}
bool GraphicsEnv::checkAngleRules(void* so) {
- char manufacturer[PROPERTY_VALUE_MAX];
- char model[PROPERTY_VALUE_MAX];
- property_get("ro.product.manufacturer", manufacturer, "UNSET");
- property_get("ro.product.model", model, "UNSET");
+ auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
+ auto model = base::GetProperty("ro.product.model", "UNSET");
auto ANGLEGetFeatureSupportUtilAPIVersion =
(fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
@@ -394,7 +398,8 @@
ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
break;
}
- if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) {
+ if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
+ systemInfoHandle)) {
ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
break;
}
@@ -584,7 +589,28 @@
}
if (mDriverPath.empty()) {
- return nullptr;
+ // For an application process, driver path is empty means this application is not opted in
+ // to use updatable driver. Application process doesn't have the ability to set up
+ // environment variables and hence before `getenv` call will return.
+ // For a process that is not an application process, if it's run from an environment,
+ // for example shell, where environment variables can be set, then it can opt into using
+ // udpatable driver by setting UPDATABLE_GFX_DRIVER to 1. By setting to 1 the developer
+ // driver will be used currently.
+ // TODO(b/159240322) Support the production updatable driver.
+ const char* id = getenv("UPDATABLE_GFX_DRIVER");
+ if (id == nullptr || std::strcmp(id, "1")) {
+ return nullptr;
+ }
+ const sp<IGpuService> gpuService = getGpuService();
+ if (!gpuService) {
+ return nullptr;
+ }
+ mDriverPath = gpuService->getUpdatableDriverPath();
+ if (mDriverPath.empty()) {
+ return nullptr;
+ }
+ mDriverPath.append(UPDATABLE_DRIVER_ABI);
+ ALOGI("Driver path is setup via UPDATABLE_GFX_DRIVER: %s", mDriverPath.c_str());
}
auto vndkNamespace = android_get_exported_namespace("vndk");
@@ -625,8 +651,7 @@
mAngleNamespace = android_create_namespace("ANGLE",
nullptr, // ld_library_path
mAnglePath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED |
- ANDROID_NAMESPACE_TYPE_ISOLATED,
+ ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED,
nullptr, // permitted_when_isolated_path
nullptr);
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index de3503b..fa25c55 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -27,11 +27,11 @@
public:
explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
- virtual void setGpuStats(const std::string& driverPackageName,
- const std::string& driverVersionName, uint64_t driverVersionCode,
- int64_t driverBuildTime, const std::string& appPackageName,
- const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
- bool isDriverLoaded, int64_t driverLoadingTime) {
+ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
+ uint64_t driverVersionCode, int64_t driverBuildTime,
+ const std::string& appPackageName, const int32_t vulkanVersion,
+ GpuStatsInfo::Driver driver, bool isDriverLoaded,
+ int64_t driverLoadingTime) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -48,8 +48,8 @@
remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
- const GpuStatsInfo::Stats stats, const uint64_t value) {
+ void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
+ const GpuStatsInfo::Stats stats, const uint64_t value) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -60,6 +60,27 @@
remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ void setUpdatableDriverPath(const std::string& driverPath) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+ data.writeUtf8AsUtf16(driverPath);
+
+ remote()->transact(BnGpuService::SET_UPDATABLE_DRIVER_PATH, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+
+ std::string getUpdatableDriverPath() override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ status_t error = remote()->transact(BnGpuService::GET_UPDATABLE_DRIVER_PATH, data, &reply);
+ std::string driverPath;
+ if (error == OK) {
+ error = reply.readUtf8FromUtf16(&driverPath);
+ }
+ return driverPath;
+ }
};
IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
@@ -126,6 +147,21 @@
return OK;
}
+ case SET_UPDATABLE_DRIVER_PATH: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string driverPath;
+ if ((status = data.readUtf8FromUtf16(&driverPath)) != OK) return status;
+
+ setUpdatableDriverPath(driverPath);
+ return OK;
+ }
+ case GET_UPDATABLE_DRIVER_PATH: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string driverPath = getUpdatableDriverPath();
+ return reply->writeUtf8AsUtf16(driverPath);
+ }
case SHELL_COMMAND_TRANSACTION: {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index c7c6d1e..2d59fa0 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -42,6 +42,10 @@
// set target stats.
virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0;
+
+ // setter and getter for updatable driver path.
+ virtual void setUpdatableDriverPath(const std::string& driverPath) = 0;
+ virtual std::string getUpdatableDriverPath() = 0;
};
class BnGpuService : public BnInterface<IGpuService> {
@@ -49,6 +53,8 @@
enum IGpuServiceTag {
SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
SET_TARGET_STATS,
+ SET_UPDATABLE_DRIVER_PATH,
+ GET_UPDATABLE_DRIVER_PATH,
// Always append new enum to the end.
};
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index e62a61f..6881be3 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -371,10 +371,8 @@
data.writeStrongBinder(display);
remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
const status_t result = reply.readInt32();
- if (result == NO_ERROR) {
- memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
- }
- return result;
+ if (result != NO_ERROR) return result;
+ return reply.read(*info);
}
virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
@@ -1093,22 +1091,22 @@
return NO_ERROR;
}
- virtual status_t notifyPowerHint(int32_t hintId) {
+ virtual status_t notifyPowerBoost(int32_t boostId) {
Parcel data, reply;
status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to write interface token: %d", error);
+ ALOGE("notifyPowerBoost: failed to write interface token: %d", error);
return error;
}
- error = data.writeInt32(hintId);
+ error = data.writeInt32(boostId);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to write hintId: %d", error);
+ ALOGE("notifyPowerBoost: failed to write boostId: %d", error);
return error;
}
- error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply,
+ error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply,
IBinder::FLAG_ONEWAY);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to transact: %d", error);
+ ALOGE("notifyPowerBoost: failed to transact: %d", error);
return error;
}
return NO_ERROR;
@@ -1442,10 +1440,8 @@
const sp<IBinder> display = data.readStrongBinder();
const status_t result = getDisplayInfo(display, &info);
reply->writeInt32(result);
- if (result == NO_ERROR) {
- memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
- }
- return NO_ERROR;
+ if (result != NO_ERROR) return result;
+ return reply->write(info);
}
case GET_DISPLAY_CONFIGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1989,15 +1985,15 @@
}
return setDisplayBrightness(displayToken, brightness);
}
- case NOTIFY_POWER_HINT: {
+ case NOTIFY_POWER_BOOST: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- int32_t hintId;
- status_t error = data.readInt32(&hintId);
+ int32_t boostId;
+ status_t error = data.readInt32(&boostId);
if (error != NO_ERROR) {
- ALOGE("notifyPowerHint: failed to read hintId: %d", error);
+ ALOGE("notifyPowerBoost: failed to read boostId: %d", error);
return error;
}
- return notifyPowerHint(hintId);
+ return notifyPowerBoost(boostId);
}
case SET_GLOBAL_SHADOW_SETTINGS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index c13401d..cbb4b97 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,12 +1,15 @@
adyabr@google.com
akrulec@google.com
alecmouri@google.com
+chaviw@google.com
+chrisforbes@google.com
jessehall@google.com
-jwcai@google.com
lpy@google.com
-marissaw@google.com
mathias@google.com
racarr@google.com
steventhomas@google.com
stoza@google.com
vhau@google.com
+vishnun@google.com
+
+per-file EndToEndNativeInputTest.cpp = svv@google.com
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2bf8ff7..a86eafa 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -110,7 +110,7 @@
mConnectedToCpu = false;
mProducerControlledByApp = controlledByApp;
mSwapIntervalZero = false;
- mMaxBufferCount = 0;
+ mMaxBufferCount = NUM_BUFFER_SLOTS;
}
Surface::~Surface() {
@@ -1493,7 +1493,7 @@
int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
if (graphicBuffer != nullptr) {
- *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+ *buffer = graphicBuffer->toAHardwareBuffer();
AHardwareBuffer_acquire(*buffer);
} else {
*buffer = nullptr;
@@ -1585,6 +1585,7 @@
mStickyTransform = 0;
mAutoPrerotation = false;
mEnableFrameTimestamps = false;
+ mMaxBufferCount = NUM_BUFFER_SLOTS;
if (api == NATIVE_WINDOW_API_CPU) {
mConnectedToCpu = false;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5922f3a..16b46df 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -353,6 +353,8 @@
mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
mEarlyWakeup(other.mEarlyWakeup),
+ mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
+ mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
mContainsBuffer(other.mContainsBuffer),
mDesiredPresentTime(other.mDesiredPresentTime) {
mDisplayStates = other.mDisplayStates;
@@ -375,6 +377,8 @@
const uint32_t transactionNestCount = parcel->readUint32();
const bool animation = parcel->readBool();
const bool earlyWakeup = parcel->readBool();
+ const bool explicitEarlyWakeupStart = parcel->readBool();
+ const bool explicitEarlyWakeupEnd = parcel->readBool();
const bool containsBuffer = parcel->readBool();
const int64_t desiredPresentTime = parcel->readInt64();
@@ -443,6 +447,8 @@
mTransactionNestCount = transactionNestCount;
mAnimation = animation;
mEarlyWakeup = earlyWakeup;
+ mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
+ mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
mContainsBuffer = containsBuffer;
mDesiredPresentTime = desiredPresentTime;
mDisplayStates = displayStates;
@@ -470,6 +476,8 @@
parcel->writeUint32(mTransactionNestCount);
parcel->writeBool(mAnimation);
parcel->writeBool(mEarlyWakeup);
+ parcel->writeBool(mExplicitEarlyWakeupStart);
+ parcel->writeBool(mExplicitEarlyWakeupEnd);
parcel->writeBool(mContainsBuffer);
parcel->writeInt64(mDesiredPresentTime);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
@@ -545,6 +553,8 @@
mContainsBuffer |= other.mContainsBuffer;
mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
+ mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
+ mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
other.clear();
return *this;
}
@@ -559,6 +569,8 @@
mTransactionNestCount = 0;
mAnimation = false;
mEarlyWakeup = false;
+ mExplicitEarlyWakeupStart = false;
+ mExplicitEarlyWakeupEnd = false;
mDesiredPresentTime = -1;
}
@@ -682,9 +694,20 @@
flags |= ISurfaceComposer::eEarlyWakeup;
}
+ // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
+ // it is equivalent for none
+ if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
+ flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
+ }
+ if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
+ flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
+ }
+
mForceSynchronous = false;
mAnimation = false;
mEarlyWakeup = false;
+ mExplicitEarlyWakeupStart = false;
+ mExplicitEarlyWakeupEnd = false;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
@@ -731,6 +754,14 @@
mEarlyWakeup = true;
}
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
+ mExplicitEarlyWakeupStart = true;
+}
+
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
+ mExplicitEarlyWakeupEnd = true;
+}
+
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
if (mComposerStates.count(handle) == 0) {
// we don't have it, add an initialized layer_state to our list
@@ -1862,8 +1893,8 @@
return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
}
-status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) {
- return ComposerService::getComposerService()->notifyPowerHint(hintId);
+status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
+ return ComposerService::getComposerService()->notifyPowerBoost(boostId);
}
status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index b49fa1b..645714a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -81,12 +81,20 @@
// flags for setTransactionState()
enum {
eSynchronous = 0x01,
- eAnimation = 0x02,
+ eAnimation = 0x02,
- // Indicates that this transaction will likely result in a lot of layers being composed, and
- // thus, SurfaceFlinger should wake-up earlier to avoid missing frame deadlines. In this
- // case SurfaceFlinger will wake up at (sf vsync offset - debug.sf.early_phase_offset_ns)
- eEarlyWakeup = 0x04
+ // DEPRECATED - use eExplicitEarlyWakeup[Start|End]
+ eEarlyWakeup = 0x04,
+
+ // Explicit indication that this transaction and others to follow will likely result in a
+ // lot of layers being composed, and thus, SurfaceFlinger should wake-up earlier to avoid
+ // missing frame deadlines. In this case SurfaceFlinger will wake up at
+ // (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
+ // in the early configuration until it receives eExplicitEarlyWakeupEnd. These flags are
+ // expected to be used by WindowManager only and are guarded by
+ // android.permission.ACCESS_SURFACE_FLINGER
+ eExplicitEarlyWakeupStart = 0x08,
+ eExplicitEarlyWakeupEnd = 0x10,
};
enum VsyncSource {
@@ -488,14 +496,14 @@
virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
/*
- * Sends a power hint to the composer. This function is asynchronous.
+ * Sends a power boost to the composer. This function is asynchronous.
*
- * hintId
- * hint id according to android::hardware::power::V1_0::PowerHint
+ * boostId
+ * boost id according to android::hardware::power::Boost
*
* Returns NO_ERROR upon success.
*/
- virtual status_t notifyPowerHint(int32_t hintId) = 0;
+ virtual status_t notifyPowerBoost(int32_t boostId) = 0;
/*
* Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -583,7 +591,7 @@
GET_DISPLAY_BRIGHTNESS_SUPPORT,
SET_DISPLAY_BRIGHTNESS,
CAPTURE_SCREEN_BY_ID,
- NOTIFY_POWER_HINT,
+ NOTIFY_POWER_BOOST,
SET_GLOBAL_SHADOW_SETTINGS,
GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
SET_AUTO_LOW_LATENCY_MODE,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index e981a39..eb757ba 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -217,14 +217,14 @@
static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
/*
- * Sends a power hint to the composer. This function is asynchronous.
+ * Sends a power boost to the composer. This function is asynchronous.
*
- * hintId
- * hint id according to android::hardware::power::V1_0::PowerHint
+ * boostId
+ * boost id according to android::hardware::power::Boost
*
* Returns NO_ERROR upon success.
*/
- static status_t notifyPowerHint(int32_t hintId);
+ static status_t notifyPowerBoost(int32_t boostId);
/*
* Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -345,10 +345,12 @@
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
mListenerCallbacks;
- uint32_t mForceSynchronous = 0;
- uint32_t mTransactionNestCount = 0;
- bool mAnimation = false;
- bool mEarlyWakeup = false;
+ uint32_t mForceSynchronous = 0;
+ uint32_t mTransactionNestCount = 0;
+ bool mAnimation = false;
+ bool mEarlyWakeup = false;
+ bool mExplicitEarlyWakeupStart = false;
+ bool mExplicitEarlyWakeupEnd = false;
// Indicates that the Transaction contains a buffer that should be cached
bool mContainsBuffer = false;
@@ -547,6 +549,8 @@
void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
void setAnimationTransaction();
void setEarlyWakeup();
+ void setExplicitEarlyWakeupStart();
+ void setExplicitEarlyWakeupEnd();
};
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 5188a09..b1d3ecb 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -104,6 +104,15 @@
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
+ static std::unique_ptr<InputSurface> makeCursorInputSurface(
+ const sp<SurfaceComposerClient> &scc, int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Cursor Surface"), 0 /* bufHeight */,
+ 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eCursorWindow);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
InputEvent* consumeEvent() {
waitForEventAvailable();
@@ -134,12 +143,14 @@
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
EXPECT_EQ(x, mev->getX(0));
EXPECT_EQ(y, mev->getY(0));
+ EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
~InputSurface() {
@@ -537,5 +548,18 @@
injectTap(0, 0);
surface->expectTap(1, 1);
}
+
+TEST_F(InputSurfacesTest, input_ignores_cursor_layer) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> cursorSurface =
+ InputSurface::makeCursorInputSurface(mComposerClient, 10, 10);
+
+ surface->showAt(10, 10);
+ surface->assertFocusChange(true);
+ cursorSurface->showAt(10, 10);
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
}
}
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index c2b4ef9..6d92143 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -30,6 +30,7 @@
#include <gui/SurfaceComposerClient.h>
#include <inttypes.h>
#include <private/gui/ComposerService.h>
+#include <ui/BufferQueueDefs.h>
#include <ui/Rect.h>
#include <utils/String8.h>
@@ -849,7 +850,7 @@
float* /*outAppRequestRefreshRateMax*/) override {
return NO_ERROR;
};
- status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+ status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; }
status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
float /*lightPosY*/, float /*lightPosZ*/,
@@ -1974,4 +1975,29 @@
ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
}
+TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+ consumer->consumerConnect(dummyConsumer, false);
+
+ sp<Surface> surface = new Surface(producer);
+ sp<ANativeWindow> window(surface);
+
+ int count = -1;
+ ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
+
+ consumer->setMaxBufferCount(10);
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU));
+ EXPECT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(10, count);
+
+ ASSERT_EQ(NO_ERROR, native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, window->query(window.get(), NATIVE_WINDOW_MAX_BUFFER_COUNT, &count));
+ EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
+}
+
} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8efaf3d..7037680 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -34,6 +34,9 @@
clang: true,
+ header_libs: ["jni_headers"],
+ export_header_lib_headers: ["jni_headers"],
+
shared_libs: [
"libbase",
"liblog",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c243767..31aa685 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -169,6 +169,18 @@
mEventTime = from.mEventTime;
}
+const char* KeyEvent::actionToString(int32_t action) {
+ // Convert KeyEvent action to string
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AKEY_EVENT_ACTION_UP:
+ return "UP";
+ case AKEY_EVENT_ACTION_MULTIPLE:
+ return "MULTIPLE";
+ }
+ return "UNKNOWN";
+}
// --- PointerCoords ---
@@ -678,6 +690,25 @@
return getAxisByLabel(label);
}
+const char* MotionEvent::actionToString(int32_t action) {
+ // Convert MotionEvent action to string
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ return "DOWN";
+ case AMOTION_EVENT_ACTION_MOVE:
+ return "MOVE";
+ case AMOTION_EVENT_ACTION_UP:
+ return "UP";
+ case AMOTION_EVENT_ACTION_CANCEL:
+ return "CANCEL";
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ return "POINTER_DOWN";
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ return "POINTER_UP";
+ }
+ return "UNKNOWN";
+}
+
// --- FocusEvent ---
void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) {
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 85a2015..0455022 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -17,9 +17,10 @@
#define LOG_TAG "InputWindow"
#define LOG_NDEBUG 0
+#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
-#include <input/InputWindow.h>
#include <input/InputTransport.h>
+#include <input/InputWindow.h>
#include <log/log.h>
@@ -28,6 +29,118 @@
namespace android {
+const char* inputWindowFlagToString(uint32_t flag) {
+ switch (flag) {
+ case InputWindowInfo::FLAG_ALLOW_LOCK_WHILE_SCREEN_ON: {
+ return "ALLOW_LOCK_WHILE_SCREEN_ON";
+ }
+ case InputWindowInfo::FLAG_DIM_BEHIND: {
+ return "DIM_BEHIND";
+ }
+ case InputWindowInfo::FLAG_BLUR_BEHIND: {
+ return "BLUR_BEHIND";
+ }
+ case InputWindowInfo::FLAG_NOT_FOCUSABLE: {
+ return "NOT_FOCUSABLE";
+ }
+ case InputWindowInfo::FLAG_NOT_TOUCHABLE: {
+ return "NOT_TOUCHABLE";
+ }
+ case InputWindowInfo::FLAG_NOT_TOUCH_MODAL: {
+ return "NOT_TOUCH_MODAL";
+ }
+ case InputWindowInfo::FLAG_TOUCHABLE_WHEN_WAKING: {
+ return "TOUCHABLE_WHEN_WAKING";
+ }
+ case InputWindowInfo::FLAG_KEEP_SCREEN_ON: {
+ return "KEEP_SCREEN_ON";
+ }
+ case InputWindowInfo::FLAG_LAYOUT_IN_SCREEN: {
+ return "LAYOUT_IN_SCREEN";
+ }
+ case InputWindowInfo::FLAG_LAYOUT_NO_LIMITS: {
+ return "LAYOUT_NO_LIMITS";
+ }
+ case InputWindowInfo::FLAG_FULLSCREEN: {
+ return "FULLSCREEN";
+ }
+ case InputWindowInfo::FLAG_FORCE_NOT_FULLSCREEN: {
+ return "FORCE_NOT_FULLSCREEN";
+ }
+ case InputWindowInfo::FLAG_DITHER: {
+ return "DITHER";
+ }
+ case InputWindowInfo::FLAG_SECURE: {
+ return "SECURE";
+ }
+ case InputWindowInfo::FLAG_SCALED: {
+ return "SCALED";
+ }
+ case InputWindowInfo::FLAG_IGNORE_CHEEK_PRESSES: {
+ return "IGNORE_CHEEK_PRESSES";
+ }
+ case InputWindowInfo::FLAG_LAYOUT_INSET_DECOR: {
+ return "LAYOUT_INSET_DECOR";
+ }
+ case InputWindowInfo::FLAG_ALT_FOCUSABLE_IM: {
+ return "ALT_FOCUSABLE_IM";
+ }
+ case InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH: {
+ return "WATCH_OUTSIDE_TOUCH";
+ }
+ case InputWindowInfo::FLAG_SHOW_WHEN_LOCKED: {
+ return "SHOW_WHEN_LOCKED";
+ }
+ case InputWindowInfo::FLAG_SHOW_WALLPAPER: {
+ return "SHOW_WALLPAPER";
+ }
+ case InputWindowInfo::FLAG_TURN_SCREEN_ON: {
+ return "TURN_SCREEN_ON";
+ }
+ case InputWindowInfo::FLAG_DISMISS_KEYGUARD: {
+ return "DISMISS_KEYGUARD";
+ }
+ case InputWindowInfo::FLAG_SPLIT_TOUCH: {
+ return "SPLIT_TOUCH";
+ }
+ case InputWindowInfo::FLAG_HARDWARE_ACCELERATED: {
+ return "HARDWARE_ACCELERATED";
+ }
+ case InputWindowInfo::FLAG_LAYOUT_IN_OVERSCAN: {
+ return "LAYOUT_IN_OVERSCAN";
+ }
+ case InputWindowInfo::FLAG_TRANSLUCENT_STATUS: {
+ return "TRANSLUCENT_STATUS";
+ }
+ case InputWindowInfo::FLAG_TRANSLUCENT_NAVIGATION: {
+ return "TRANSLUCENT_NAVIGATION";
+ }
+ case InputWindowInfo::FLAG_LOCAL_FOCUS_MODE: {
+ return "LOCAL_FOCUS_MODE";
+ }
+ case InputWindowInfo::FLAG_SLIPPERY: {
+ return "SLIPPERY";
+ }
+ case InputWindowInfo::FLAG_LAYOUT_ATTACHED_IN_DECOR: {
+ return "LAYOUT_ATTACHED_IN_DECOR";
+ }
+ case InputWindowInfo::FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS: {
+ return "DRAWS_SYSTEM_BAR_BACKGROUNDS";
+ }
+ }
+ return "UNKNOWN";
+}
+
+std::string inputWindowFlagsToString(uint32_t flags) {
+ std::string result;
+ for (BitSet32 bits(flags); !bits.isEmpty();) {
+ uint32_t bit = bits.clearLastMarkedBit(); // counts from left
+ const uint32_t flag = 1 << (32 - bit - 1);
+ result += android::base::StringPrintf("%s | ", inputWindowFlagToString(flag));
+ }
+ return result;
+}
+
// --- InputWindowInfo ---
void InputWindowInfo::addTouchableRegion(const Rect& region) {
touchableRegion.orSelf(region);
@@ -42,20 +155,6 @@
&& y >= frameTop && y < frameBottom;
}
-// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready.
-bool InputWindowInfo::isTrustedOverlay() const {
- return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG ||
- layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR ||
- layoutParamsType == TYPE_NOTIFICATION_SHADE ||
- layoutParamsType == TYPE_NAVIGATION_BAR ||
- layoutParamsType == TYPE_NAVIGATION_BAR_PANEL ||
- layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY ||
- layoutParamsType == TYPE_DOCK_DIVIDER ||
- layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY ||
- layoutParamsType == TYPE_INPUT_CONSUMER ||
- layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY;
-}
-
bool InputWindowInfo::supportsSplitTouch() const {
return layoutParamsFlags & FLAG_SPLIT_TOUCH;
}
@@ -92,6 +191,7 @@
output.writeBool(hasFocus);
output.writeBool(hasWallpaper);
output.writeBool(paused);
+ output.writeBool(trustedOverlay);
output.writeInt32(ownerPid);
output.writeInt32(ownerUid);
output.writeInt32(inputFeatures);
@@ -130,6 +230,7 @@
ret.hasFocus = from.readBool();
ret.hasWallpaper = from.readBool();
ret.paused = from.readBool();
+ ret.trustedOverlay = from.readBool();
ret.ownerPid = from.readInt32();
ret.ownerUid = from.readInt32();
ret.inputFeatures = from.readInt32();
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index c6cc4fc..7c28ac5 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -104,107 +104,73 @@
// --- VelocityTracker ---
-// The default velocity tracker strategy.
-// Although other strategies are available for testing and comparison purposes,
-// this is the strategy that applications will actually use. Be very careful
-// when adjusting the default strategy because it can dramatically affect
-// (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
-
-VelocityTracker::VelocityTracker(const char* strategy) :
- mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
- char value[PROPERTY_VALUE_MAX];
-
- // Allow the default strategy to be overridden using a system property for debugging.
- if (!strategy) {
- int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
- if (length > 0) {
- strategy = value;
- } else {
- strategy = DEFAULT_STRATEGY;
- }
- }
-
+VelocityTracker::VelocityTracker(const Strategy strategy)
+ : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
// Configure the strategy.
if (!configureStrategy(strategy)) {
- ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
- if (!configureStrategy(DEFAULT_STRATEGY)) {
- LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
- strategy);
+ ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
+ if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
+ LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
+ "'!",
+ strategy);
}
}
}
VelocityTracker::~VelocityTracker() {
- delete mStrategy;
}
-bool VelocityTracker::configureStrategy(const char* strategy) {
- mStrategy = createStrategy(strategy);
+bool VelocityTracker::configureStrategy(Strategy strategy) {
+ if (strategy == VelocityTracker::Strategy::DEFAULT) {
+ mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+ } else {
+ mStrategy = createStrategy(strategy);
+ }
return mStrategy != nullptr;
}
-VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
- if (!strcmp("impulse", strategy)) {
- // Physical model of pushing an object. Quality: VERY GOOD.
- // Works with duplicate coordinates, unclean finger liftoff.
- return new ImpulseVelocityTrackerStrategy();
- }
- if (!strcmp("lsq1", strategy)) {
- // 1st order least squares. Quality: POOR.
- // Frequently underfits the touch data especially when the finger accelerates
- // or changes direction. Often underestimates velocity. The direction
- // is overly influenced by historical touch points.
- return new LeastSquaresVelocityTrackerStrategy(1);
- }
- if (!strcmp("lsq2", strategy)) {
- // 2nd order least squares. Quality: VERY GOOD.
- // Pretty much ideal, but can be confused by certain kinds of touch data,
- // particularly if the panel has a tendency to generate delayed,
- // duplicate or jittery touch coordinates when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(2);
- }
- if (!strcmp("lsq3", strategy)) {
- // 3rd order least squares. Quality: UNUSABLE.
- // Frequently overfits the touch data yielding wildly divergent estimates
- // of the velocity when the finger is released.
- return new LeastSquaresVelocityTrackerStrategy(3);
- }
- if (!strcmp("wlsq2-delta", strategy)) {
- // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
- }
- if (!strcmp("wlsq2-central", strategy)) {
- // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
- }
- if (!strcmp("wlsq2-recent", strategy)) {
- // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
- return new LeastSquaresVelocityTrackerStrategy(2,
- LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
- }
- if (!strcmp("int1", strategy)) {
- // 1st order integrating filter. Quality: GOOD.
- // Not as good as 'lsq2' because it cannot estimate acceleration but it is
- // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
- // the velocity of a fling but this strategy tends to respond to changes in
- // direction more quickly and accurately.
- return new IntegratingVelocityTrackerStrategy(1);
- }
- if (!strcmp("int2", strategy)) {
- // 2nd order integrating filter. Quality: EXPERIMENTAL.
- // For comparison purposes only. Unlike 'int1' this strategy can compensate
- // for acceleration but it typically overestimates the effect.
- return new IntegratingVelocityTrackerStrategy(2);
- }
- if (!strcmp("legacy", strategy)) {
- // Legacy velocity tracker algorithm. Quality: POOR.
- // For comparison purposes only. This algorithm is strongly influenced by
- // old data points, consistently underestimates velocity and takes a very long
- // time to adjust to changes in direction.
- return new LegacyVelocityTrackerStrategy();
+std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
+ VelocityTracker::Strategy strategy) {
+ switch (strategy) {
+ case VelocityTracker::Strategy::IMPULSE:
+ return std::make_unique<ImpulseVelocityTrackerStrategy>();
+
+ case VelocityTracker::Strategy::LSQ1:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
+
+ case VelocityTracker::Strategy::LSQ2:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
+
+ case VelocityTracker::Strategy::LSQ3:
+ return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3);
+
+ case VelocityTracker::Strategy::WLSQ2_DELTA:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_DELTA);
+ case VelocityTracker::Strategy::WLSQ2_CENTRAL:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_CENTRAL);
+ case VelocityTracker::Strategy::WLSQ2_RECENT:
+ return std::make_unique<
+ LeastSquaresVelocityTrackerStrategy>(2,
+ LeastSquaresVelocityTrackerStrategy::
+ WEIGHTING_RECENT);
+
+ case VelocityTracker::Strategy::INT1:
+ return std::make_unique<IntegratingVelocityTrackerStrategy>(1);
+
+ case VelocityTracker::Strategy::INT2:
+ return std::make_unique<IntegratingVelocityTrackerStrategy>(2);
+
+ case VelocityTracker::Strategy::LEGACY:
+ return std::make_unique<LegacyVelocityTrackerStrategy>();
+
+ default:
+ break;
}
return nullptr;
}
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index bf452c0..249d9d4 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -191,8 +191,9 @@
return events;
}
-static void computeAndCheckVelocity(const char* strategy,
- const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) {
+static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
+ const std::vector<MotionEventEntry>& motions, int32_t axis,
+ float targetVelocity) {
VelocityTracker vt(strategy);
float Vx, Vy;
@@ -217,7 +218,7 @@
static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
const std::array<float, 3>& coefficients) {
- VelocityTracker vt("lsq2");
+ VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
std::vector<MotionEvent> events = createMotionEventStream(motions);
for (MotionEvent event : events) {
vt.addMovement(&event);
@@ -243,7 +244,8 @@
{14730us, {{293, NAN}}},
{14730us, {{293, NAN}}}, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1600);
}
TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
@@ -254,8 +256,8 @@
{ 11283us, {{293, NAN}} },
{ 11283us, {{293, NAN}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
}
TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
@@ -266,8 +268,8 @@
{ 20ms, {{10, NAN}} },
{ 20ms, {{10, NAN}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
}
@@ -297,8 +299,10 @@
{ 96948871ns, {{274.79245, 428.113159}} },
{ 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 623.577637);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 5970.7309);
}
// --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -339,10 +343,14 @@
{ 235089162955851ns, {{560.66, 843.82}} },
{ 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181);
- computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 872.794617);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 951.698181);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -3604.819336);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3044.966064);
}
@@ -368,8 +376,10 @@
{ 235110660368000ns, {{530.00, 980.00}} },
{ 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -4096.583008);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3455.094238);
}
@@ -396,10 +406,14 @@
{ 792629200000ns, {{619.00, 1115.00}} },
{ 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 574.33429);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 617.40564);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -2361.982666);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -2500.055664);
}
@@ -426,10 +440,14 @@
{ 235160520366000ns, {{679.00, 814.00}} },
{ 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 1274.141724);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 1438.53186);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -3001.4348);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -3695.859619);
}
@@ -452,8 +470,10 @@
{ 847237986000ns, {{610.00, 1095.00}} },
{ 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -4280.07959);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -4241.004395);
}
@@ -476,8 +496,10 @@
{ 235200616933000ns, {{590.00, 844.00}} },
{ 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -8715.686523);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -7639.026367);
}
@@ -499,10 +521,14 @@
{ 920989261000ns, {{715.00, 903.00}} },
{ 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ 5670.329102);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ 5991.866699);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -13021.101562);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -15093.995117);
}
@@ -522,8 +548,10 @@
{ 235247220736000ns, {{620.00, 641.00}} },
{ 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -20286.958984);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -20494.587891);
}
@@ -541,8 +569,10 @@
{ 235302613019881ns, {{679.26, 526.73}} },
{ 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ -39295.941406);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ -36461.421875);
}
@@ -569,10 +599,14 @@
{ 235655842893000ns, {{563.00, 649.00}} },
{ 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -419.749695);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -398.303894);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 3309.016357);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 3969.099854);
}
@@ -599,10 +633,14 @@
{ 235671246532000ns, {{470.00, 799.00}} },
{ 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -262.80426);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -243.665344);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4215.682129);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4587.986816);
}
@@ -622,10 +660,14 @@
{ 171051052000ns, {{536.00, 586.00}} },
{ 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -723.413513);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -651.038452);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 2091.502441);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 1934.517456);
}
@@ -652,8 +694,10 @@
{ 235695373403000ns, {{564.00, 744.00}} },
{ 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4254.639648);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4698.415039);
}
@@ -677,10 +721,14 @@
{ 235709710626776ns, {{511.72, 741.85}} },
{ 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -430.440247);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -447.600311);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 3953.859375);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4316.155273);
}
@@ -706,8 +754,10 @@
{ 235727721580000ns, {{516.00, 658.00}} },
{ 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 4484.617676);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 4927.92627);
}
@@ -725,8 +775,10 @@
{ 235762396429369ns, {{404.37, 680.67}} },
{ 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 14227.0224);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 16064.685547);
}
@@ -744,8 +796,10 @@
{ 235772537635000ns, {{484.00, 589.00}} },
{ 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 18660.048828);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 16918.439453);
}
@@ -764,10 +818,14 @@
{ 507703352649ns, {{443.71, 857.77}} },
{ 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP
};
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ -4111.8173);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ -6388.48877);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ 29765.908203);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ 28354.796875);
}
/**
@@ -789,10 +847,10 @@
// Velocity should actually be zero, but we expect 0.016 here instead.
// This is close enough to zero, and is likely caused by division by a very small number.
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016);
- computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
}
/**
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
index 3b1edcb..22d4712 100644
--- a/libs/math/Android.bp
+++ b/libs/math/Android.bp
@@ -25,6 +25,11 @@
min_sdk_version: "29",
export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ }
+ }
}
subdirs = ["tests"]
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 7682973..617a0ab 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -82,6 +82,7 @@
};
public:
+ CONSTEXPR half() noexcept { }
CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { }
CONSTEXPR operator float() const noexcept { return htof(mBits); }
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
index 496a7ef..604072e 100644
--- a/libs/math/tests/half_test.cpp
+++ b/libs/math/tests/half_test.cpp
@@ -35,6 +35,7 @@
EXPECT_EQ(2UL, sizeof(half));
// test +/- zero
+ EXPECT_EQ(0x0000, half().getBits());
EXPECT_EQ(0x0000, half( 0.0f).getBits());
EXPECT_EQ(0x8000, half(-0.0f).getBits());
diff --git a/libs/nativebase/Android.bp b/libs/nativebase/Android.bp
index 9e7e4a2..8399e8c 100644
--- a/libs/nativebase/Android.bp
+++ b/libs/nativebase/Android.bp
@@ -16,6 +16,8 @@
name: "libnativebase_headers",
vendor_available: true,
host_supported: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index ee006aa..52d73e0 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -25,6 +25,8 @@
name: "libnativewindow_headers",
export_include_dirs: ["include"],
vendor_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
min_sdk_version: "29",
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb6080f..3dcb498 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -64,6 +64,13 @@
],
}
+filegroup {
+ name: "librenderengine_threaded_sources",
+ srcs: [
+ "threaded/RenderEngineThreaded.cpp",
+ ],
+}
+
cc_library_static {
name: "librenderengine",
defaults: ["librenderengine_defaults"],
@@ -80,6 +87,7 @@
srcs: [
":librenderengine_sources",
":librenderengine_gl_sources",
+ ":librenderengine_threaded_sources",
],
lto: {
thin: true,
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 0fdf093..c3fbb60 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -20,19 +20,34 @@
#include <log/log.h>
#include <private/gui/SyncFeatures.h>
#include "gl/GLESRenderEngine.h"
+#include "threaded/RenderEngineThreaded.h"
namespace android {
namespace renderengine {
-std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+ RenderEngineType renderEngineType = args.renderEngineType;
+
+ // Keep the ability to override by PROPERTIES:
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
if (strcmp(prop, "gles") == 0) {
- ALOGD("RenderEngine GLES Backend");
- return renderengine::gl::GLESRenderEngine::create(args);
+ renderEngineType = RenderEngineType::GLES;
}
- ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
- return renderengine::gl::GLESRenderEngine::create(args);
+ if (strcmp(prop, "threaded") == 0) {
+ renderEngineType = RenderEngineType::THREADED;
+ }
+
+ switch (renderEngineType) {
+ case RenderEngineType::THREADED:
+ ALOGD("Threaded RenderEngine with GLES Backend");
+ return renderengine::threaded::RenderEngineThreaded::create(
+ [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
+ case RenderEngineType::GLES:
+ default:
+ ALOGD("RenderEngine with GLES Backend");
+ return renderengine::gl::GLESRenderEngine::create(args);
+ }
}
RenderEngine::~RenderEngine() = default;
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 92e7e71..d102696 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -982,7 +982,7 @@
status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* const buffer,
+ const sp<GraphicBuffer>& buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
ATRACE_CALL();
@@ -1019,7 +1019,9 @@
const auto blurLayersSize = blurLayers.size();
if (blurLayersSize == 0) {
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()->getNativeBuffer(),
+ useFramebufferCache);
if (fbo->getStatus() != NO_ERROR) {
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
buffer->handle);
@@ -1076,7 +1078,9 @@
if (blurLayers.size() == 0) {
// Done blurring, time to bind the native FBO and render our blur onto it.
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
+ buffer.get()
+ ->getNativeBuffer(),
useFramebufferCache);
status = fbo->getStatus();
setViewportAndProjection(display.physicalDisplay, display.clip);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 42b8537..9ab5ee6 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -73,7 +73,7 @@
bool useProtectedContext(bool useProtectedContext) override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
bool cleanupPostRender() override;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index e06e128..b137023 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -50,6 +50,10 @@
class Texture;
struct RenderEngineCreationArgs;
+namespace threaded {
+class RenderEngineThreaded;
+}
+
namespace impl {
class RenderEngine;
}
@@ -67,7 +71,12 @@
HIGH = 3,
};
- static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args);
+ enum class RenderEngineType {
+ GLES = 1,
+ THREADED = 2,
+ };
+
+ static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
virtual ~RenderEngine() = 0;
@@ -163,7 +172,7 @@
// now, this always returns NO_ERROR.
virtual status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
- ANativeWindowBuffer* buffer, const bool useFramebufferCache,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
protected:
@@ -174,6 +183,7 @@
// live longer than RenderEngine.
virtual Framebuffer* getFramebufferForDrawing() = 0;
friend class BindNativeBufferAsFramebuffer;
+ friend class threaded::RenderEngineThreaded;
};
struct RenderEngineCreationArgs {
@@ -184,26 +194,25 @@
bool precacheToneMapperShaderOnly;
bool supportsBackgroundBlur;
RenderEngine::ContextPriority contextPriority;
+ RenderEngine::RenderEngineType renderEngineType;
struct Builder;
private:
// must be created by Builder via constructor with full argument list
- RenderEngineCreationArgs(
- int _pixelFormat,
- uint32_t _imageCacheSize,
- bool _useColorManagement,
- bool _enableProtectedContext,
- bool _precacheToneMapperShaderOnly,
- bool _supportsBackgroundBlur,
- RenderEngine::ContextPriority _contextPriority)
- : pixelFormat(_pixelFormat)
- , imageCacheSize(_imageCacheSize)
- , useColorManagement(_useColorManagement)
- , enableProtectedContext(_enableProtectedContext)
- , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
- , supportsBackgroundBlur(_supportsBackgroundBlur)
- , contextPriority(_contextPriority) {}
+ RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+ bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
+ bool _supportsBackgroundBlur,
+ RenderEngine::ContextPriority _contextPriority,
+ RenderEngine::RenderEngineType _renderEngineType)
+ : pixelFormat(_pixelFormat),
+ imageCacheSize(_imageCacheSize),
+ useColorManagement(_useColorManagement),
+ enableProtectedContext(_enableProtectedContext),
+ precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
+ supportsBackgroundBlur(_supportsBackgroundBlur),
+ contextPriority(_contextPriority),
+ renderEngineType(_renderEngineType) {}
RenderEngineCreationArgs() = delete;
};
@@ -238,10 +247,14 @@
this->contextPriority = contextPriority;
return *this;
}
+ Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) {
+ this->renderEngineType = renderEngineType;
+ return *this;
+ }
RenderEngineCreationArgs build() const {
return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
enableProtectedContext, precacheToneMapperShaderOnly,
- supportsBackgroundBlur, contextPriority);
+ supportsBackgroundBlur, contextPriority, renderEngineType);
}
private:
@@ -253,6 +266,7 @@
bool precacheToneMapperShaderOnly = false;
bool supportsBackgroundBlur = false;
RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
+ RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
};
class BindNativeBufferAsFramebuffer {
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index df0f17a..d0343ba 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -59,7 +59,8 @@
MOCK_METHOD0(cleanupPostRender, bool());
MOCK_METHOD6(drawLayers,
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*));
};
} // namespace mock
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index e98babc..bcf389b 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -18,10 +18,12 @@
test_suites: ["device-tests"],
srcs: [
"RenderEngineTest.cpp",
+ "RenderEngineThreadedTest.cpp",
],
static_libs: [
"libgmock",
"librenderengine",
+ "librenderengine_mocks",
],
shared_libs: [
"libbase",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 16a8a0d..77b6c0f 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -22,12 +22,13 @@
#include <condition_variable>
#include <fstream>
-#include <gtest/gtest.h>
#include <cutils/properties.h>
+#include <gtest/gtest.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
#include "../gl/GLESRenderEngine.h"
+#include "../threaded/RenderEngineThreaded.h"
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -40,14 +41,15 @@
static void SetUpTestSuite() {
sRE = renderengine::gl::GLESRenderEngine::create(
renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
- .setImageCacheSize(1)
- .setUseColorManagerment(false)
- .setEnableProtectedContext(false)
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
- .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build());
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+ .build());
}
static void TearDownTestSuite() {
@@ -252,8 +254,8 @@
std::vector<const renderengine::LayerSettings*> layers,
sp<GraphicBuffer> buffer) {
base::unique_fd fence;
- status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
- base::unique_fd(), &fence);
+ status_t status =
+ sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
sCurrentBuffer = buffer;
int fd = fence.release();
@@ -1004,8 +1006,7 @@
layer.alpha = 1.0;
layers.push_back(&layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true,
- base::unique_fd(), nullptr);
+ status_t status = sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
sCurrentBuffer = mBuffer;
ASSERT_EQ(NO_ERROR, status);
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
@@ -1023,8 +1024,7 @@
layer.alpha = 1.0;
layers.push_back(&layer);
- status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false,
- base::unique_fd(), nullptr);
+ status_t status = sRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
sCurrentBuffer = mBuffer;
ASSERT_EQ(NO_ERROR, status);
ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
@@ -1414,11 +1414,9 @@
layers.push_back(&layer);
base::unique_fd fenceOne;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(),
- &fenceOne);
+ sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
base::unique_fd fenceTwo;
- sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne),
- &fenceTwo);
+ sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
const int fd = fenceTwo.get();
if (fd >= 0) {
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
new file mode 100644
index 0000000..69a0e19
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2020 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 <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include "../threaded/RenderEngineThreaded.h"
+
+namespace android {
+
+using testing::_;
+using testing::Eq;
+using testing::Mock;
+using testing::Return;
+
+struct RenderEngineThreadedTest : public ::testing::Test {
+ ~RenderEngineThreadedTest() {}
+
+ void SetUp() override {
+ mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
+ [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); });
+ }
+
+ std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+};
+
+TEST_F(RenderEngineThreadedTest, dump) {
+ std::string testString = "XYZ";
+ EXPECT_CALL(*mRenderEngine, dump(_));
+ mThreadedRE->dump(testString);
+}
+
+TEST_F(RenderEngineThreadedTest, primeCache) {
+ EXPECT_CALL(*mRenderEngine, primeCache());
+ mThreadedRE->primeCache();
+}
+
+TEST_F(RenderEngineThreadedTest, genTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
+ mThreadedRE->genTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, deleteTextures) {
+ uint32_t texName;
+ EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
+ mThreadedRE->deleteTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, bindExternalBuffer_nullptrBuffer) {
+ EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, Eq(nullptr), Eq(nullptr)))
+ .WillOnce(Return(BAD_VALUE));
+ status_t result = mThreadedRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
+ .WillOnce(Return(NO_ERROR));
+ status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) {
+ EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr)));
+ mThreadedRE->cacheExternalTextureBuffer(nullptr);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
+ mThreadedRE->cacheExternalTextureBuffer(buf);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) {
+ EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0));
+ mThreadedRE->unbindExternalTextureBuffer(0x0);
+}
+
+TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsBadValue) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(BAD_VALUE));
+ status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsNoError) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(NO_ERROR));
+ status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindFrameBuffer) {
+ std::unique_ptr<renderengine::Framebuffer> framebuffer;
+ EXPECT_CALL(*mRenderEngine, unbindFrameBuffer(framebuffer.get()));
+ mThreadedRE->unbindFrameBuffer(framebuffer.get());
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
+ size_t size = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) {
+ size_t size = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+ size_t result = mThreadedRE->getMaxTextureSize();
+ ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) {
+ size_t dims = 20;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) {
+ size_t dims = 0;
+ EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+ size_t result = mThreadedRE->getMaxViewportDims();
+ ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+ status_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true));
+ size_t result = mThreadedRE->isProtected();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
+ status_t result = mThreadedRE->supportsProtectedContent();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
+ status_t result = mThreadedRE->useProtectedContext(false);
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
+ EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(false));
+ status_t result = mThreadedRE->cleanupPostRender();
+ ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
+ EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(true));
+ status_t result = mThreadedRE->cleanupPostRender();
+ ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, drawLayers) {
+ renderengine::DisplaySettings settings;
+ std::vector<const renderengine::LayerSettings*> layers;
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ base::unique_fd bufferFence;
+ base::unique_fd drawFence;
+
+ EXPECT_CALL(*mRenderEngine, drawLayers)
+ .WillOnce([](const renderengine::DisplaySettings&,
+ const std::vector<const renderengine::LayerSettings*>&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t { return NO_ERROR; });
+
+ status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
+ std::move(bufferFence), &drawFence);
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
new file mode 100644
index 0000000..ad61718
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2020 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
+
+#include "RenderEngineThreaded.h"
+
+#include <sched.h>
+#include <chrono>
+#include <future>
+
+#include <android-base/stringprintf.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Trace.h>
+
+#include "gl/GLESRenderEngine.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
+ return std::make_unique<RenderEngineThreaded>(std::move(factory));
+}
+
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) {
+ ATRACE_CALL();
+
+ std::lock_guard lockThread(mThreadMutex);
+ mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
+}
+
+RenderEngineThreaded::~RenderEngineThreaded() {
+ {
+ std::lock_guard lock(mThreadMutex);
+ mRunning = false;
+ mCondition.notify_one();
+ }
+
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
+void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
+ ALOGE("Couldn't set SCHED_FIFO");
+ }
+
+ mRenderEngine = factory();
+
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ pthread_setname_np(pthread_self(), mThreadName);
+
+ while (mRunning) {
+ if (!mFunctionCalls.empty()) {
+ auto task = mFunctionCalls.front();
+ mFunctionCalls.pop();
+ task(*mRenderEngine);
+ }
+ mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
+ return !mRunning || !mFunctionCalls.empty();
+ });
+ }
+}
+
+void RenderEngineThreaded::primeCache() const {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::primeCache");
+ instance.primeCache();
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::dump(std::string& result) {
+ std::promise<std::string> resultPromise;
+ std::future<std::string> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::dump");
+ std::string localResult = result;
+ instance.dump(localResult);
+ resultPromise.set_value(std::move(localResult));
+ });
+ }
+ mCondition.notify_one();
+ // Note: This is an rvalue.
+ result.assign(resultFuture.get());
+}
+
+bool RenderEngineThreaded::useNativeFenceSync() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
+ ATRACE_NAME("REThreaded::useNativeFenceSync");
+ bool returnValue = SyncFeatures::getInstance().useNativeFenceSync();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useWaitSync() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
+ ATRACE_NAME("REThreaded::useWaitSync");
+ bool returnValue = SyncFeatures::getInstance().useWaitSync();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::genTextures");
+ instance.genTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::deleteTextures");
+ instance.deleteTextures(count, names);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::bindExternalTextureImage(uint32_t texName, const Image& image) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, texName, &image](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindExternalTextureImage");
+ instance.bindExternalTextureImage(texName, image);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+status_t RenderEngineThreaded::bindExternalTextureBuffer(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, texName, &buffer, &fence](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindExternalTextureBuffer");
+ status_t status = instance.bindExternalTextureBuffer(texName, buffer, fence);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
+ instance.cacheExternalTextureBuffer(buffer);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
+ instance.unbindExternalTextureBuffer(bufferId);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+status_t RenderEngineThreaded::bindFrameBuffer(Framebuffer* framebuffer) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::bindFrameBuffer");
+ status_t status = instance.bindFrameBuffer(framebuffer);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+void RenderEngineThreaded::unbindFrameBuffer(Framebuffer* framebuffer) {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::unbindFrameBuffer");
+ instance.unbindFrameBuffer(framebuffer);
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
+size_t RenderEngineThreaded::getMaxTextureSize() const {
+ std::promise<size_t> resultPromise;
+ std::future<size_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getMaxTextureSize");
+ size_t size = instance.getMaxTextureSize();
+ resultPromise.set_value(size);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+size_t RenderEngineThreaded::getMaxViewportDims() const {
+ std::promise<size_t> resultPromise;
+ std::future<size_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getMaxViewportDims");
+ size_t size = instance.getMaxViewportDims();
+ resultPromise.set_value(size);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::isProtected() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::isProtected");
+ bool returnValue = instance.isProtected();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::supportsProtectedContent() const {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::supportsProtectedContent");
+ bool returnValue = instance.supportsProtectedContent();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push(
+ [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::useProtectedContext");
+ bool returnValue = instance.useProtectedContext(useProtectedContext);
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+Framebuffer* RenderEngineThreaded::getFramebufferForDrawing() {
+ std::promise<Framebuffer*> resultPromise;
+ std::future<Framebuffer*> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getFramebufferForDrawing");
+ Framebuffer* framebuffer = instance.getFramebufferForDrawing();
+ resultPromise.set_value(framebuffer);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+bool RenderEngineThreaded::cleanupPostRender() {
+ std::promise<bool> resultPromise;
+ std::future<bool> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cleanupPostRender");
+ bool returnValue = instance.cleanupPostRender();
+ resultPromise.set_value(returnValue);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer,
+ const bool useFramebufferCache,
+ base::unique_fd&& bufferFence,
+ base::unique_fd* drawFence) {
+ std::promise<status_t> resultPromise;
+ std::future<status_t> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
+ &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::drawLayers");
+ status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
+ std::move(bufferFence), drawFence);
+ resultPromise.set_value(status);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
new file mode 100644
index 0000000..ec18e1f
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 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 <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "renderengine/RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>;
+
+/**
+ * This class extends a basic RenderEngine class. It contains a thread. Each time a function of
+ * this class is called, we create a lambda function that is put on a queue. The main thread then
+ * executes the functions in order.
+ */
+class RenderEngineThreaded : public RenderEngine {
+public:
+ static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory);
+
+ RenderEngineThreaded(CreateInstanceFactory factory);
+ ~RenderEngineThreaded() override;
+ void primeCache() const override;
+
+ void dump(std::string& result) override;
+
+ bool useNativeFenceSync() const override;
+ bool useWaitSync() const override;
+ void genTextures(size_t count, uint32_t* names) override;
+ void deleteTextures(size_t count, uint32_t const* names) override;
+ void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+ status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) override;
+ void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ void unbindExternalTextureBuffer(uint64_t bufferId) override;
+ status_t bindFrameBuffer(Framebuffer* framebuffer) override;
+ void unbindFrameBuffer(Framebuffer* framebuffer) override;
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+
+ bool isProtected() const override;
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
+ bool cleanupPostRender() override;
+
+ status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+
+protected:
+ Framebuffer* getFramebufferForDrawing() override;
+
+private:
+ void threadMain(CreateInstanceFactory factory);
+
+ /* ------------------------------------------------------------------------
+ * Threading
+ */
+ const char* const mThreadName = "RenderEngineThread";
+ // Protects the creation and destruction of mThread.
+ mutable std::mutex mThreadMutex;
+ std::thread mThread GUARDED_BY(mThreadMutex);
+ bool mRunning GUARDED_BY(mThreadMutex) = true;
+ mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
+ GUARDED_BY(mThreadMutex);
+ mutable std::condition_variable mCondition;
+
+ /* ------------------------------------------------------------------------
+ * Render Engine
+ */
+ std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 8ed09f8..a6b0aaf 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -216,14 +216,25 @@
int32_t type;
Vector<float> floats;
Vector<int32_t> ints;
+ uint32_t count;
handle = data.readInt32();
type = data.readInt32();
- floats.resize(data.readUint32());
+
+ count = data.readUint32();
+ if (count > (data.dataAvail() / sizeof(float))) {
+ return BAD_VALUE;
+ }
+ floats.resize(count);
for (auto &i : floats) {
i = data.readFloat();
}
- ints.resize(data.readUint32());
+
+ count = data.readUint32();
+ if (count > (data.dataAvail() / sizeof(int32_t))) {
+ return BAD_VALUE;
+ }
+ ints.resize(count);
for (auto &i : ints) {
i = data.readInt32();
}
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 1ee8c71..f3edd3c 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -12,6 +12,66 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+cc_defaults {
+ name: "libui-defaults",
+ clang: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ cppflags: [
+ "-Wextra",
+ ],
+
+ sanitize: {
+ integer_overflow: true,
+ misc_undefined: ["bounds"],
+ },
+
+}
+
+cc_library_static {
+ name: "libui-types",
+ vendor_available: true,
+ host_supported: true,
+ target: {
+ windows: {
+ enabled: true,
+ }
+ },
+
+ defaults: [
+ "libui-defaults",
+ ],
+
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "apex_inherit",
+
+ shared_libs: [
+ "libutils",
+ ],
+
+ static_libs: [
+ "libmath",
+ ],
+
+ srcs: [
+ "ColorSpace.cpp",
+ ],
+
+ export_include_dirs: [
+ "include_types",
+ ],
+
+ export_static_lib_headers: [
+ "libmath",
+ ],
+
+}
+
cc_library_shared {
name: "libui",
vendor_available: true,
@@ -35,8 +95,9 @@
},
srcs: [
- "ColorSpace.cpp",
"DebugUtils.cpp",
+ "DeviceProductInfo.cpp",
+ "DisplayInfo.cpp",
"Fence.cpp",
"FenceTime.cpp",
"FrameStats.cpp",
@@ -65,8 +126,11 @@
"include_private",
],
- // Uncomment the following line to enable VALIDATE_REGIONS traces
- //defaults: ["libui-validate-regions-defaults"],
+ defaults: [
+ "libui-defaults",
+ // Uncomment the following line to enable VALIDATE_REGIONS traces
+ //defaults: ["libui-validate-regions-defaults"],
+ ],
shared_libs: [
"android.hardware.graphics.allocator@2.0",
@@ -100,6 +164,10 @@
"libmath",
],
+ whole_static_libs: [
+ "libui-types",
+ ],
+
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
new file mode 100644
index 0000000..4d6ce43
--- /dev/null
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 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 <ui/DeviceProductInfo.h>
+
+#include <android-base/stringprintf.h>
+#include <ui/FlattenableHelpers.h>
+#include <utils/Log.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+using base::StringAppendF;
+
+size_t DeviceProductInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(name) +
+ FlattenableHelpers::getFlattenedSize(manufacturerPnpId) +
+ FlattenableHelpers::getFlattenedSize(productId) +
+ FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) +
+ FlattenableHelpers::getFlattenedSize(relativeAddress);
+}
+
+status_t DeviceProductInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress));
+ return OK;
+}
+
+status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress));
+ return OK;
+}
+
+void DeviceProductInfo::dump(std::string& result) const {
+ StringAppendF(&result, "{name=%s, ", name.c_str());
+ StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data());
+ StringAppendF(&result, "productId=%s, ", productId.c_str());
+
+ if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "modelYear=%u, ", model->year);
+ } else if (const auto* manufactureWeekAndYear =
+ std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week);
+ StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year);
+ } else if (const auto* manufactureYear =
+ std::get_if<ManufactureYear>(&manufactureOrModelDate)) {
+ StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year);
+ } else {
+ ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+ }
+
+ result.append("relativeAddress=[");
+ for (size_t i = 0; i < relativeAddress.size(); i++) {
+ if (i != 0) {
+ result.append(", ");
+ }
+ StringAppendF(&result, "%u", relativeAddress[i]);
+ }
+ result.append("]}");
+}
+
+} // namespace android
diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/DisplayInfo.cpp
new file mode 100644
index 0000000..73a78af
--- /dev/null
+++ b/libs/ui/DisplayInfo.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 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 <ui/DisplayInfo.h>
+
+#include <cstdint>
+
+#include <ui/FlattenableHelpers.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+size_t DisplayInfo::getFlattenedSize() const {
+ return FlattenableHelpers::getFlattenedSize(connectionType) +
+ FlattenableHelpers::getFlattenedSize(density) +
+ FlattenableHelpers::getFlattenedSize(secure) +
+ FlattenableHelpers::getFlattenedSize(deviceProductInfo);
+}
+
+status_t DisplayInfo::flatten(void* buffer, size_t size) const {
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo));
+ return OK;
+}
+
+status_t DisplayInfo::unflatten(void const* buffer, size_t size) {
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo));
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 97ead21..203a739 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,7 +1,6 @@
+chrisforbes@google.com
lpy@google.com
-marissaw@google.com
mathias@google.com
romainguy@google.com
stoza@google.com
-jwcai@google.com
-tianyuj@google.com
+vhau@google.com
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index af00342..807a5d9 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -19,7 +19,12 @@
#include <array>
#include <cstdint>
#include <optional>
+#include <string>
+#include <type_traits>
#include <variant>
+#include <vector>
+
+#include <utils/Flattenable.h>
namespace android {
@@ -29,13 +34,7 @@
// Product-specific information about the display or the directly connected device on the
// display chain. For example, if the display is transitively connected, this field may contain
// product information about the intermediate device.
-struct DeviceProductInfo {
- static constexpr size_t TEXT_BUFFER_SIZE = 20;
- static constexpr size_t RELATIVE_ADDRESS_SIZE = 4;
-
- using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>;
- static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff};
-
+struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> {
struct ModelYear {
uint32_t year;
};
@@ -48,21 +47,29 @@
};
// Display name.
- std::array<char, TEXT_BUFFER_SIZE> name;
+ std::string name;
// Manufacturer Plug and Play ID.
PnpId manufacturerPnpId;
// Manufacturer product ID.
- std::array<char, TEXT_BUFFER_SIZE> productId;
+ std::string productId;
using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>;
+ static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>);
ManufactureOrModelDate manufactureOrModelDate;
- // Relative address in the display network. Unavailable address is indicated
- // by all elements equal to 255.
+ // Relative address in the display network. Empty vector indicates that the
+ // address is unavailable.
// For example, for HDMI connected device this will be the physical address.
- RelativeAddress relativeAddress;
+ std::vector<uint8_t> relativeAddress;
+
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+
+ void dump(std::string& result) const;
};
} // namespace android
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 897060c..03e0a38 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -20,19 +20,23 @@
#include <type_traits>
#include <ui/DeviceProductInfo.h>
+#include <utils/Flattenable.h>
namespace android {
enum class DisplayConnectionType { Internal, External };
// Immutable information about physical display.
-struct DisplayInfo {
+struct DisplayInfo : LightFlattenable<DisplayInfo> {
DisplayConnectionType connectionType = DisplayConnectionType::Internal;
float density = 0.f;
bool secure = false;
std::optional<DeviceProductInfo> deviceProductInfo;
-};
-static_assert(std::is_trivially_copyable_v<DisplayInfo>);
+ bool isFixedSize() const { return false; }
+ size_t getFlattenedSize() const;
+ status_t flatten(void* buffer, size_t size) const;
+ status_t unflatten(void const* buffer, size_t size);
+};
} // namespace android
diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h
new file mode 100644
index 0000000..8e316d8
--- /dev/null
+++ b/libs/ui/include_private/ui/FlattenableHelpers.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2020 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 <numeric>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+#include <utils/Flattenable.h>
+
+#define RETURN_IF_ERROR(op) \
+ if (const status_t status = (op); status != OK) return status;
+
+namespace android {
+
+struct FlattenableHelpers {
+ // Helpers for reading and writing POD structures
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static constexpr size_t getFlattenedSize(const T&) {
+ return sizeof(T);
+ }
+
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static status_t flatten(void** buffer, size_t* size, const T& value) {
+ if (*size < sizeof(T)) return NO_MEMORY;
+ FlattenableUtils::write(*buffer, *size, value);
+ return OK;
+ }
+
+ template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+ static status_t unflatten(const void** buffer, size_t* size, T* value) {
+ if (*size < sizeof(T)) return NO_MEMORY;
+ FlattenableUtils::read(*buffer, *size, *value);
+ return OK;
+ }
+
+ // Helpers for reading and writing std::string
+ static size_t getFlattenedSize(const std::string& str) {
+ return sizeof(uint64_t) + str.length();
+ }
+
+ static status_t flatten(void** buffer, size_t* size, const std::string& str) {
+ if (*size < getFlattenedSize(str)) return NO_MEMORY;
+ flatten(buffer, size, (uint64_t)str.length());
+ memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
+ FlattenableUtils::advance(*buffer, *size, str.length());
+ return OK;
+ }
+
+ static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
+ uint64_t length;
+ RETURN_IF_ERROR(unflatten(buffer, size, &length));
+ if (*size < length) return NO_MEMORY;
+ str->assign(reinterpret_cast<const char*>(*buffer), length);
+ FlattenableUtils::advance(*buffer, *size, length);
+ return OK;
+ }
+
+ // Helpers for reading and writing LightFlattenable
+ template <class T>
+ static size_t getFlattenedSize(const LightFlattenable<T>& value) {
+ return value.getFlattenedSize();
+ }
+
+ template <class T>
+ static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) {
+ RETURN_IF_ERROR(value.flatten(*buffer, *size));
+ FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize());
+ return OK;
+ }
+
+ template <class T>
+ static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) {
+ RETURN_IF_ERROR(value->unflatten(*buffer, *size));
+ FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize());
+ return OK;
+ }
+
+ // Helpers for reading and writing std::optional
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static size_t getFlattenedSize(const std::optional<T>& value) {
+ return sizeof(bool) + (value ? getFlattenedSize(*value) : 0);
+ }
+
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) {
+ if (value) {
+ RETURN_IF_ERROR(flatten(buffer, size, true));
+ RETURN_IF_ERROR(flatten(buffer, size, *value));
+ } else {
+ RETURN_IF_ERROR(flatten(buffer, size, false));
+ }
+ return OK;
+ }
+
+ template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+ static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) {
+ bool isPresent;
+ RETURN_IF_ERROR(unflatten(buffer, size, &isPresent));
+ if (isPresent) {
+ *value = T();
+ RETURN_IF_ERROR(unflatten(buffer, size, &(**value)));
+ } else {
+ value->reset();
+ }
+ return OK;
+ }
+
+ // Helpers for reading and writing std::vector
+ template <class T>
+ static size_t getFlattenedSize(const std::vector<T>& value) {
+ return std::accumulate(value.begin(), value.end(), sizeof(uint64_t),
+ [](size_t sum, const T& element) {
+ return sum + getFlattenedSize(element);
+ });
+ }
+
+ template <class T>
+ static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
+ RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size()));
+ for (const auto& element : value) {
+ RETURN_IF_ERROR(flatten(buffer, size, element));
+ }
+ return OK;
+ }
+
+ template <class T>
+ static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
+ uint64_t numElements;
+ RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
+ // We don't need an extra size check since each iteration of the loop does that
+ std::vector<T> elements;
+ for (size_t i = 0; i < numElements; i++) {
+ T element;
+ RETURN_IF_ERROR(unflatten(buffer, size, &element));
+ elements.push_back(element);
+ }
+ *value = std::move(elements);
+ return OK;
+ }
+};
+
+} // namespace android
+
+#undef RETURN_IF_ERROR
\ No newline at end of file
diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include_types/ui/ColorSpace.h
similarity index 100%
rename from libs/ui/include/ui/ColorSpace.h
rename to libs/ui/include_types/ui/ColorSpace.h
diff --git a/libs/ui/include_vndk/ui/ColorSpace.h b/libs/ui/include_vndk/ui/ColorSpace.h
index ddf70d5..7d2a6d3 120000
--- a/libs/ui/include_vndk/ui/ColorSpace.h
+++ b/libs/ui/include_vndk/ui/ColorSpace.h
@@ -1 +1 @@
-../../include/ui/ColorSpace.h
\ No newline at end of file
+../../include_types/ui/ColorSpace.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index b53342c..28ef77a 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,13 @@
}
cc_test {
+ name: "FlattenableHelpers_test",
+ shared_libs: ["libui"],
+ srcs: ["FlattenableHelpers_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "GraphicBufferAllocator_test",
header_libs: [
"libnativewindow_headers",
diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp
new file mode 100644
index 0000000..db32bc7
--- /dev/null
+++ b/libs/ui/tests/FlattenableHelpers_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FlattenableHelpersTest"
+
+#include <ui/FlattenableHelpers.h>
+
+#include <gtest/gtest.h>
+#include <utils/Flattenable.h>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android {
+
+namespace {
+
+struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> {
+ std::unique_ptr<int32_t> ptr;
+
+ bool isFixedSize() const { return true; }
+ size_t getFlattenedSize() const { return sizeof(int32_t); }
+
+ status_t flatten(void* buffer, size_t size) const {
+ FlattenableUtils::write(buffer, size, *ptr);
+ return OK;
+ }
+
+ status_t unflatten(void const* buffer, size_t size) {
+ int value;
+ FlattenableUtils::read(buffer, size, value);
+ ptr = std::make_unique<int32_t>(value);
+ return OK;
+ }
+};
+
+class FlattenableHelpersTest : public testing::Test {
+public:
+ template <class T>
+ void testWriteThenRead(const T& value, size_t bufferSize) {
+ std::vector<int8_t> buffer(bufferSize);
+ auto rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+
+ auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size = buffer.size();
+ T valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ EXPECT_EQ(value, valueRead);
+ }
+
+ template <class T>
+ void testTriviallyCopyable(const T& value) {
+ testWriteThenRead(value, sizeof(T));
+ }
+
+ template <class T>
+ void testWriteThenRead(const T& value) {
+ testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value));
+ }
+};
+
+TEST_F(FlattenableHelpersTest, TriviallyCopyable) {
+ testTriviallyCopyable(42);
+ testTriviallyCopyable(1LL << 63);
+ testTriviallyCopyable(false);
+ testTriviallyCopyable(true);
+ testTriviallyCopyable(std::optional<int>());
+ testTriviallyCopyable(std::optional<int>(4));
+}
+
+TEST_F(FlattenableHelpersTest, String) {
+ testWriteThenRead(std::string("Android"));
+ testWriteThenRead(std::string());
+}
+
+TEST_F(FlattenableHelpersTest, Vector) {
+ testWriteThenRead(std::vector<int>({1, 2, 3}));
+ testWriteThenRead(std::vector<int>());
+}
+
+TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) {
+ std::vector<size_t> buffer;
+ constexpr int kInternalValue = 16;
+ {
+ std::optional<TestLightFlattenable> value =
+ TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)};
+ buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+ void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+ }
+
+ const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size_t size = buffer.size();
+ std::optional<TestLightFlattenable> valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ ASSERT_TRUE(valueRead.has_value());
+ EXPECT_EQ(kInternalValue, *valueRead->ptr);
+}
+
+TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) {
+ std::vector<size_t> buffer;
+ {
+ std::optional<TestLightFlattenable> value;
+ buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+ void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+ size_t size = buffer.size();
+ ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+ }
+
+ const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+ size_t size = buffer.size();
+ std::optional<TestLightFlattenable> valueRead;
+ ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+ ASSERT_FALSE(valueRead.has_value());
+}
+
+} // namespace
+} // namespace android
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 2fcee7b..37c19d4 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -16,6 +16,12 @@
name: "libbufferhub_headers",
export_include_dirs: ["include"],
vendor_available: true, // TODO(b/112338314): Does shouldn't be available to vendor.
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
}
sourceFiles = [
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 81a9b2d..340d7bf 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -17,6 +17,12 @@
name: "libdvr_headers",
export_include_dirs: ["include"],
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
}
cc_library_headers {
diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp
new file mode 100644
index 0000000..3490775
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/Android.bp
@@ -0,0 +1,23 @@
+cc_fuzz {
+ name: "libpdx_fuzz",
+ clang: true,
+ srcs: [
+ "service_dispatcher_fuzzer.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ static_libs: [
+ "libpdx",
+ ],
+ shared_libs: [
+ "libutils",
+ "liblog",
+ ],
+ fuzz_config: {
+ fuzz_on_haiku_host: false,
+ fuzz_on_haiku_device: false,
+ },
+}
diff --git a/libs/vr/libpdx/fuzz/helpers.h b/libs/vr/libpdx/fuzz/helpers.h
new file mode 100644
index 0000000..83ec409
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/helpers.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2020 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.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+// brian.balling@leviathansecurity.com
+
+#ifndef LEV_FUZZERS_LIBPDX_HELPERS_H_
+#define LEV_FUZZERS_LIBPDX_HELPERS_H_
+
+#define UNUSED(expr) \
+ do { \
+ (void)(expr); \
+ } while (0)
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <pdx/client.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <pdx/service_endpoint.h>
+#include <sys/eventfd.h>
+#include <memory>
+#include <vector>
+
+using namespace android::pdx;
+
+// Vector of operations we can call in the dispatcher.
+static const std::vector<std::function<void(
+ const std::unique_ptr<ServiceDispatcher>&, FuzzedDataProvider*)>>
+ dispatcher_operations = {
+ [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+ FuzzedDataProvider*) -> void { dispatcher->EnterDispatchLoop(); },
+ [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+ FuzzedDataProvider*) -> void { dispatcher->ReceiveAndDispatch(); },
+ [](const std::unique_ptr<ServiceDispatcher>& dispatcher,
+ FuzzedDataProvider* fdp) -> void {
+ dispatcher->ReceiveAndDispatch(fdp->ConsumeIntegral<int>());
+ }};
+
+// Most of the fuzzing occurs within the endpoint, which is derived from an
+// abstract class. So we are returning garbage data for most functions besides
+// the ones we added or need to actually use.
+class FuzzEndpoint : public Endpoint {
+ public:
+ explicit FuzzEndpoint(FuzzedDataProvider* fdp) {
+ _fdp = fdp;
+ _epoll_fd = eventfd(0, 0);
+ }
+
+ ~FuzzEndpoint() { close(_epoll_fd); }
+
+ // Returns an fd that can be used with epoll() to wait for incoming messages
+ // from this endpoint.
+ int epoll_fd() const { return _epoll_fd; }
+
+ // Associates a Service instance with an endpoint by setting the service
+ // context pointer to the address of the Service. Only one Service may be
+ // associated with a given endpoint.
+ Status<void> SetService(Service* service) {
+ _service = service;
+ return Status<void>(0);
+ }
+
+ // Set the channel context for the given channel.
+ Status<void> SetChannel(int channel_id, Channel* channel) {
+ UNUSED(channel_id);
+ _channel = std::shared_ptr<Channel>(channel);
+ return Status<void>(0);
+ }
+
+ // Receives a message on the given endpoint file descriptor.
+ // This is called by the dispatcher to determine what operations
+ // to make, so we are fuzzing the response.
+ Status<void> MessageReceive(Message* message) {
+ // Create a randomized MessageInfo struct.
+ MessageInfo info;
+ eventfd_t wakeup_val = 0;
+ info.pid = _fdp->ConsumeIntegral<int>();
+ info.tid = _fdp->ConsumeIntegral<int>();
+ info.cid = _fdp->ConsumeIntegral<int>();
+ info.mid = _fdp->ConsumeIntegral<int>();
+ info.euid = _fdp->ConsumeIntegral<int>();
+ info.egid = _fdp->ConsumeIntegral<int>();
+ info.op = _fdp->ConsumeIntegral<int32_t>();
+ info.flags = _fdp->ConsumeIntegral<uint32_t>();
+ info.service = _service;
+ info.channel = _channel.get();
+ info.send_len = _fdp->ConsumeIntegral<size_t>();
+ info.recv_len = _fdp->ConsumeIntegral<size_t>();
+ info.fd_count = _fdp->ConsumeIntegral<size_t>();
+ if (_fdp->remaining_bytes() >= 32) {
+ std::vector<uint8_t> impulse_vec = _fdp->ConsumeBytes<uint8_t>(32);
+ memcpy(info.impulse, impulse_vec.data(), 32);
+ }
+
+ *message = Message(info);
+ eventfd_read(_epoll_fd, &wakeup_val);
+
+ return Status<void>();
+ }
+
+ // Returns a tag that uniquely identifies a specific underlying IPC
+ // transport.
+ uint32_t GetIpcTag() const { return 0; }
+
+ // Close a channel, signaling the client file object and freeing the channel
+ // id. Once closed, the client side of the channel always returns the error
+ // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE.
+ Status<void> CloseChannel(int channel_id) {
+ UNUSED(channel_id);
+ return Status<void>();
+ }
+
+ // Update the event bits for the given channel (given by id), using the
+ // given clear and set masks.
+ Status<void> ModifyChannelEvents(int channel_id, int clear_mask,
+ int set_mask) {
+ UNUSED(channel_id);
+ UNUSED(clear_mask);
+ UNUSED(set_mask);
+ return Status<void>();
+ }
+
+ // Create a new channel and push it as a file descriptor to the process
+ // sending the |message|. |flags| may be set to O_NONBLOCK and/or
+ // O_CLOEXEC to control the initial behavior of the new file descriptor (the
+ // sending process may change these later using fcntl()). The internal
+ // Channel instance associated with this channel is set to |channel|,
+ // which may be nullptr. The new channel id allocated for this channel is
+ // returned in |channel_id|, which may also be nullptr if not needed.
+ Status<RemoteChannelHandle> PushChannel(Message* message, int flags,
+ Channel* channel, int* channel_id) {
+ UNUSED(message);
+ UNUSED(flags);
+ UNUSED(channel);
+ UNUSED(channel_id);
+ return Status<RemoteChannelHandle>();
+ }
+
+ // Check whether the |ref| is a reference to a channel to the service
+ // represented by the |endpoint|. If the channel reference in question is
+ // valid, the Channel object is returned in |channel| when non-nullptr and
+ // the channel ID is returned through the Status object.
+ Status<int> CheckChannel(const Message* message, ChannelReference ref,
+ Channel** channel) {
+ UNUSED(message);
+ UNUSED(ref);
+ UNUSED(channel);
+ return Status<int>();
+ }
+
+ // Replies to the message with a return code.
+ Status<void> MessageReply(Message* message, int return_code) {
+ UNUSED(message);
+ UNUSED(return_code);
+ return Status<void>();
+ }
+
+ // Replies to the message with a file descriptor.
+ Status<void> MessageReplyFd(Message* message, unsigned int push_fd) {
+ UNUSED(message);
+ UNUSED(push_fd);
+ return Status<void>();
+ }
+
+ // Replies to the message with a local channel handle.
+ Status<void> MessageReplyChannelHandle(Message* message,
+ const LocalChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<void>();
+ }
+
+ // Replies to the message with a borrowed local channel handle.
+ Status<void> MessageReplyChannelHandle(Message* message,
+ const BorrowedChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<void>();
+ }
+
+ // Replies to the message with a remote channel handle.
+ Status<void> MessageReplyChannelHandle(Message* message,
+ const RemoteChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<void>();
+ }
+
+ // Reads message data into an array of memory buffers.
+ Status<size_t> ReadMessageData(Message* message, const iovec* vector,
+ size_t vector_length) {
+ UNUSED(message);
+ UNUSED(vector);
+ UNUSED(vector_length);
+ return Status<size_t>();
+ }
+
+ // Sends reply data for message.
+ Status<size_t> WriteMessageData(Message* message, const iovec* vector,
+ size_t vector_length) {
+ UNUSED(message);
+ UNUSED(vector);
+ UNUSED(vector_length);
+ return Status<size_t>();
+ }
+
+ // Records a file descriptor into the message buffer and returns the
+ // remapped reference to be sent to the remote process.
+ Status<FileReference> PushFileHandle(Message* message,
+ const LocalHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<FileReference>();
+ }
+
+ Status<FileReference> PushFileHandle(Message* message,
+ const BorrowedHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<FileReference>();
+ }
+
+ Status<FileReference> PushFileHandle(Message* message,
+ const RemoteHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<FileReference>();
+ }
+
+ Status<ChannelReference> PushChannelHandle(Message* message,
+ const LocalChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<ChannelReference>();
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ Message* message, const BorrowedChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<ChannelReference>();
+ }
+
+ Status<ChannelReference> PushChannelHandle(
+ Message* message, const RemoteChannelHandle& handle) {
+ UNUSED(message);
+ UNUSED(handle);
+ return Status<ChannelReference>();
+ }
+
+ // Obtains a file descriptor/channel handle from a message for the given
+ // reference.
+ LocalHandle GetFileHandle(Message* message, FileReference ref) const {
+ UNUSED(message);
+ UNUSED(ref);
+ return LocalHandle();
+ }
+
+ LocalChannelHandle GetChannelHandle(Message* message,
+ ChannelReference ref) const {
+ UNUSED(message);
+ UNUSED(ref);
+ return LocalChannelHandle();
+ }
+
+ // Transport-specific message state management.
+ void* AllocateMessageState() { return nullptr; }
+
+ void FreeMessageState(void* state) { UNUSED(state); }
+
+ // Cancels the endpoint, unblocking any receiver threads waiting for a
+ // message.
+ Status<void> Cancel() { return Status<void>(); }
+
+ private:
+ FuzzedDataProvider* _fdp;
+ std::shared_ptr<Channel> _channel;
+ Service* _service;
+ int _epoll_fd;
+};
+
+#endif // LEV_FUZZERS_LIBPDX_HELPERS_H_
diff --git a/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp b/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp
new file mode 100644
index 0000000..3a3bfd9
--- /dev/null
+++ b/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 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.
+ */
+// Authors: corbin.souffrant@leviathansecurity.com
+// brian.balling@leviathansecurity.com
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <helpers.h>
+#include <pdx/client_channel.h>
+#include <pdx/service.h>
+#include <pdx/service_dispatcher.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/eventfd.h>
+#include <thread>
+
+using namespace android::pdx;
+
+// Dispatch fuzzer entry point. This fuzzer creates a ServiceDispatcher
+// and creates an endpoint that returns fuzzed messages that are passed
+// to the ReceiveAndDispatch and DispatchLoop functions.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ eventfd_t wakeup_val = 1;
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+
+ // Endpoint is only used to be immediately wrapped as a unique_ptr,
+ // so it is ok to be using a raw ptr and new here without freeing.
+ FuzzEndpoint* endpoint = new FuzzEndpoint(&fdp);
+ std::unique_ptr<ServiceDispatcher> dispatcher = ServiceDispatcher::Create();
+ std::shared_ptr<Channel> channel(nullptr);
+ std::shared_ptr<Client> client(nullptr);
+ std::shared_ptr<Service> service(
+ new Service("FuzzService", std::unique_ptr<Endpoint>(endpoint)));
+
+ service->SetChannel(0, std::shared_ptr<Channel>(channel));
+ dispatcher->AddService(service);
+
+ // Dispatcher blocks, so needs to run in its own thread.
+ std::thread run_dispatcher([&]() {
+ uint8_t opt = 0;
+
+ // Right now the only operations block, so the while loop is pointless
+ // but leaving it in, just in case that ever changes.
+ while (fdp.remaining_bytes() > sizeof(MessageInfo)) {
+ opt = fdp.ConsumeIntegral<uint8_t>() % dispatcher_operations.size();
+ dispatcher_operations[opt](dispatcher, &fdp);
+ }
+ });
+
+ // Continuously wake up the epoll so the dispatcher can run.
+ while (fdp.remaining_bytes() > sizeof(MessageInfo)) {
+ eventfd_write(endpoint->epoll_fd(), wakeup_val);
+ }
+
+ // Cleanup the dispatcher and thread.
+ dispatcher->SetCanceled(true);
+ if (run_dispatcher.joinable())
+ run_dispatcher.join();
+ dispatcher->RemoveService(service);
+
+ return 0;
+}
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 0556ea1..1f1bcb3 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -4,12 +4,12 @@
// found in the LICENSE file.
//
// eglext_angle.h: ANGLE modifications to the eglext.h header file.
-// Currently we don't include this file directly, we patch eglext.h
-// to include it implicitly so it is visible throughout our code.
#ifndef INCLUDE_EGL_EGLEXT_ANGLE_
#define INCLUDE_EGL_EGLEXT_ANGLE_
+#include <EGL/eglext.h>
+
// clang-format off
#ifndef EGL_ANGLE_robust_resource_initialization
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index e8d3684..3c76c62 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -102,11 +102,6 @@
"libbacktrace",
"libbase",
],
- target: {
- vendor: {
- exclude_shared_libs: ["libgraphicsenv"],
- },
- },
}
cc_library_static {
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index cc42ac7..3284778 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -17,11 +17,13 @@
#include "FileBlobCache.h"
#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
-#include <log/log.h>
#include <sys/mman.h>
#include <sys/stat.h>
+#include <unistd.h>
+#include <log/log.h>
// Cache file header
static const char* cacheFileMagic = "EGL$";
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 83de260..1afc693 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,27 +17,23 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <EGL/Loader.h>
-
-#include <string>
-
-#include <dirent.h>
-#include <dlfcn.h>
+#include "EGL/Loader.h"
#include <android-base/properties.h>
#include <android/dlext.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
#include <utils/Timers.h>
-
-#ifndef __ANDROID_VNDK__
-#include <graphicsenv/GraphicsEnv.h>
-#endif
#include <vndksupport/linker.h>
+#include <string>
+
+#include "EGL/eglext_angle.h"
#include "egl_platform_entries.h"
#include "egl_trace.h"
#include "egldefs.h"
-#include <EGL/eglext_angle.h>
namespace android {
@@ -159,13 +155,11 @@
return true;
}
-#ifndef __ANDROID_VNDK__
// Return true if updated driver namespace is set.
ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (ns) {
return true;
}
-#endif
return false;
}
@@ -276,7 +270,7 @@
// will set cnx->useAngle appropriately.
// Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
// not just loading ANGLE as option.
- init_angle_backend(hnd->dso[0], cnx);
+ init_angle_backend(hnd->dso[2], cnx);
}
LOG_ALWAYS_FATAL_IF(!hnd,
@@ -557,12 +551,8 @@
}
void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) {
- void* eglCreateDeviceANGLE = nullptr;
-
- ALOGV("dso: %p", dso);
- eglCreateDeviceANGLE = dlsym(dso, "eglCreateDeviceANGLE");
- ALOGV("eglCreateDeviceANGLE: %p", eglCreateDeviceANGLE);
- if (eglCreateDeviceANGLE) {
+ void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
+ if (pANGLEGetDisplayPlatform) {
ALOGV("ANGLE GLES library in use");
cnx->useAngle = true;
} else {
@@ -573,7 +563,7 @@
Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
ATRACE_CALL();
-#ifndef __ANDROID_VNDK__
+
android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (!ns) {
return nullptr;
@@ -603,9 +593,6 @@
hnd->set(dso, GLESv2);
}
return hnd;
-#else
- return nullptr;
-#endif
}
Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3b1cf71..8c6f284 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -19,25 +19,22 @@
#include "egl_display.h"
+#include <SurfaceFlingerProperties.h>
+#include <android-base/properties.h>
+#include <android/dlext.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+
#include "../egl_impl.h"
-
-#include <EGL/eglext_angle.h>
-#include <private/EGL/display.h>
-
+#include "EGL/eglext_angle.h"
#include "Loader.h"
#include "egl_angle_platform.h"
#include "egl_cache.h"
#include "egl_object.h"
#include "egl_tls.h"
-
-#include <SurfaceFlingerProperties.h>
-#include <android-base/properties.h>
-#include <android/dlext.h>
-#include <dlfcn.h>
-#include <graphicsenv/GraphicsEnv.h>
-
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
+#include "private/EGL/display.h"
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 99283be..1119e4a 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -18,36 +18,32 @@
#include "egl_platform_entries.h"
-#include <ctype.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/eglext_angle.h>
-
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware_buffer.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
+#include <ctype.h>
#include <cutils/compiler.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <stdlib.h>
+#include <string.h>
#include <condition_variable>
#include <deque>
#include <mutex>
-#include <unordered_map>
#include <string>
#include <thread>
+#include <unordered_map>
#include "../egl_impl.h"
-
+#include "EGL/egl.h"
+#include "EGL/eglext.h"
+#include "EGL/eglext_angle.h"
#include "egl_display.h"
-#include "egl_object.h"
#include "egl_layers.h"
+#include "egl_object.h"
#include "egl_tls.h"
#include "egl_trace.h"
@@ -96,7 +92,7 @@
"EGL_EXT_surface_CTA861_3_metadata "
;
-// Whitelist of extensions exposed to applications if implemented in the vendor driver.
+// Allowed list of extensions exposed to applications if implemented in the vendor driver.
char const * const gExtensionString =
"EGL_KHR_image " // mandatory
"EGL_KHR_image_base " // mandatory
@@ -2248,15 +2244,8 @@
}
EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
- // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
- // this function cannot be implemented when this libEGL is built for
- // vendors.
-#ifndef __ANDROID_VNDK__
if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
- return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-#endif
}
// ----------------------------------------------------------------------------
diff --git a/opengl/tests/gl2_jni/Android.bp b/opengl/tests/gl2_jni/Android.bp
index 65f89b1..8d4323f 100644
--- a/opengl/tests/gl2_jni/Android.bp
+++ b/opengl/tests/gl2_jni/Android.bp
@@ -17,6 +17,7 @@
"-Werror",
"-Wno-error=unused-parameter",
],
+ header_libs: ["jni_headers"],
srcs: ["jni/gl_code.cpp"],
shared_libs: [
"liblog",
diff --git a/opengl/tests/gl_jni/Android.bp b/opengl/tests/gl_jni/Android.bp
index 5bec336..0cb129a 100644
--- a/opengl/tests/gl_jni/Android.bp
+++ b/opengl/tests/gl_jni/Android.bp
@@ -19,6 +19,7 @@
"-Werror",
"-Wno-error=unused-parameter",
],
+ header_libs: ["jni_headers"],
srcs: ["jni/gl_code.cpp"],
shared_libs: [
"liblog",
diff --git a/opengl/tests/gl_perfapp/Android.bp b/opengl/tests/gl_perfapp/Android.bp
index cf899ac..66afb6a 100644
--- a/opengl/tests/gl_perfapp/Android.bp
+++ b/opengl/tests/gl_perfapp/Android.bp
@@ -17,6 +17,7 @@
"-Werror",
"-Wno-error=unused-parameter",
],
+ header_libs: ["jni_headers"],
srcs: ["jni/gl_code.cpp"],
shared_libs: [
"liblog",
diff --git a/opengl/tests/gldual/Android.bp b/opengl/tests/gldual/Android.bp
index 2432566..1006d44 100644
--- a/opengl/tests/gldual/Android.bp
+++ b/opengl/tests/gldual/Android.bp
@@ -20,6 +20,7 @@
"-Werror",
"-Wno-error=unused-parameter",
],
+ header_libs: ["jni_headers"],
srcs: ["jni/gl_code.cpp"],
shared_libs: [
"liblog",
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index 41fcf1b..9efd38f 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -1,6 +1,18 @@
#!/bin/bash
set -u
set -e
+
+if [ -z "$ANDROID_BUILD_TOP" ] ; then
+ echo "ANDROID_BUILD_TOP is not set, did you run lunch?"
+ exit 1
+fi
+
+# Avoid spewing files in any location other than the intended one.
+if [ ! -x "$PWD/gen" ] ; then
+ echo "Run this script from its parent directory".
+ exit 1
+fi
+
rm -rf out generated
mkdir out
@@ -92,7 +104,7 @@
# Add UnsupportedAppUsage.java to known sources.
mkdir -p out/android/compat/annotation
-cp ../../../../../tools/platform-compat/annotation/src/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
+cp ${ANDROID_BUILD_TOP}/tools/platform-compat/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
pushd out > /dev/null
mkdir classes
@@ -153,23 +165,23 @@
fi
}
-compareGenerated ../../../../base/core/jni generated/C com_google_android_gles_jni_GLImpl.cpp
-compareGenerated ../../../../base/opengl/java/com/google/android/gles_jni generated/com/google/android/gles_jni GLImpl.java
+compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/core/jni generated/C com_google_android_gles_jni_GLImpl.cpp
+compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/com/google/android/gles_jni generated/com/google/android/gles_jni GLImpl.java
for x in GL.java GL10.java GL10Ext.java GL11.java GL11Ext.java GL11ExtensionPack.java
do
- compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
+ compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
done
for x in EGL14 EGL15 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
do
- compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
- compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp
+ compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/android/opengl generated/android/opengl ${x}.java
+ compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/core/jni generated/C android_opengl_${x}.cpp
done
for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync
do
- compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
+ compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/android/opengl generated/android/opengl ${x}.java
done
if [ $KEEP_GENERATED == "0" ] ; then
diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
index 9932556..951ecff 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
@@ -21,8 +21,8 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.SurfaceTexture;
import android.view.Surface;
-import android.view.SurfaceView;
import android.view.SurfaceHolder;
+import android.view.SurfaceView;
/**
* EGL 1.4
diff --git a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
index afcc3eb..32c9d7d 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
+++ b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
@@ -19,6 +19,7 @@
package com.google.android.gles_jni;
import android.app.AppGlobals;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.Build;
@@ -26,6 +27,7 @@
import android.util.Log;
import java.nio.Buffer;
+
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL10Ext;
import javax.microedition.khronos.opengles.GL11;
@@ -55,6 +57,7 @@
private boolean have_OES_framebuffer_object;
private boolean have_OES_texture_cube_map;
+ @UnsupportedAppUsage
public GLImpl() {
}
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 6eed24a..04fe1e6 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,6 +21,7 @@
"libbinder",
"libcutils",
"libgfxstats",
+ "libgpumem",
"libgraphicsenv",
"liblog",
"libutils",
@@ -85,6 +86,10 @@
name: "gpuservice",
defaults: ["libgpuservice_binary"],
init_rc: ["gpuservice.rc"],
+ required: [
+ "bpfloader",
+ "gpu_mem.o",
+ ],
srcs: [":gpuservice_binary_sources"],
shared_libs: [
"libgpuservice",
diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk
new file mode 100644
index 0000000..482fc6d
--- /dev/null
+++ b/services/gpuservice/CleanSpec.mk
@@ -0,0 +1,52 @@
+# Copyright 2020 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# Remove gpu_mem.o
+$(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp)
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index f40ce71..bad5637 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,11 +24,11 @@
#include <binder/Parcel.h>
#include <binder/PermissionCache.h>
#include <cutils/properties.h>
+#include <gpumem/GpuMem.h>
#include <gpustats/GpuStats.h>
#include <private/android_filesystem_config.h>
#include <utils/String8.h>
#include <utils/Trace.h>
-
#include <vkjson.h>
namespace android {
@@ -45,7 +45,10 @@
const char* const GpuService::SERVICE_NAME = "gpu";
-GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){};
+GpuService::GpuService()
+ : mGpuMem(std::make_unique<GpuMem>()), mGpuStats(std::make_unique<GpuStats>()) {
+ mGpuMem->initialize();
+};
void GpuService::setGpuStats(const std::string& driverPackageName,
const std::string& driverVersionName, uint64_t driverVersionCode,
@@ -62,6 +65,14 @@
mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value);
}
+void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
+ developerDriverPath = driverPath;
+}
+
+std::string GpuService::getUpdatableDriverPath() {
+ return developerDriverPath;
+}
+
status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
ATRACE_CALL();
@@ -90,6 +101,7 @@
} else {
bool dumpAll = true;
bool dumpDriverInfo = false;
+ bool dumpMem = false;
bool dumpStats = false;
size_t numArgs = args.size();
@@ -99,15 +111,21 @@
dumpStats = true;
} else if (args[index] == String16("--gpudriverinfo")) {
dumpDriverInfo = true;
+ } else if (args[index] == String16("--gpumem")) {
+ dumpMem = true;
}
}
- dumpAll = !(dumpDriverInfo || dumpStats);
+ dumpAll = !(dumpDriverInfo || dumpMem || dumpStats);
}
if (dumpAll || dumpDriverInfo) {
dumpGameDriverInfo(&result);
result.append("\n");
}
+ if (dumpAll || dumpMem) {
+ mGpuMem->dump(args, &result);
+ result.append("\n");
+ }
if (dumpAll || dumpStats) {
mGpuStats->dump(args, &result);
result.append("\n");
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index b3e34d5..9a0460d 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -28,6 +28,7 @@
namespace android {
+class GpuMem;
class GpuStats;
class GpuService : public BnGpuService, public PriorityDumper {
@@ -50,6 +51,8 @@
int64_t driverLoadingTime) override;
void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value) override;
+ void setUpdatableDriverPath(const std::string& driverPath) override;
+ std::string getUpdatableDriverPath() override;
/*
* IBinder interface
@@ -72,7 +75,9 @@
/*
* Attributes
*/
+ std::unique_ptr<GpuMem> mGpuMem;
std::unique_ptr<GpuStats> mGpuStats;
+ std::string developerDriverPath;
};
} // namespace android
diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp
new file mode 100644
index 0000000..b875814
--- /dev/null
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -0,0 +1,22 @@
+// Copyright 2020 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.
+
+bpf {
+ name: "gpu_mem.o",
+ srcs: ["gpu_mem.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c
new file mode 100644
index 0000000..c75213b
--- /dev/null
+++ b/services/gpuservice/bpfprogs/gpu_mem.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 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 <bpf_helpers.h>
+
+/*
+ * On Android the number of active processes using gpu is limited.
+ * So this is assumed to be true: SUM(num_procs_using_gpu[i]) <= 1024
+ */
+#define GPU_MEM_TOTAL_MAP_SIZE 1024
+
+/*
+ * This map maintains the global and per process gpu memory total counters.
+ *
+ * The KEY is ((gpu_id << 32) | pid) while VAL is the size in bytes.
+ * Use HASH type here since key is not int.
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ */
+DEFINE_BPF_MAP_GRO(gpu_mem_total_map, HASH, uint64_t, uint64_t, GPU_MEM_TOTAL_MAP_SIZE,
+ AID_GRAPHICS);
+
+/* This struct aligns with the fields offsets of the raw tracepoint format */
+struct gpu_mem_total_args {
+ uint64_t ignore;
+ /* Actual fields start at offset 8 */
+ uint32_t gpu_id;
+ uint32_t pid;
+ uint64_t size;
+};
+
+/*
+ * This program parses the gpu_mem/gpu_mem_total tracepoint's data into
+ * {KEY, VAL} pair used to update the corresponding bpf map.
+ *
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ * Upon seeing size 0, the corresponding KEY needs to be cleaned up.
+ */
+DEFINE_BPF_PROG("tracepoint/gpu_mem/gpu_mem_total", AID_ROOT, AID_GRAPHICS, tp_gpu_mem_total)
+(struct gpu_mem_total_args* args) {
+ uint64_t key = 0;
+ uint64_t cur_val = 0;
+ uint64_t* prev_val = NULL;
+
+ /* The upper 32 bits are for gpu_id while the lower is the pid */
+ key = ((uint64_t)args->gpu_id << 32) | args->pid;
+ cur_val = args->size;
+
+ if (!cur_val) {
+ bpf_gpu_mem_total_map_delete_elem(&key);
+ return 0;
+ }
+
+ prev_val = bpf_gpu_mem_total_map_lookup_elem(&key);
+ if (prev_val) {
+ *prev_val = cur_val;
+ } else {
+ bpf_gpu_mem_total_map_update_elem(&key, &cur_val, BPF_NOEXIST);
+ }
+ return 0;
+}
+
+char _license[] SEC("license") = "Apache 2.0";
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
new file mode 100644
index 0000000..b2230b6
--- /dev/null
+++ b/services/gpuservice/gpumem/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libgpumem",
+ srcs: [
+ "GpuMem.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: [
+ "libbase",
+ "libbpf_android",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
new file mode 100644
index 0000000..1d4b524
--- /dev/null
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2020 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 "GpuMem"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "gpumem/GpuMem.h"
+
+#include <android-base/stringprintf.h>
+#include <libbpf.h>
+#include <libbpf_android.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+using base::StringAppendF;
+
+GpuMem::~GpuMem() {
+ bpf_detach_tracepoint(kGpuMemTraceGroup, kGpuMemTotalTracepoint);
+}
+
+void GpuMem::initialize() {
+ // Make sure bpf programs are loaded
+ bpf::waitForProgsLoaded();
+
+ int fd = bpf::bpfFdGet(kGpuMemTotalProgPath, BPF_F_RDONLY);
+ if (fd < 0) {
+ ALOGE("Failed to retrieve pinned program from %s", kGpuMemTotalProgPath);
+ return;
+ }
+
+ // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
+ if (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
+ ALOGE("Failed to attach bpf program to %s/%s tracepoint", kGpuMemTraceGroup,
+ kGpuMemTotalTracepoint);
+ return;
+ }
+
+ // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+ auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
+ if (!map.isValid()) {
+ ALOGE("Failed to create bpf map from %s", kGpuMemTotalMapPath);
+ return;
+ }
+ setGpuMemTotalMap(map);
+}
+
+void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ mGpuMemTotalMap = std::move(map);
+}
+
+// Dump the snapshots of global and per process memory usage on all gpus
+void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) {
+ ATRACE_CALL();
+
+ if (!mGpuMemTotalMap.isValid()) {
+ result->append("Failed to initialize GPU memory eBPF\n");
+ return;
+ }
+
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) {
+ result->append("GPU memory total usage map is empty\n");
+ return;
+ }
+ uint64_t key = res.value();
+ // unordered_map<gpu_id, vector<pair<pid, size>>>
+ std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint64_t>>> dumpMap;
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ dumpMap[gpu_id].emplace_back(pid, size);
+
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+
+ for (auto& gpu : dumpMap) {
+ if (gpu.second.empty()) continue;
+ StringAppendF(result, "Memory snapshot for GPU %u:\n", gpu.first);
+
+ std::sort(gpu.second.begin(), gpu.second.end(),
+ [](auto& l, auto& r) { return l.first < r.first; });
+
+ int i = 0;
+ if (gpu.second[0].first != 0) {
+ StringAppendF(result, "Global total: N/A\n");
+ } else {
+ StringAppendF(result, "Global total: %" PRIu64 "\n", gpu.second[0].second);
+ i++;
+ }
+ for (; i < gpu.second.size(); i++) {
+ StringAppendF(result, "Proc %u total: %" PRIu64 "\n", gpu.second[i].first,
+ gpu.second[i].second);
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
new file mode 100644
index 0000000..6d0322a
--- /dev/null
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 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 <bpf/BpfMap.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class GpuMem {
+public:
+ GpuMem() = default;
+ ~GpuMem();
+
+ // initialize eBPF program and map
+ void initialize();
+ // dumpsys interface
+ void dump(const Vector<String16>& args, std::string* result);
+
+private:
+ // Friend class for testing.
+ friend class TestableGpuMem;
+
+ // set gpu memory total map
+ void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map);
+
+ // bpf map for GPU memory total data
+ android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap;
+
+ // gpu memory tracepoint event category
+ static constexpr char kGpuMemTraceGroup[] = "gpu_mem";
+ // gpu memory total tracepoint
+ static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total";
+ // pinned gpu memory total bpf c program path in bpf sysfs
+ static constexpr char kGpuMemTotalProgPath[] =
+ "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total";
+ // pinned gpu memory total bpf map path in bpf sysfs
+ static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+};
+
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 538506d..db81f5d 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -19,11 +19,16 @@
address: true,
},
srcs: [
+ "GpuMemTest.cpp",
"GpuStatsTest.cpp",
],
shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
"libcutils",
"libgfxstats",
+ "libgpumem",
"libgraphicsenv",
"liblog",
"libstatslog",
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
new file mode 100644
index 0000000..6ba304c
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2020 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 "gpuservice_unittest"
+
+#include <android-base/stringprintf.h>
+#include <bpf/BpfMap.h>
+#include <gmock/gmock.h>
+#include <gpumem/GpuMem.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "TestableGpuMem.h"
+
+namespace android {
+namespace {
+
+using base::StringPrintf;
+using testing::HasSubstr;
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint64_t TEST_GLOBAL_KEY = 0;
+constexpr uint64_t TEST_GLOBAL_VAL = 123;
+constexpr uint64_t TEST_PROC_KEY_1 = 1;
+constexpr uint64_t TEST_PROC_VAL_1 = 234;
+constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2
+constexpr uint64_t TEST_PROC_VAL_2 = 345;
+
+class GpuMemTest : public testing::Test {
+public:
+ GpuMemTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~GpuMemTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ void SetUp() override {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_EQ(0, bpf::setrlimitForTest());
+
+ mGpuMem = std::make_unique<GpuMem>();
+ mTestableGpuMem = TestableGpuMem(mGpuMem.get());
+ errno = 0;
+ mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC);
+
+ EXPECT_EQ(0, errno);
+ EXPECT_LE(0, mTestMap.getMap().get());
+ EXPECT_TRUE(mTestMap.isValid());
+ }
+
+ std::string dumpsys() {
+ std::string result;
+ Vector<String16> args;
+ mGpuMem->dump(args, &result);
+ return result;
+ }
+
+ std::unique_ptr<GpuMem> mGpuMem;
+ TestableGpuMem mTestableGpuMem;
+ bpf::BpfMap<uint64_t, uint64_t> mTestMap;
+};
+
+TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(),
+ "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total");
+ EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map");
+}
+
+TEST_F(GpuMemTest, bpfInitializationFailed) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n");
+}
+
+TEST_F(GpuMemTest, gpuMemTotalMapEmpty) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n");
+}
+
+TEST_F(GpuMemTest, globalMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(), HasSubstr(StringPrintf("Global total: %" PRIu64 "\n", TEST_GLOBAL_VAL)));
+}
+
+TEST_F(GpuMemTest, missingGlobalMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(), HasSubstr("Global total: N/A"));
+}
+
+TEST_F(GpuMemTest, procMemTotal) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+ ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+ mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+ (uint32_t)(TEST_PROC_KEY_1 >> 32))));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_1,
+ TEST_PROC_VAL_1)));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Memory snapshot for GPU %u:\n",
+ (uint32_t)(TEST_PROC_KEY_2 >> 32))));
+ EXPECT_THAT(dumpsys(),
+ HasSubstr(StringPrintf("Proc %u total: %" PRIu64 "\n", (uint32_t)TEST_PROC_KEY_2,
+ TEST_PROC_VAL_2)));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/TestableGpuMem.h b/services/gpuservice/tests/unittests/TestableGpuMem.h
new file mode 100644
index 0000000..0e4b01c
--- /dev/null
+++ b/services/gpuservice/tests/unittests/TestableGpuMem.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 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 <bpf/BpfMap.h>
+#include <gpumem/GpuMem.h>
+
+namespace android {
+
+class TestableGpuMem {
+public:
+ TestableGpuMem() = default;
+ explicit TestableGpuMem(GpuMem *gpuMem) : mGpuMem(gpuMem) {}
+
+ void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ mGpuMem->setGpuMemTotalMap(map);
+ }
+
+ std::string getGpuMemTraceGroup() { return mGpuMem->kGpuMemTraceGroup; }
+
+ std::string getGpuMemTotalTracepoint() { return mGpuMem->kGpuMemTotalTracepoint; }
+
+ std::string getGpuMemTotalProgPath() { return mGpuMem->kGpuMemTotalProgPath; }
+
+ std::string getGpuMemTotalMapPath() { return mGpuMem->kGpuMemTotalMapPath; }
+
+private:
+ GpuMem *mGpuMem;
+};
+
+} // namespace android
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index a98f4b4..390c6b8 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -22,6 +22,7 @@
filegroup {
name: "libinputdispatcher_sources",
srcs: [
+ "AnrTracker.cpp",
"Connection.cpp",
"Entry.cpp",
"InjectionState.cpp",
diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp
new file mode 100644
index 0000000..c3f611e
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 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 "AnrTracker.h"
+
+namespace android::inputdispatcher {
+
+template <typename T>
+static T max(const T& a, const T& b) {
+ return a < b ? b : a;
+}
+
+void AnrTracker::insert(nsecs_t timeoutTime, sp<IBinder> token) {
+ mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token)));
+}
+
+/**
+ * Erase a single entry only. If there are multiple duplicate entries
+ * (same time, same connection), then only remove one of them.
+ */
+void AnrTracker::erase(nsecs_t timeoutTime, const sp<IBinder>& token) {
+ auto pair = std::make_pair(timeoutTime, token);
+ auto it = mAnrTimeouts.find(pair);
+ if (it != mAnrTimeouts.end()) {
+ mAnrTimeouts.erase(it);
+ }
+}
+
+void AnrTracker::eraseToken(const sp<IBinder>& token) {
+ for (auto it = mAnrTimeouts.begin(); it != mAnrTimeouts.end();) {
+ if (it->second == token) {
+ it = mAnrTimeouts.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+bool AnrTracker::empty() const {
+ return mAnrTimeouts.empty();
+}
+
+// If empty() is false, return the time at which the next connection should cause an ANR
+// If empty() is true, return LONG_LONG_MAX
+nsecs_t AnrTracker::firstTimeout() const {
+ if (mAnrTimeouts.empty()) {
+ return std::numeric_limits<nsecs_t>::max();
+ }
+ return mAnrTimeouts.begin()->first;
+}
+
+const sp<IBinder>& AnrTracker::firstToken() const {
+ return mAnrTimeouts.begin()->second;
+}
+
+void AnrTracker::clear() {
+ mAnrTimeouts.clear();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
new file mode 100644
index 0000000..097dba5
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+
+#include <binder/IBinder.h>
+#include <utils/Timers.h>
+#include <set>
+
+namespace android::inputdispatcher {
+
+/**
+ * Keeps track of the times when each connection is going to ANR.
+ * Provides the ability to quickly find the connection that is going to cause ANR next.
+ */
+class AnrTracker {
+public:
+ void insert(nsecs_t timeoutTime, sp<IBinder> token);
+ void erase(nsecs_t timeoutTime, const sp<IBinder>& token);
+ void eraseToken(const sp<IBinder>& token);
+ void clear();
+
+ bool empty() const;
+ // If empty() is false, return the time at which the next connection should cause an ANR
+ // If empty() is true, return LONG_LONG_MAX
+ nsecs_t firstTimeout() const;
+ // Return the token of the next connection that should cause an ANR.
+ // Do not call this unless empty() is false, you will encounter undefined behaviour.
+ const sp<IBinder>& firstToken() const;
+
+private:
+ // Optimization: use a multiset to keep track of the event timeouts. When an event is sent
+ // to the InputConsumer, we add an entry to this structure. We look at the smallest value to
+ // determine if any of the connections is unresponsive, and to determine when we should wake
+ // next for the future ANR check.
+ // Using a multiset helps quickly look up the next timeout due.
+ //
+ // We must use a multi-set, because it is plausible (although highly unlikely) to have entries
+ // from the same connection and same timestamp, but different sequence numbers.
+ // We are not tracking sequence numbers, and just allow duplicates to exist.
+ std::multiset<std::pair<nsecs_t /*timeoutTime*/, sp<IBinder> /*connectionToken*/>> mAnrTimeouts;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index 188212b..f5ea563 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -26,8 +26,7 @@
inputChannel(inputChannel),
monitor(monitor),
inputPublisher(inputChannel),
- inputState(idGenerator),
- inputPublisherBlocked(false) {}
+ inputState(idGenerator) {}
Connection::~Connection() {}
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index bb3f2fe..3b33f29 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -47,9 +47,10 @@
InputPublisher inputPublisher;
InputState inputState;
- // True if the socket is full and no further events can be published until
- // the application consumes some of the input.
- bool inputPublisherBlocked;
+ // True if this connection is responsive.
+ // If this connection is not responsive, avoid publishing more events to it until the
+ // application consumes some of the input.
+ bool responsive = true;
// Queue of events that need to be published to the connection.
std::deque<DispatchEntry*> outboundQueue;
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 21c8ae1..fdbb1d1 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -28,38 +28,6 @@
namespace android::inputdispatcher {
-static std::string motionActionToString(int32_t action) {
- // Convert MotionEvent action to string
- switch (action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN:
- return "DOWN";
- case AMOTION_EVENT_ACTION_MOVE:
- return "MOVE";
- case AMOTION_EVENT_ACTION_UP:
- return "UP";
- case AMOTION_EVENT_ACTION_CANCEL:
- return "CANCEL";
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- return "POINTER_DOWN";
- case AMOTION_EVENT_ACTION_POINTER_UP:
- return "POINTER_UP";
- }
- return StringPrintf("%" PRId32, action);
-}
-
-static std::string keyActionToString(int32_t action) {
- // Convert KeyEvent action to string
- switch (action) {
- case AKEY_EVENT_ACTION_DOWN:
- return "DOWN";
- case AKEY_EVENT_ACTION_UP:
- return "UP";
- case AKEY_EVENT_ACTION_MULTIPLE:
- return "MULTIPLE";
- }
- return StringPrintf("%" PRId32, action);
-}
-
VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) {
return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
entry.displayId},
@@ -191,7 +159,7 @@
msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
"flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
- deviceId, source, displayId, keyActionToString(action).c_str(), flags,
+ deviceId, source, displayId, KeyEvent::actionToString(action), flags,
keyCode, scanCode, metaState, repeatCount, policyFlags);
}
@@ -253,7 +221,7 @@
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
"xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, source, displayId, motionActionToString(action).c_str(),
+ deviceId, source, displayId, MotionEvent::actionToString(action),
actionButton, flags, metaState, buttonState,
motionClassificationToString(classification), edgeFlags, xPrecision,
yPrecision, xCursorPosition, yCursorPosition);
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index a135409..6b7697d 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -198,7 +198,11 @@
float globalScaleFactor;
float windowXScale = 1.0f;
float windowYScale = 1.0f;
+ // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
+ // and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
+ // An ANR will be triggered if a response for this entry is not received by timeoutTime
+ nsecs_t timeoutTime;
// Set to the resolved ID, action and flags when the event is enqueued.
int32_t resolvedEventId;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8dddd6d..9cb1ee6 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -89,18 +89,17 @@
// before considering it stale and dropping it.
constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
-// Amount of time to allow touch events to be streamed out to a connection before requiring
-// that the first event be finished. This value extends the ANR timeout by the specified
-// amount. For example, if streaming is allowed to get ahead by one second relative to the
-// queue of waiting unfinished events, then ANRs will similarly be delayed by one second.
-constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec
-
// Log a warning when an event takes longer than this to process, even if an ANR does not occur.
constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
// Log a warning when an interception call takes longer than this to process.
constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms;
+// Additional key latency in case a connection is still processing some motion events.
+// This will help with the case when a user touched a button that opens a new window,
+// and gives us the chance to dispatch the key to this new window.
+constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms;
+
// Number of recent events to keep for debugging purposes.
constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
@@ -328,6 +327,18 @@
return dispatchEntry;
}
+static void addGestureMonitors(const std::vector<Monitor>& monitors,
+ std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+ float yOffset = 0) {
+ if (monitors.empty()) {
+ return;
+ }
+ outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
+ for (const Monitor& monitor : monitors) {
+ outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
+ }
+}
+
static std::array<uint8_t, 128> getRandomKey() {
std::array<uint8_t, 128> key;
if (RAND_bytes(key.data(), key.size()) != 1) {
@@ -392,8 +403,7 @@
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
mInTouchMode(true),
- mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
- mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
+ mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
mLooper = new Looper(false);
mReporter = createInputReporter();
@@ -453,6 +463,11 @@
nextWakeupTime = LONG_LONG_MIN;
}
+ // If we are still waiting for ack on some events,
+ // we might have to wake up earlier to check if an app is anr'ing.
+ const nsecs_t nextAnrCheck = processAnrsLocked();
+ nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
+
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LONG_LONG_MAX) {
@@ -466,6 +481,55 @@
mLooper->pollOnce(timeoutMillis);
}
+/**
+ * Check if any of the connections' wait queues have events that are too old.
+ * If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
+ * Return the time at which we should wake up next.
+ */
+nsecs_t InputDispatcher::processAnrsLocked() {
+ const nsecs_t currentTime = now();
+ nsecs_t nextAnrCheck = LONG_LONG_MAX;
+ // Check if we are waiting for a focused window to appear. Raise ANR if waited too long
+ if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
+ if (currentTime >= *mNoFocusedWindowTimeoutTime) {
+ onAnrLocked(mAwaitedFocusedApplication);
+ mAwaitedFocusedApplication.clear();
+ return LONG_LONG_MIN;
+ } else {
+ // Keep waiting
+ const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
+ ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
+ nextAnrCheck = *mNoFocusedWindowTimeoutTime;
+ }
+ }
+
+ // Check if any connection ANRs are due
+ nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
+ if (currentTime < nextAnrCheck) { // most likely scenario
+ return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck
+ }
+
+ // If we reached here, we have an unresponsive connection.
+ sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
+ if (connection == nullptr) {
+ ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
+ return nextAnrCheck;
+ }
+ connection->responsive = false;
+ // Stop waking up for this unresponsive connection
+ mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
+ onAnrLocked(connection);
+ return LONG_LONG_MIN;
+}
+
+nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
+ sp<InputWindowHandle> window = getWindowHandleLocked(token);
+ if (window != nullptr) {
+ return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count();
+ }
+ return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count();
+}
+
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
@@ -529,9 +593,6 @@
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(*mPendingEvent);
}
-
- // Get ready to dispatch the event.
- resetAnrTimeoutsLocked();
}
// Now we have an event to dispatch.
@@ -625,24 +686,57 @@
* Return false otherwise (the default behaviour)
*/
bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
- bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
+ const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
(motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
- if (isPointerDownEvent &&
- mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
- mInputTargetWaitApplicationToken != nullptr) {
+
+ // Optimize case where the current application is unresponsive and the user
+ // decides to touch a window in a different application.
+ // If the application takes too long to catch up then we drop all events preceding
+ // the touch into the other window.
+ if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
int32_t displayId = motionEntry.displayId;
int32_t x = static_cast<int32_t>(
motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = static_cast<int32_t>(
motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
- sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
+ sp<InputWindowHandle> touchedWindowHandle =
+ findTouchedWindowAtLocked(displayId, x, y, nullptr);
if (touchedWindowHandle != nullptr &&
- touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) {
+ touchedWindowHandle->getApplicationToken() !=
+ mAwaitedFocusedApplication->getApplicationToken()) {
// User touched a different application than the one we are waiting on.
- // Flag the event, and start pruning the input queue.
- ALOGI("Pruning input queue because user touched a different application");
+ ALOGI("Pruning input queue because user touched a different application while waiting "
+ "for %s",
+ mAwaitedFocusedApplication->getName().c_str());
return true;
}
+
+ // Alternatively, maybe there's a gesture monitor that could handle this event
+ std::vector<TouchedMonitor> gestureMonitors =
+ findTouchedGestureMonitorsLocked(displayId, {});
+ for (TouchedMonitor& gestureMonitor : gestureMonitors) {
+ sp<Connection> connection =
+ getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
+ if (connection != nullptr && connection->responsive) {
+ // This monitor could take more input. Drop all events preceding this
+ // event, so that gesture monitor could get a chance to receive the stream
+ ALOGW("Pruning the input queue because %s is unresponsive, but we have a "
+ "responsive gesture monitor that may handle the event",
+ mAwaitedFocusedApplication->getName().c_str());
+ return true;
+ }
+ }
+ }
+
+ // Prevent getting stuck: if we have a pending key event, and some motion events that have not
+ // yet been processed by some connections, the dispatcher will wait for these motion
+ // events to be processed before dispatching the key event. This is because these motion events
+ // may cause a new window to be launched, which the user might expect to receive focus.
+ // To prevent waiting forever for such events, just send the key to the currently focused window
+ if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) {
+ ALOGD("Received a new pointer down event, stop waiting for events to process and "
+ "just send the pending key event to the focused window.");
+ mKeyIsWaitingForEventsTimeout = now();
}
return false;
}
@@ -676,10 +770,6 @@
}
case EventEntry::Type::MOTION: {
- // Optimize case where the current application is unresponsive and the user
- // decides to touch a window in a different application.
- // If the application takes too long to catch up then we drop all events preceding
- // the touch into the other window.
if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
mNextUnblockedEvent = entry;
needWake = true;
@@ -710,8 +800,13 @@
}
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
- int32_t y, bool addOutsideTargets,
+ int32_t y, TouchState* touchState,
+ bool addOutsideTargets,
bool addPortalWindows) {
+ if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
+ LOG_ALWAYS_FATAL(
+ "Must provide a valid touch state if adding portal windows or outside targets");
+ }
// Traverse windows from front to back to find touched window.
const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -730,9 +825,9 @@
portalToDisplayId != displayId) {
if (addPortalWindows) {
// For the monitoring channels of the display.
- mTempTouchState.addPortalWindow(windowHandle);
+ touchState->addPortalWindow(windowHandle);
}
- return findTouchedWindowAtLocked(portalToDisplayId, x, y,
+ return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
addOutsideTargets, addPortalWindows);
}
// Found window.
@@ -741,9 +836,9 @@
}
if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
- mTempTouchState.addOrUpdateWindow(windowHandle,
- InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
- BitSet32(0));
+ touchState->addOrUpdateWindow(windowHandle,
+ InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+ BitSet32(0));
}
}
}
@@ -752,7 +847,7 @@
}
std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
- int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const {
std::vector<TouchedMonitor> touchedMonitors;
std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
@@ -766,18 +861,6 @@
return touchedMonitors;
}
-void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors,
- float xOffset, float yOffset) {
- if (monitors.empty()) {
- return;
- }
- outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
- for (const Monitor& monitor : monitors) {
- outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
- }
-}
-
void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
@@ -901,7 +984,6 @@
void InputDispatcher::releasePendingEventLocked() {
if (mPendingEvent) {
- resetAnrTimeoutsLocked();
releaseInboundEventLocked(mPendingEvent);
mPendingEvent = nullptr;
}
@@ -1197,9 +1279,10 @@
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
if (isPointerEvent) {
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
- if (stateIndex >= 0) {
- const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+ std::unordered_map<int32_t, TouchState>::iterator it =
+ mTouchStatesByDisplay.find(entry->displayId);
+ if (it != mTouchStatesByDisplay.end()) {
+ const TouchState& state = it->second;
if (!state.portalWindows.empty()) {
// The event has gone through these portal windows, so we add monitoring targets of
// the corresponding displays as well.
@@ -1278,109 +1361,29 @@
}
}
-int32_t InputDispatcher::handleTargetsNotReadyLocked(
- nsecs_t currentTime, const EventEntry& entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
- if (applicationHandle == nullptr && windowHandle == nullptr) {
- if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
- if (DEBUG_FOCUS) {
- ALOGD("Waiting for system to become ready for input. Reason: %s", reason);
- }
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
- mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
- mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationToken.clear();
- }
- } else {
- if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
- ALOGI("Waiting for application to become ready for input: %s. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
- std::chrono::nanoseconds timeout;
- if (windowHandle != nullptr) {
- timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else if (applicationHandle != nullptr) {
- timeout =
- applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- } else {
- timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
- }
-
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
- mInputTargetWaitStartTime = currentTime;
- mInputTargetWaitTimeoutTime = currentTime + timeout.count();
- mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationToken.clear();
-
- if (windowHandle != nullptr) {
- mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
- }
- if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
- mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
- }
- }
- }
-
- if (mInputTargetWaitTimeoutExpired) {
- return INPUT_EVENT_INJECTION_TIMED_OUT;
- }
-
- if (currentTime >= mInputTargetWaitTimeoutTime) {
- onAnrLocked(currentTime, applicationHandle, windowHandle, entry.eventTime,
- mInputTargetWaitStartTime, reason);
-
- // Force poll loop to wake up immediately on next iteration once we get the
- // ANR response back from the policy.
- *nextWakeupTime = LONG_LONG_MIN;
- return INPUT_EVENT_INJECTION_PENDING;
- } else {
- // Force poll loop to wake up when timeout is due.
- if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
- *nextWakeupTime = mInputTargetWaitTimeoutTime;
- }
- return INPUT_EVENT_INJECTION_PENDING;
+void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) {
+ // We will not be breaking any connections here, even if the policy wants us to abort dispatch.
+ // If the policy decides to close the app, we will get a channel removal event via
+ // unregisterInputChannel, and will clean up the connection that way. We are already not
+ // sending new pointers to the connection when it blocked, but focused events will continue to
+ // pile up.
+ ALOGW("Canceling events for %s because it is unresponsive",
+ connection->inputChannel->getName().c_str());
+ if (connection->status == Connection::STATUS_NORMAL) {
+ CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+ "application not responding");
+ synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
-void InputDispatcher::removeWindowByTokenLocked(const sp<IBinder>& token) {
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
- state.removeWindowByToken(token);
- }
-}
-
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
- nsecs_t timeoutExtension, const sp<IBinder>& inputConnectionToken) {
- if (timeoutExtension > 0) {
- // Extend the timeout.
- mInputTargetWaitTimeoutTime = now() + timeoutExtension;
- } else {
- // Give up.
- mInputTargetWaitTimeoutExpired = true;
-
- // Input state will not be realistic. Mark it out of sync.
- sp<Connection> connection = getConnectionLocked(inputConnectionToken);
- if (connection != nullptr) {
- removeWindowByTokenLocked(inputConnectionToken);
-
- if (connection->status == Connection::STATUS_NORMAL) {
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
- "application not responding");
- synthesizeCancelationEventsForConnectionLocked(connection, options);
- }
- }
- }
-}
-
-void InputDispatcher::resetAnrTimeoutsLocked() {
+void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
if (DEBUG_FOCUS) {
ALOGD("Resetting ANR timeouts.");
}
// Reset input target wait timeout.
- mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
- mInputTargetWaitApplicationToken.clear();
+ mNoFocusedWindowTimeoutTime = std::nullopt;
+ mAwaitedFocusedApplication.clear();
}
/**
@@ -1411,6 +1414,36 @@
return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
}
+bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
+ const char* focusedWindowName) {
+ if (mAnrTracker.empty()) {
+ // already processed all events that we waited for
+ mKeyIsWaitingForEventsTimeout = std::nullopt;
+ return false;
+ }
+
+ if (!mKeyIsWaitingForEventsTimeout.has_value()) {
+ // Start the timer
+ ALOGD("Waiting to send key to %s because there are unprocessed events that may cause "
+ "focus to change",
+ focusedWindowName);
+ mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count();
+ return true;
+ }
+
+ // We still have pending events, and already started the timer
+ if (currentTime < *mKeyIsWaitingForEventsTimeout) {
+ return true; // Still waiting
+ }
+
+ // Waited too long, and some connection still hasn't processed all motions
+ // Just send the key to the focused window
+ ALOGW("Dispatching key to %s even though there are other unprocessed events",
+ focusedWindowName);
+ mKeyIsWaitingForEventsTimeout = std::nullopt;
+ return false;
+}
+
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry& entry,
std::vector<InputTarget>& inputTargets,
@@ -1425,31 +1458,70 @@
// If there is no currently focused window and no focused application
// then drop the event.
- if (focusedWindowHandle == nullptr) {
- if (focusedApplicationHandle != nullptr) {
- return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
- nullptr, nextWakeupTime,
- "Waiting because no window has focus but there is "
- "a focused application that may eventually add a "
- "window when it finishes starting up.");
- }
-
- ALOGI("Dropping event because there is no focused window or focused application in display "
- "%" PRId32 ".",
- displayId);
+ if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
+ ALOGI("Dropping %s event because there is no focused window or focused application in "
+ "display %" PRId32 ".",
+ EventEntry::typeToString(entry.type), displayId);
return INPUT_EVENT_INJECTION_FAILED;
}
+ // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
+ // Only start counting when we have a focused event to dispatch. The ANR is canceled if we
+ // start interacting with another application via touch (app switch). This code can be removed
+ // if the "no focused window ANR" is moved to the policy. Input doesn't know whether
+ // an app is expected to have a focused window.
+ if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
+ if (!mNoFocusedWindowTimeoutTime.has_value()) {
+ // We just discovered that there's no focused window. Start the ANR timer
+ const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
+ DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
+ mNoFocusedWindowTimeoutTime = currentTime + timeout;
+ mAwaitedFocusedApplication = focusedApplicationHandle;
+ ALOGW("Waiting because no window has focus but %s may eventually add a "
+ "window when it finishes starting up. Will wait for %" PRId64 "ms",
+ mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
+ *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
+ return INPUT_EVENT_INJECTION_PENDING;
+ } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
+ // Already raised ANR. Drop the event
+ ALOGE("Dropping %s event because there is no focused window",
+ EventEntry::typeToString(entry.type));
+ return INPUT_EVENT_INJECTION_FAILED;
+ } else {
+ // Still waiting for the focused window
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+ }
+
+ // we have a valid, non-null focused window
+ resetNoFocusedWindowTimeoutLocked();
+
// Check permissions.
if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
}
- // Check whether the window is ready for more input.
- reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused");
- if (!reason.empty()) {
- return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
- focusedWindowHandle, nextWakeupTime, reason.c_str());
+ if (focusedWindowHandle->getInfo()->paused) {
+ ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+
+ // If the event is a key event, then we must wait for all previous events to
+ // complete before delivering it because previous events may have the
+ // side-effect of transferring focus to a different window and we want to
+ // ensure that the following keys are sent to the new window.
+ //
+ // Suppose the user touches a button in a window then immediately presses "A".
+ // If the button causes a pop-up window to appear then we want to ensure that
+ // the "A" key is delivered to the new pop-up window. This is because users
+ // often anticipate pending UI changes when typing on a keyboard.
+ // To obtain this behavior, we must serialize key events with respect to all
+ // prior input events.
+ if (entry.type == EventEntry::Type::KEY) {
+ if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
+ *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
}
// Success! Output targets.
@@ -1461,6 +1533,32 @@
return INPUT_EVENT_INJECTION_SUCCEEDED;
}
+/**
+ * Given a list of monitors, remove the ones we cannot find a connection for, and the ones
+ * that are currently unresponsive.
+ */
+std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked(
+ const std::vector<TouchedMonitor>& monitors) const {
+ std::vector<TouchedMonitor> responsiveMonitors;
+ std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
+ [this](const TouchedMonitor& monitor) REQUIRES(mLock) {
+ sp<Connection> connection = getConnectionLocked(
+ monitor.monitor.inputChannel->getConnectionToken());
+ if (connection == nullptr) {
+ ALOGE("Could not find connection for monitor %s",
+ monitor.monitor.inputChannel->getName().c_str());
+ return false;
+ }
+ if (!connection->responsive) {
+ ALOGW("Unresponsive monitor %s will not get the new gesture",
+ connection->inputChannel->getName().c_str());
+ return false;
+ }
+ return true;
+ });
+ return responsiveMonitors;
+}
+
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const MotionEntry& entry,
std::vector<InputTarget>& inputTargets,
@@ -1484,20 +1582,22 @@
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
sp<InputWindowHandle> newHoverWindowHandle;
- // Copy current touch state into mTempTouchState.
- // This state is always reset at the end of this function, so if we don't find state
- // for the specified display then our initial state will be empty.
+ // Copy current touch state into tempTouchState.
+ // This state will be used to update mTouchStatesByDisplay at the end of this function.
+ // If no state for the specified display exists, then our initial state will be empty.
const TouchState* oldState = nullptr;
- ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (oldStateIndex >= 0) {
- oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
- mTempTouchState.copyFrom(*oldState);
+ TouchState tempTouchState;
+ std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (oldStateIt != mTouchStatesByDisplay.end()) {
+ oldState = &(oldStateIt->second);
+ tempTouchState.copyFrom(*oldState);
}
- bool isSplit = mTempTouchState.split;
- bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
- (mTempTouchState.deviceId != entry.deviceId || mTempTouchState.source != entry.source ||
- mTempTouchState.displayId != displayId);
+ bool isSplit = tempTouchState.split;
+ bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
+ (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
+ tempTouchState.displayId != displayId);
bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
@@ -1507,30 +1607,26 @@
bool wrongDevice = false;
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
- if (DEBUG_FOCUS) {
- ALOGD("Dropping event because a pointer for a different device is already down "
- "in display %" PRId32,
- displayId);
- }
+ if (switchedDevice && tempTouchState.down && !down && !isHoverAction) {
+ ALOGI("Dropping event because a pointer for a different device is already down "
+ "in display %" PRId32,
+ displayId);
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_FAILED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
}
- mTempTouchState.reset();
- mTempTouchState.down = down;
- mTempTouchState.deviceId = entry.deviceId;
- mTempTouchState.source = entry.source;
- mTempTouchState.displayId = displayId;
+ tempTouchState.reset();
+ tempTouchState.down = down;
+ tempTouchState.deviceId = entry.deviceId;
+ tempTouchState.source = entry.source;
+ tempTouchState.displayId = displayId;
isSplit = false;
} else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
- if (DEBUG_FOCUS) {
- ALOGI("Dropping move event because a pointer for a different device is already active "
- "in display %" PRId32,
- displayId);
- }
+ ALOGI("Dropping move event because a pointer for a different device is already active "
+ "in display %" PRId32,
+ displayId);
// TODO: test multiple simultaneous input streams.
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
switchedDevice = false;
@@ -1554,11 +1650,11 @@
}
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/,
- true /*addPortalWindows*/);
+ findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+ isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
std::vector<TouchedMonitor> newGestureMonitors = isDown
- ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
+ ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)
: std::vector<TouchedMonitor>{};
// Figure out whether splitting will be allowed for this window.
@@ -1575,9 +1671,32 @@
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
// Try to assign the pointer to the first foreground window we find, if there is one.
- newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
+ newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
+ if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) {
+ ALOGI("Not sending touch event to %s because it is paused",
+ newTouchedWindowHandle->getName().c_str());
+ newTouchedWindowHandle = nullptr;
+ }
+
+ if (newTouchedWindowHandle != nullptr) {
+ sp<Connection> connection = getConnectionLocked(newTouchedWindowHandle->getToken());
+ if (connection == nullptr) {
+ ALOGI("Could not find connection for %s",
+ newTouchedWindowHandle->getName().c_str());
+ newTouchedWindowHandle = nullptr;
+ } else if (!connection->responsive) {
+ // don't send the new touch to an unresponsive window
+ ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64,
+ newTouchedWindowHandle->getName().c_str(), entry.eventTime);
+ newTouchedWindowHandle = nullptr;
+ }
+ }
+
+ // Also don't send the new touch event to unresponsive gesture monitors
+ newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
+
if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
ALOGI("Dropping event because there is no touchable window or gesture monitor at "
"(%d, %d) in display %" PRId32 ".",
@@ -1611,15 +1730,15 @@
uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
pointerIds.markBit(pointerId);
}
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
- mTempTouchState.addGestureMonitors(newGestureMonitors);
+ tempTouchState.addGestureMonitors(newGestureMonitors);
} else {
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (!mTempTouchState.down) {
+ if (!tempTouchState.down) {
if (DEBUG_FOCUS) {
ALOGD("Dropping event because the pointer is not down or we previously "
"dropped the pointer down event in display %" PRId32,
@@ -1631,14 +1750,14 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
- mTempTouchState.isSlippery()) {
+ tempTouchState.isSlippery()) {
int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
sp<InputWindowHandle> oldTouchedWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle();
sp<InputWindowHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y);
+ findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
if (oldTouchedWindowHandle != newTouchedWindowHandle &&
oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
@@ -1647,9 +1766,9 @@
newTouchedWindowHandle->getName().c_str(), displayId);
}
// Make a slippery exit from the old window.
- mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+ BitSet32(0));
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -1669,7 +1788,7 @@
if (isSplit) {
pointerIds.markBit(entry.pointerProperties[0].id);
}
- mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
}
}
@@ -1681,9 +1800,8 @@
ALOGD("Sending hover exit event to window %s.",
mLastHoverWindowHandle->getName().c_str());
#endif
- mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
}
// Let the new window know that the hover sequence is starting.
@@ -1692,9 +1810,9 @@
ALOGD("Sending hover enter event to window %s.",
newHoverWindowHandle->getName().c_str());
#endif
- mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
+ InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
+ BitSet32(0));
}
}
@@ -1702,7 +1820,7 @@
// is at least one touched foreground window.
{
bool haveForegroundWindow = false;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
haveForegroundWindow = true;
if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
@@ -1712,13 +1830,11 @@
}
}
}
- bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
+ bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();
if (!haveForegroundWindow && !hasGestureMonitor) {
- if (DEBUG_FOCUS) {
- ALOGD("Dropping event because there is no touched foreground window in display "
- "%" PRId32 " or gesture monitor to receive it.",
- displayId);
- }
+ ALOGI("Dropping event because there is no touched foreground window in display "
+ "%" PRId32 " or gesture monitor to receive it.",
+ displayId);
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
@@ -1731,37 +1847,22 @@
// set the policy flag that we will not reveal coordinate information to this window.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle) {
const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
- mTempTouchState.addOrUpdateWindow(inputWindowHandle,
- InputTarget::FLAG_ZERO_COORDS,
- BitSet32(0));
+ tempTouchState.addOrUpdateWindow(inputWindowHandle,
+ InputTarget::FLAG_ZERO_COORDS,
+ BitSet32(0));
}
}
}
}
}
- // Ensure all touched foreground windows are ready for new input.
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
- if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
- // Check whether the window is ready for more input.
- std::string reason =
- checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle,
- entry, "touched");
- if (!reason.empty()) {
- return handleTargetsNotReadyLocked(currentTime, entry, nullptr,
- touchedWindow.windowHandle, nextWakeupTime,
- reason.c_str());
- }
- }
- }
-
// If this is the first pointer going down and the touched window has a wallpaper
// then also add the touched wallpaper windows so they are locked in for the duration
// of the touch gesture.
@@ -1770,7 +1871,7 @@
// to View.onGenericMotionEvent to enable wallpapers to handle these events.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
- mTempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
const std::vector<sp<InputWindowHandle>> windowHandles =
getWindowHandlesLocked(displayId);
@@ -1778,7 +1879,7 @@
const InputWindowInfo* info = windowHandle->getInfo();
if (info->displayId == displayId &&
windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
- mTempTouchState
+ tempTouchState
.addOrUpdateWindow(windowHandle,
InputTarget::FLAG_WINDOW_IS_OBSCURED |
InputTarget::
@@ -1793,19 +1894,19 @@
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, inputTargets);
}
- for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
+ for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
touchedMonitor.yOffset, inputTargets);
}
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
- mTempTouchState.filterNonAsIsTouchWindows();
+ tempTouchState.filterNonAsIsTouchWindows();
Failed:
// Check injection permission once and for all.
@@ -1839,17 +1940,17 @@
}
*outConflictingPointerActions = true;
}
- mTempTouchState.reset();
+ tempTouchState.reset();
if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mTempTouchState.deviceId = entry.deviceId;
- mTempTouchState.source = entry.source;
- mTempTouchState.displayId = displayId;
+ tempTouchState.deviceId = entry.deviceId;
+ tempTouchState.source = entry.source;
+ tempTouchState.displayId = displayId;
}
} else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
- mTempTouchState.reset();
+ tempTouchState.reset();
} else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// First pointer went down.
if (oldState && oldState->down) {
@@ -1864,12 +1965,12 @@
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
- for (size_t i = 0; i < mTempTouchState.windows.size();) {
- TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ for (size_t i = 0; i < tempTouchState.windows.size();) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
touchedWindow.pointerIds.clearBit(pointerId);
if (touchedWindow.pointerIds.isEmpty()) {
- mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i);
+ tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
continue;
}
}
@@ -1881,14 +1982,10 @@
// Save changes unless the action was scroll in which case the temporary touch
// state was only valid for this one action.
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
- if (mTempTouchState.displayId >= 0) {
- if (oldStateIndex >= 0) {
- mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState);
- } else {
- mTouchStatesByDisplay.add(displayId, mTempTouchState);
- }
- } else if (oldStateIndex >= 0) {
- mTouchStatesByDisplay.removeItemsAt(oldStateIndex);
+ if (tempTouchState.displayId >= 0) {
+ mTouchStatesByDisplay[displayId] = tempTouchState;
+ } else {
+ mTouchStatesByDisplay.erase(displayId);
}
}
@@ -1991,16 +2088,11 @@
auto otherInfo = otherHandle->getInfo();
if (!otherInfo->visible) {
return false;
- } else if (info->ownerPid == otherInfo->ownerPid && otherHandle->getToken() == nullptr) {
- // In general, if ownerPid is the same we don't want to generate occlusion
- // events. This line is now necessary since we are including all Surfaces
- // in occlusion calculation, so if we didn't check PID like this SurfaceView
- // would occlude their parents. On the other hand before we started including
- // all surfaces in occlusion calculation and had this line, we would count
- // windows with an input channel from the same PID as occluding, and so we
- // preserve this behavior with the getToken() == null check.
+ } else if (info->ownerPid == otherInfo->ownerPid) {
+ // If ownerPid is the same we don't generate occlusion events as there
+ // is no in-process security boundary.
return false;
- } else if (otherInfo->isTrustedOverlay()) {
+ } else if (otherInfo->trustedOverlay) {
return false;
} else if (otherInfo->displayId != info->displayId) {
return false;
@@ -2017,7 +2109,7 @@
break; // All future windows are below us. Exit early.
}
const InputWindowInfo* otherInfo = otherHandle->getInfo();
- if (canBeObscuredBy(windowHandle, otherHandle) &&
+ if (canBeObscuredBy(windowHandle, otherHandle) &&
otherInfo->frameContainsPoint(x, y)) {
return true;
}
@@ -2033,7 +2125,6 @@
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
}
-
const InputWindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
otherInfo->overlaps(windowInfo)) {
@@ -2043,92 +2134,6 @@
return false;
}
-std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
- nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
- const EventEntry& eventEntry, const char* targetType) {
- // If the window is paused then keep waiting.
- if (windowHandle->getInfo()->paused) {
- return StringPrintf("Waiting because the %s window is paused.", targetType);
- }
-
- // If the window's connection is not registered then keep waiting.
- sp<Connection> connection = getConnectionLocked(windowHandle->getToken());
- if (connection == nullptr) {
- return StringPrintf("Waiting because the %s window's input channel is not "
- "registered with the input dispatcher. The window may be in the "
- "process of being removed.",
- targetType);
- }
-
- // If the connection is dead then keep waiting.
- if (connection->status != Connection::STATUS_NORMAL) {
- return StringPrintf("Waiting because the %s window's input connection is %s."
- "The window may be in the process of being removed.",
- targetType, connection->getStatusLabel());
- }
-
- // If the connection is backed up then keep waiting.
- if (connection->inputPublisherBlocked) {
- return StringPrintf("Waiting because the %s window's input channel is full. "
- "Outbound queue length: %zu. Wait queue length: %zu.",
- targetType, connection->outboundQueue.size(),
- connection->waitQueue.size());
- }
-
- // Ensure that the dispatch queues aren't too far backed up for this event.
- if (eventEntry.type == EventEntry::Type::KEY) {
- // If the event is a key event, then we must wait for all previous events to
- // complete before delivering it because previous events may have the
- // side-effect of transferring focus to a different window and we want to
- // ensure that the following keys are sent to the new window.
- //
- // Suppose the user touches a button in a window then immediately presses "A".
- // If the button causes a pop-up window to appear then we want to ensure that
- // the "A" key is delivered to the new pop-up window. This is because users
- // often anticipate pending UI changes when typing on a keyboard.
- // To obtain this behavior, we must serialize key events with respect to all
- // prior input events.
- if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) {
- return StringPrintf("Waiting to send key event because the %s window has not "
- "finished processing all of the input events that were previously "
- "delivered to it. Outbound queue length: %zu. Wait queue length: "
- "%zu.",
- targetType, connection->outboundQueue.size(),
- connection->waitQueue.size());
- }
- } else {
- // Touch events can always be sent to a window immediately because the user intended
- // to touch whatever was visible at the time. Even if focus changes or a new
- // window appears moments later, the touch event was meant to be delivered to
- // whatever window happened to be on screen at the time.
- //
- // Generic motion events, such as trackball or joystick events are a little trickier.
- // Like key events, generic motion events are delivered to the focused window.
- // Unlike key events, generic motion events don't tend to transfer focus to other
- // windows and it is not important for them to be serialized. So we prefer to deliver
- // generic motion events as soon as possible to improve efficiency and reduce lag
- // through batching.
- //
- // The one case where we pause input event delivery is when the wait queue is piling
- // up with lots of events because the application is not responding.
- // This condition ensures that ANRs are detected reliably.
- if (!connection->waitQueue.empty() &&
- currentTime >=
- connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) {
- return StringPrintf("Waiting to send non-key event because the %s window has not "
- "finished processing certain input events that were delivered to "
- "it over "
- "%0.1fms ago. Wait queue length: %zu. Wait queue head age: "
- "%0.1fms.",
- targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
- connection->waitQueue.size(),
- (currentTime - connection->waitQueue.front()->deliveryTime) *
- 0.000001f);
- }
- }
- return "";
-}
-
std::string InputDispatcher::getApplicationWindowLabel(
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle) {
@@ -2461,6 +2466,9 @@
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
+ const nsecs_t timeout =
+ getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
+ dispatchEntry->timeoutTime = currentTime + timeout;
// Publish the event.
status_t status;
@@ -2580,7 +2588,6 @@
"waiting for the application to catch up",
connection->getInputChannelName().c_str());
#endif
- connection->inputPublisherBlocked = true;
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -2597,6 +2604,10 @@
dispatchEntry));
traceOutboundQueueLength(connection);
connection->waitQueue.push_back(dispatchEntry);
+ if (connection->responsive) {
+ mAnrTracker.insert(dispatchEntry->timeoutTime,
+ connection->inputChannel->getConnectionToken());
+ }
traceWaitQueueLength(connection);
}
}
@@ -2631,8 +2642,6 @@
connection->getInputChannelName().c_str(), seq, toString(handled));
#endif
- connection->inputPublisherBlocked = false;
-
if (connection->status == Connection::STATUS_BROKEN ||
connection->status == Connection::STATUS_ZOMBIE) {
return;
@@ -3043,7 +3052,7 @@
if (newKeyCode != AKEYCODE_UNKNOWN) {
std::scoped_lock _l(mLock);
struct KeyReplacement replacement = {keyCode, deviceId};
- mReplacedKeys.add(replacement, newKeyCode);
+ mReplacedKeys[replacement] = newKeyCode;
keyCode = newKeyCode;
metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
}
@@ -3053,10 +3062,10 @@
// even if the modifier was released between the down and the up events.
std::scoped_lock _l(mLock);
struct KeyReplacement replacement = {keyCode, deviceId};
- ssize_t index = mReplacedKeys.indexOfKey(replacement);
- if (index >= 0) {
- keyCode = mReplacedKeys.valueAt(index);
- mReplacedKeys.removeItemsAt(index);
+ auto replacementIt = mReplacedKeys.find(replacement);
+ if (replacementIt != mReplacedKeys.end()) {
+ keyCode = replacementIt->second;
+ mReplacedKeys.erase(replacementIt);
metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
}
}
@@ -3559,6 +3568,10 @@
sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
const sp<IBinder>& windowHandleToken) const {
+ if (windowHandleToken == nullptr) {
+ return nullptr;
+ }
+
for (auto& it : mWindowHandlesByDisplay) {
const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -3570,21 +3583,22 @@
return nullptr;
}
-bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
- for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
- for (const sp<InputWindowHandle>& handle : windowHandles) {
- if (handle->getToken() == windowHandle->getToken()) {
- if (windowHandle->getInfo()->displayId != it.first) {
- ALOGE("Found window %s in display %" PRId32
- ", but it should belong to display %" PRId32,
- windowHandle->getName().c_str(), it.first,
- windowHandle->getInfo()->displayId);
- }
- return true;
+bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle,
+ int32_t displayId) const {
+ const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+ for (const sp<InputWindowHandle>& handle : windowHandles) {
+ if (handle->getId() == windowHandle->getId() &&
+ handle->getToken() == windowHandle->getToken()) {
+ if (handle->getInfo()->displayId != displayId) {
+ ALOGE("Found window %s in display %" PRId32
+ ", but it should belong to display %" PRId32,
+ windowHandle->getName().c_str(), displayId,
+ windowHandle->getInfo()->displayId);
}
+ return true;
}
}
+
return false;
}
@@ -3738,12 +3752,13 @@
}
}
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (stateIndex >= 0) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ std::unordered_map<int32_t, TouchState>::iterator stateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (stateIt != mTouchStatesByDisplay.end()) {
+ TouchState& state = stateIt->second;
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
- if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
+ if (!hasWindowHandleLocked(touchedWindow.windowHandle, displayId)) {
if (DEBUG_FOCUS) {
ALOGD("Touched window was removed: %s in display %" PRId32,
touchedWindow.windowHandle->getName().c_str(), displayId);
@@ -3767,7 +3782,7 @@
// Otherwise, they might stick around until the window handle is destroyed
// which might not happen until the next GC.
for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
- if (!hasWindowHandleLocked(oldWindowHandle)) {
+ if (!hasWindowHandleLocked(oldWindowHandle, displayId)) {
if (DEBUG_FOCUS) {
ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
}
@@ -3787,15 +3802,17 @@
sp<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+
+ if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
+ inputApplicationHandle != oldFocusedApplicationHandle) {
+ resetNoFocusedWindowTimeoutLocked();
+ }
+
if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
if (oldFocusedApplicationHandle != inputApplicationHandle) {
- if (oldFocusedApplicationHandle != nullptr) {
- resetAnrTimeoutsLocked();
- }
mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
} else if (oldFocusedApplicationHandle != nullptr) {
- resetAnrTimeoutsLocked();
oldFocusedApplicationHandle.clear();
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
@@ -3876,7 +3893,7 @@
if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
if (mDispatchFrozen && !frozen) {
- resetAnrTimeoutsLocked();
+ resetNoFocusedWindowTimeoutLocked();
}
if (mDispatchEnabled && !enabled) {
@@ -3955,8 +3972,8 @@
}
bool found = false;
- for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
- TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+ for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+ TouchState& state = pair.second;
for (size_t i = 0; i < state.windows.size(); i++) {
const TouchedWindow& touchedWindow = state.windows[i];
if (touchedWindow.windowHandle == fromWindowHandle) {
@@ -4016,8 +4033,9 @@
resetKeyRepeatLocked();
releasePendingEventLocked();
drainInboundQueueLocked();
- resetAnrTimeoutsLocked();
+ resetNoFocusedWindowTimeoutLocked();
+ mAnrTracker.clear();
mTouchStatesByDisplay.clear();
mLastHoverWindowHandle.clear();
mReplacedKeys.clear();
@@ -4070,10 +4088,10 @@
dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
}
- if (!mTouchStatesByDisplay.isEmpty()) {
+ if (!mTouchStatesByDisplay.empty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
- for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) {
- const TouchState& state = mTouchStatesByDisplay.valueAt(i);
+ for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+ const TouchState& state = pair.second;
dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
state.displayId, toString(state.down), toString(state.split),
state.deviceId, state.source);
@@ -4136,6 +4154,9 @@
"ms\n",
windowInfo->ownerPid, windowInfo->ownerUid,
ns2ms(windowInfo->dispatchingTimeout));
+ dump += StringPrintf(INDENT4 " flags: %s\n",
+ inputWindowFlagsToString(windowInfo->layoutParamsFlags)
+ .c_str());
}
} else {
dump += INDENT2 "Windows: <none>\n";
@@ -4197,12 +4218,12 @@
dump += INDENT "InboundQueue: <empty>\n";
}
- if (!mReplacedKeys.isEmpty()) {
+ if (!mReplacedKeys.empty()) {
dump += INDENT "ReplacedKeys:\n";
- for (size_t i = 0; i < mReplacedKeys.size(); i++) {
- const KeyReplacement& replacement = mReplacedKeys.keyAt(i);
- int32_t newKeyCode = mReplacedKeys.valueAt(i);
- dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i,
+ for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) {
+ const KeyReplacement& replacement = pair.first;
+ int32_t newKeyCode = pair.second;
+ dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n",
replacement.keyCode, replacement.deviceId, newKeyCode);
}
} else {
@@ -4214,11 +4235,10 @@
for (const auto& pair : mConnectionsByFd) {
const sp<Connection>& connection = pair.second;
dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
- "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
+ "status=%s, monitor=%s, responsive=%s\n",
pair.first, connection->getInputChannelName().c_str(),
connection->getWindowName().c_str(), connection->getStatusLabel(),
- toString(connection->monitor),
- toString(connection->inputPublisherBlocked));
+ toString(connection->monitor), toString(connection->responsive));
if (!connection->outboundQueue.empty()) {
dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
@@ -4419,13 +4439,14 @@
}
int32_t displayId = foundDisplayId.value();
- ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
- if (stateIndex < 0) {
+ std::unordered_map<int32_t, TouchState>::iterator stateIt =
+ mTouchStatesByDisplay.find(displayId);
+ if (stateIt == mTouchStatesByDisplay.end()) {
ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
return BAD_VALUE;
}
- TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+ TouchState& state = stateIt->second;
std::optional<int32_t> foundDeviceId;
for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
@@ -4485,6 +4506,7 @@
}
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+ mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
removeByValue(mConnectionsByFd, connection);
}
@@ -4522,17 +4544,69 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(nsecs_t currentTime,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
- nsecs_t waitStartTime, const char* reason) {
- float dispatchLatency = (currentTime - eventTime) * 0.000001f;
- float waitDuration = (currentTime - waitStartTime) * 0.000001f;
- ALOGI("Application is not responding: %s. "
- "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency,
- waitDuration, reason);
+void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+ // Since we are allowing the policy to extend the timeout, maybe the waitQueue
+ // is already healthy again. Don't raise ANR in this situation
+ if (connection->waitQueue.empty()) {
+ ALOGI("Not raising ANR because the connection %s has recovered",
+ connection->inputChannel->getName().c_str());
+ return;
+ }
+ /**
+ * The "oldestEntry" is the entry that was first sent to the application. That entry, however,
+ * may not be the one that caused the timeout to occur. One possibility is that window timeout
+ * has changed. This could cause newer entries to time out before the already dispatched
+ * entries. In that situation, the newest entries caused ANR. But in all likelihood, the app
+ * processes the events linearly. So providing information about the oldest entry seems to be
+ * most useful.
+ */
+ DispatchEntry* oldestEntry = *connection->waitQueue.begin();
+ const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
+ std::string reason =
+ android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
+ connection->inputChannel->getName().c_str(),
+ ns2ms(currentWait),
+ oldestEntry->eventEntry->getDescription().c_str());
+ updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+ reason);
+
+ std::unique_ptr<CommandEntry> commandEntry =
+ std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+ commandEntry->inputApplicationHandle = nullptr;
+ commandEntry->inputChannel = connection->inputChannel;
+ commandEntry->reason = std::move(reason);
+ postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
+ std::string reason = android::base::StringPrintf("%s does not have a focused window",
+ application->getName().c_str());
+
+ updateLastAnrStateLocked(application, reason);
+
+ std::unique_ptr<CommandEntry> commandEntry =
+ std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+ commandEntry->inputApplicationHandle = application;
+ commandEntry->inputChannel = nullptr;
+ commandEntry->reason = std::move(reason);
+ postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window,
+ const std::string& reason) {
+ const std::string windowLabel = getApplicationWindowLabel(nullptr, window);
+ updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+ const std::string& reason) {
+ const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
+ updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel,
+ const std::string& reason) {
// Capture a record of the InputDispatcher state at the time of the ANR.
time_t t = time(nullptr);
struct tm tm;
@@ -4542,21 +4616,9 @@
mLastAnrState.clear();
mLastAnrState += INDENT "ANR:\n";
mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr);
- mLastAnrState +=
- StringPrintf(INDENT2 "Window: %s\n",
- getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
- mLastAnrState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
- mLastAnrState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
- mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason);
+ mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str());
+ mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str());
dumpDispatchStateLocked(mLastAnrState);
-
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
- commandEntry->inputApplicationHandle = applicationHandle;
- commandEntry->inputChannel =
- windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
- commandEntry->reason = reason;
- postCommandLocked(std::move(commandEntry));
}
void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
@@ -4597,13 +4659,50 @@
mLock.lock();
- resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token);
+ if (timeoutExtension > 0) {
+ extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
+ } else {
+ // stop waking up for events in this connection, it is already not responding
+ sp<Connection> connection = getConnectionLocked(token);
+ if (connection == nullptr) {
+ return;
+ }
+ cancelEventsForAnrLocked(connection);
+ }
+}
+
+void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
+ const sp<IBinder>& connectionToken,
+ nsecs_t timeoutExtension) {
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
+ // Maybe ANR happened because there's no focused window?
+ mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
+ mAwaitedFocusedApplication = application;
+ } else {
+ // It's also possible that the connection already disappeared. No action necessary.
+ }
+ return;
+ }
+
+ ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
+ connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension));
+
+ connection->responsive = true;
+ const nsecs_t newTimeout = now() + timeoutExtension;
+ for (DispatchEntry* entry : connection->waitQueue) {
+ if (newTimeout >= entry->timeoutTime) {
+ // Already removed old entries when connection was marked unresponsive
+ entry->timeoutTime = newTimeout;
+ mAnrTracker.insert(entry->timeoutTime, connectionToken);
+ }
+ }
}
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
KeyEntry* entry = commandEntry->keyEntry;
-
KeyEvent event = createKeyEvent(*entry);
mLock.unlock();
@@ -4637,6 +4736,20 @@
mLock.lock();
}
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+static bool isConnectionResponsive(const Connection& connection) {
+ const nsecs_t currentTime = now();
+ for (const DispatchEntry* entry : connection.waitQueue) {
+ if (entry->timeoutTime < currentTime) {
+ return false;
+ }
+ }
+ return true;
+}
+
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
const nsecs_t finishTime = commandEntry->eventTime;
@@ -4649,7 +4762,6 @@
return;
}
DispatchEntry* dispatchEntry = *dispatchEntryIt;
-
const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
@@ -4678,6 +4790,11 @@
if (dispatchEntryIt != connection->waitQueue.end()) {
dispatchEntry = *dispatchEntryIt;
connection->waitQueue.erase(dispatchEntryIt);
+ mAnrTracker.erase(dispatchEntry->timeoutTime,
+ connection->inputChannel->getConnectionToken());
+ if (!connection->responsive) {
+ connection->responsive = isConnectionResponsive(*connection);
+ }
traceWaitQueueLength(connection);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
connection->outboundQueue.push_front(dispatchEntry);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ff7be87..0f558b4 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUT_DISPATCHER_H
#define _UI_INPUT_DISPATCHER_H
+#include "AnrTracker.h"
#include "CancelationOptions.h"
#include "Entry.h"
#include "InjectionState.h"
@@ -191,6 +192,7 @@
EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+ TouchState* touchState,
bool addOutsideTargets = false,
bool addPortalWindows = false) REQUIRES(mLock);
@@ -214,7 +216,6 @@
std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
REQUIRES(mLock);
-
// Input channels that will receive a copy of all input events sent to the provided display.
std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -255,12 +256,14 @@
bool operator==(const KeyReplacement& rhs) const {
return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
}
- bool operator<(const KeyReplacement& rhs) const {
- return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
+ };
+ struct KeyReplacementHash {
+ size_t operator()(const KeyReplacement& key) const {
+ return std::hash<int32_t>()(key.keyCode) ^ (std::hash<int32_t>()(key.deviceId) << 1);
}
};
// Maps the key code replaced, device id tuple to the key code it was replaced with
- KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
+ std::unordered_map<KeyReplacement, int32_t, KeyReplacementHash> mReplacedKeys GUARDED_BY(mLock);
// Process certain Meta + Key combinations
void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode,
int32_t& metaState);
@@ -270,6 +273,9 @@
bool runCommandsLockedInterruptible() REQUIRES(mLock);
void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+ nsecs_t processAnrsLocked() REQUIRES(mLock);
+ nsecs_t getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
+
// Input filter processing.
bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock);
@@ -295,7 +301,8 @@
sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
REQUIRES(mLock);
sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
- bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+ bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle, int32_t displayId) const
+ REQUIRES(mLock);
/*
* Validate and update InputWindowHandles for a given display.
@@ -308,8 +315,7 @@
std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
GUARDED_BY(mLock);
- KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
- TouchState mTempTouchState GUARDED_BY(mLock);
+ std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
// Focused applications.
std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
@@ -336,38 +342,53 @@
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
- // Keeping track of ANR timeouts.
- enum InputTargetWaitCause {
- INPUT_TARGET_WAIT_CAUSE_NONE,
- INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
- INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
- };
-
- InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
- nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
- nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
- bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
- sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
+ /**
+ * This field is set if there is no focused window, and we have an event that requires
+ * a focused window to be dispatched (for example, a KeyEvent).
+ * When this happens, we will wait until *mNoFocusedWindowTimeoutTime before
+ * dropping the event and raising an ANR for that application.
+ * This is useful if an application is slow to add a focused window.
+ */
+ std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
+ /**
+ * Time to stop waiting for the events to be processed while trying to dispatch a key.
+ * When this time expires, we just send the pending key event to the currently focused window,
+ * without waiting on other events to be processed first.
+ */
+ std::optional<nsecs_t> mKeyIsWaitingForEventsTimeout GUARDED_BY(mLock);
+ bool shouldWaitToSendKeyLocked(nsecs_t currentTime, const char* focusedWindowName)
+ REQUIRES(mLock);
+
+ /**
+ * The focused application at the time when no focused window was present.
+ * Used to raise an ANR when we have no focused window.
+ */
+ sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+
+ // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
+ // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
+ // If a connection is not responsive, then the entries should not be added to the AnrTracker.
+ // Once a connection becomes unresponsive, its entries are removed from AnrTracker to
+ // prevent unneeded wakeups.
+ AnrTracker mAnrTracker GUARDED_BY(mLock);
+ void extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
+ const sp<IBinder>& connectionToken, nsecs_t timeoutExtension)
+ REQUIRES(mLock);
+
// Contains the last window which received a hover event.
sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
- // Finding targets for input events.
- int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry,
- const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle,
- nsecs_t* nextWakeupTime, const char* reason)
- REQUIRES(mLock);
-
- void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
- void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
- const sp<IBinder>& inputConnectionToken)
- REQUIRES(mLock);
+ void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
- void resetAnrTimeoutsLocked() REQUIRES(mLock);
+ // If a focused application changes, we should stop counting down the "no focused window" time,
+ // because we will have no way of knowing when the previous application actually added a window.
+ // This also means that we will miss cases like pulling down notification shade when the
+ // focused application does not have a focused window (no ANR will be raised if notification
+ // shade is pulled down while we are counting down the timeout).
+ void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
int32_t getTargetDisplayId(const EventEntry& entry);
int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
@@ -378,11 +399,10 @@
nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) REQUIRES(mLock);
std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
- int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows)
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
REQUIRES(mLock);
- void addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
- float yOffset = 0);
+ std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
+ const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
@@ -401,11 +421,6 @@
std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle);
- std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
- const sp<InputWindowHandle>& windowHandle,
- const EventEntry& eventEntry,
- const char* targetType) REQUIRES(mLock);
-
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
// with the mutex held makes it easier to ensure that connection invariants are maintained.
@@ -475,9 +490,14 @@
REQUIRES(mLock);
void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
- void onAnrLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
- const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
- nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
+ void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock);
+ void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
+ REQUIRES(mLock);
+ void updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+ const std::string& reason) REQUIRES(mLock);
+ void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
+ REQUIRES(mLock);
// Outbound policy interactions.
void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index cbe0190..b56f356 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -21,6 +21,7 @@
"InputHost.cpp",
],
+ header_libs: ["jni_headers"],
shared_libs: [
"libbinder",
"libcrypto",
@@ -42,6 +43,7 @@
//-fvisibility=hidden
],
+ export_header_lib_headers: ["jni_headers"],
export_include_dirs: ["."],
}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 73d2272..a0d2f4f 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -27,6 +27,7 @@
"libinputflinger_defaults",
],
srcs: [
+ "AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
"TestInputListener.cpp",
diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp
new file mode 100644
index 0000000..b561da1
--- /dev/null
+++ b/services/inputflinger/tests/AnrTracker_test.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 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 "../AnrTracker.h"
+
+#include <binder/Binder.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace inputdispatcher {
+
+// --- AnrTrackerTest ---
+
+/**
+ * Add a single entry and ensure it's returned as first, even if the token isn't valid
+ */
+TEST(AnrTrackerTest, SingleEntry_First) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(tracker.firstToken(), nullptr);
+}
+
+TEST(AnrTrackerTest, MultipleEntries_RemoveToken) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(1, token1);
+ tracker.insert(2, token2);
+ tracker.insert(3, token1);
+ tracker.insert(4, token2);
+ tracker.insert(5, token1);
+
+ tracker.eraseToken(token1);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+}
+
+TEST(AnrTrackerTest, AddAndRemove_Empty) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ tracker.insert(1, nullptr);
+ ASSERT_FALSE(tracker.empty());
+
+ tracker.erase(1, nullptr);
+ ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, Clear) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+ tracker.clear();
+ ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, SingleToken_MaintainsOrder) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ tracker.insert(2, nullptr);
+ tracker.insert(5, nullptr);
+ tracker.insert(0, nullptr);
+
+ ASSERT_EQ(0, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(5, token2);
+ tracker.insert(0, token2);
+
+ ASSERT_EQ(0, tracker.firstTimeout());
+ ASSERT_EQ(token2, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(2, token2);
+ tracker.insert(10, token2);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+ // Doesn't matter which token is returned - both are valid results
+ ASSERT_TRUE(token1 == tracker.firstToken() || token2 == tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) {
+ AnrTracker tracker;
+
+ sp<IBinder> token1 = new BBinder();
+ sp<IBinder> token2 = new BBinder();
+
+ tracker.insert(2, token1);
+ tracker.insert(2, token2);
+ tracker.insert(10, token2);
+
+ tracker.erase(2, token2);
+
+ ASSERT_EQ(2, tracker.firstTimeout());
+ ASSERT_EQ(token1, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, Empty_DoesntCrash) {
+ AnrTracker tracker;
+
+ ASSERT_TRUE(tracker.empty());
+
+ ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout());
+ // Can't call firstToken() if tracker.empty()
+}
+
+TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) {
+ AnrTracker tracker;
+
+ tracker.insert(1, nullptr);
+
+ // Remove with non-matching timestamp
+ tracker.erase(2, nullptr);
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+
+ // Remove with non-matching token
+ tracker.erase(1, new BBinder());
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+
+ // Remove with both non-matching
+ tracker.erase(2, new BBinder());
+ ASSERT_EQ(1, tracker.firstTimeout());
+ ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 13e8354..1a133dc 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -126,6 +126,14 @@
void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
const sp<InputApplicationHandle>& expectedApplication,
const sp<IBinder>& expectedToken) {
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData;
+ ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
+ ASSERT_EQ(expectedApplication, anrData.first);
+ ASSERT_EQ(expectedToken, anrData.second);
+ }
+
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
+ std::chrono::nanoseconds timeout) {
const std::chrono::time_point start = std::chrono::steady_clock::now();
std::unique_lock lock(mLock);
std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
@@ -136,16 +144,33 @@
// before checking if ANR was called.
// Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
// it some time to act. 100ms seems reasonable.
- mNotifyAnr.wait_for(lock, timeToWait,
- [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; });
+ mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
+ return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
+ });
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
- ASSERT_TRUE(mNotifyAnrWasCalled);
+ if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
+ ADD_FAILURE() << "Did not receive ANR callback";
+ }
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
- ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for
- mNotifyAnrWasCalled = false;
- ASSERT_EQ(expectedApplication, mLastAnrApplication);
- ASSERT_EQ(expectedToken, mLastAnrWindowToken);
+ if (std::chrono::abs(timeout - waited) > 100ms) {
+ ADD_FAILURE() << "ANR was raised too early or too late. Expected "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
+ << "ms, but waited "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
+ << "ms instead";
+ }
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> result =
+ std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
+ mAnrApplications.pop();
+ mAnrWindowTokens.pop();
+ return result;
+ }
+
+ void assertNotifyAnrWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mAnrApplications.empty());
+ ASSERT_TRUE(mAnrWindowTokens.empty());
}
void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -153,6 +178,8 @@
mConfig.keyRepeatDelay = delay;
}
+ void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -161,9 +188,8 @@
std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
// ANR handling
- bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false;
- sp<InputApplicationHandle> mLastAnrApplication GUARDED_BY(mLock);
- sp<IBinder> mLastAnrWindowToken GUARDED_BY(mLock);
+ std::queue<sp<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
+ std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
std::chrono::nanoseconds mAnrTimeout = 0ms;
@@ -175,9 +201,8 @@
virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application,
const sp<IBinder>& windowToken, const std::string&) override {
std::scoped_lock lock(mLock);
- mLastAnrApplication = application;
- mLastAnrWindowToken = windowToken;
- mNotifyAnrWasCalled = true;
+ mAnrApplications.push(application);
+ mAnrWindowTokens.push(windowToken);
mNotifyAnr.notify_all();
return mAnrTimeout.count();
}
@@ -643,7 +668,7 @@
ASSERT_NE(nullptr, event) << mName.c_str()
<< ": consumer should have returned non-NULL event.";
ASSERT_EQ(expectedEventType, event->getType())
- << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+ << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
<< " event, got " << inputEventTypeToString(event->getType()) << " event";
EXPECT_EQ(expectedDisplayId, event->getDisplayId());
@@ -688,9 +713,24 @@
void assertNoEvents() {
InputEvent* event = consume();
- ASSERT_EQ(nullptr, event)
- << mName.c_str()
- << ": should not have received any events, so consume() should return NULL";
+ if (event == nullptr) {
+ return;
+ }
+ if (event->getType() == AINPUT_EVENT_TYPE_KEY) {
+ KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
+ ADD_FAILURE() << "Received key event "
+ << KeyEvent::actionToString(keyEvent.getAction());
+ } else if (event->getType() == AINPUT_EVENT_TYPE_MOTION) {
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ ADD_FAILURE() << "Received motion event "
+ << MotionEvent::actionToString(motionEvent.getAction());
+ } else if (event->getType() == AINPUT_EVENT_TYPE_FOCUS) {
+ FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+ ADD_FAILURE() << "Received focus event, hasFocus = "
+ << (focusEvent.getHasFocus() ? "true" : "false");
+ }
+ FAIL() << mName.c_str()
+ << ": should not have received any events, so consume() should return NULL";
}
sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
@@ -754,6 +794,8 @@
mInfo.dispatchingTimeout = timeout.count();
}
+ void setPaused(bool paused) { mInfo.paused = paused; }
+
void setFrame(const Rect& frame) {
mInfo.frameLeft = frame.left;
mInfo.frameTop = frame.top;
@@ -775,6 +817,10 @@
expectedFlags);
}
+ void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags);
+ }
+
void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
@@ -826,12 +872,12 @@
expectedFlags);
}
- std::optional<uint32_t> receiveEvent() {
+ std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
if (mInputReceiver == nullptr) {
ADD_FAILURE() << "Invalid receive event on window with no receiver";
return std::nullopt;
}
- return mInputReceiver->receiveEvent();
+ return mInputReceiver->receiveEvent(outEvent);
}
void finishEvent(uint32_t sequenceNum) {
@@ -865,7 +911,9 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
- int32_t displayId = ADISPLAY_ID_NONE) {
+ int32_t displayId = ADISPLAY_ID_NONE,
+ int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -875,10 +923,9 @@
repeatCount, currentTime, currentTime);
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
+ injectionTimeout,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -886,11 +933,19 @@
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
}
+static int32_t injectKeyUp(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
+ return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
+}
+
static int32_t injectMotionEvent(
const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
const PointF& position,
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION}) {
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+ int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
MotionEvent event;
PointerProperties pointerProperties[1];
PointerCoords pointerCoords[1];
@@ -903,7 +958,6 @@
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
- nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion down event.
event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action,
/* actionButton */ 0,
@@ -911,14 +965,13 @@
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
/* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
/* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
- currentTime, currentTime,
+ eventTime, eventTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
// Inject event until dispatch out.
- return dispatcher->injectInputEvent(
- &event,
- INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
+ injectionTimeout,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
@@ -1429,6 +1482,10 @@
expectedDisplayId, expectedFlags);
}
+ std::optional<int32_t> receiveEvent() { return mInputReceiver->receiveEvent(); }
+
+ void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+
void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
expectedDisplayId, expectedFlags);
@@ -1507,6 +1564,21 @@
monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
+TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
+ ASSERT_TRUE(consumeSeq);
+
+ mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken());
+ monitor.finishEvent(*consumeSeq);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
TEST_F(InputDispatcherTest, TestMoveEvent) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window =
@@ -2329,23 +2401,40 @@
}
};
+// Send a tap and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) {
+ tapOnWindow();
+ mWindow->consumeMotionDown();
+ mWindow->consumeMotionUp();
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Send a regular key and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
// Send an event to the app and have the app not respond right away.
-// Make sure that ANR is raised
+// When ANR is raised, policy will tell the dispatcher to cancel the events for that window.
+// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
- // Also, overwhelm the socket to make sure ANR starts
- for (size_t i = 0; i < 100; i++) {
- injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i});
- }
-
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+
+ // The remaining lines are not really needed for the test, but kept as a sanity check
+ mWindow->finishEvent(*sequenceNum);
+ mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
ASSERT_TRUE(mDispatcher->waitForIdle());
}
@@ -2355,14 +2444,591 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
-
- // Start ANR process by sending a 2nd key, which would trigger the check for whether
- // waitQueue is empty
- injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1);
-
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken());
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
ASSERT_TRUE(mDispatcher->waitForIdle());
}
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ // taps on the window work as normal
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+ mDispatcher->waitForIdle();
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ // Once a focused event arrives, we get an ANR for this application
+ // We specify the injection timeout to be smaller than the application timeout, to ensure that
+ // injection times out (instead of failing).
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+// If the policy wants to keep waiting on the focused window to be added, make sure
+// that this timeout extension is honored and ANR is raised again.
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+ const std::chrono::duration timeout = 5ms;
+ mFakePolicy->setAnrTimeout(timeout);
+
+ // Once a focused event arrives, we get an ANR for this application
+ // We specify the injection timeout to be smaller than the application timeout, to ensure that
+ // injection times out (instead of failing).
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ const std::chrono::duration appTimeout =
+ mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
+
+ // After the extended time has passed, ANR should be raised again
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+ // If we stop extending the timeout, dispatcher should go to idle.
+ // Another ANR may be raised during this time
+ mFakePolicy->setAnrTimeout(0ms);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
+ mWindow->setFocus(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ // Once a focused event arrives, we get an ANR for this application
+ const int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+
+ const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+ // Future focused events get dropped right away
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mWindow->assertNoEvents();
+}
+
+/**
+ * Ensure that the implementation is valid. Since we are using multiset to keep track of the
+ * ANR timeouts, we are allowing entries with identical timestamps in the same connection.
+ * If we process 1 of the events, but ANR on the second event with the same timestamp,
+ * the ANR mechanism should still work.
+ *
+ * In this test, we are injecting DOWN and UP events with the same timestamps, and acknowledging the
+ * DOWN event, while not responding on the second one.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
+ nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+ {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+
+ // Now send ACTION_UP, with identical timestamp
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+ {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION},
+ 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+
+ // We have now sent down and up. Let's consume first event and then ANR on the second.
+ mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+}
+
+// If an app is not responding to a key event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+
+ // Stuck on the ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+ // New tap will go to the gesture monitor, but not to the window
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+ monitor.assertNoEvents();
+}
+
+// If an app is not responding to a motion event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) {
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeMotionDown();
+ // Stuck on the ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+ // New tap will go to the gesture monitor, but not to the window
+ tapOnWindow();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+ mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+ monitor.assertNoEvents();
+}
+
+// If a window is unresponsive, then you get anr. if the window later catches up and starts to
+// process events, you don't get an anr. When the window later becomes unresponsive again, you
+// get an ANR again.
+// 1. tap -> block on ACTION_UP -> receive ANR
+// 2. consume all pending events (= queue becomes healthy again)
+// 3. tap again -> block on ACTION_UP again -> receive ANR second time
+TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) {
+ tapOnWindow();
+
+ mWindow->consumeMotionDown();
+ // Block on ACTION_UP
+ const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mWindow->consumeMotionUp(); // Now the connection should be healthy again
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+
+ tapOnWindow();
+ mWindow->consumeMotionDown();
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mWindow->consumeMotionUp();
+
+ mDispatcher->waitForIdle();
+ mWindow->assertNoEvents();
+}
+
+// If the policy tells us to raise ANR again after some time, ensure that the timeout extension
+// is honored
+TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) {
+ const std::chrono::duration timeout = 5ms;
+ mFakePolicy->setAnrTimeout(timeout);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ WINDOW_LOCATION));
+
+ const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/,
+ mWindow->getToken());
+
+ // Since the policy wanted to extend ANR, make sure it is called again after the extension
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+ mFakePolicy->setAnrTimeout(0ms);
+ std::this_thread::sleep_for(windowTimeout);
+ // We are not checking if ANR has been called, because it may have been called again by the
+ // time we set the timeout to 0
+
+ // When the policy finally says stop, we should get ACTION_CANCEL
+ mWindow->consumeMotionDown();
+ mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ mWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
+ mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+ tapOnWindow();
+ std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection will "succeed" because we will eventually give up and send the key to the focused
+ // window even if motions are still being processed. But because the injection timeout is short,
+ // we will receive INJECTION_TIMED_OUT as the result.
+
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ // Key will not be sent to the window, yet, because the window is still processing events
+ // and the key remains pending, waiting for the touch events to be processed
+ std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ std::this_thread::sleep_for(500ms);
+ // if we wait long enough though, dispatcher will give up, and still send the key
+ // to the focused window, even though we have not yet finished the motion event
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mWindow->finishEvent(*downSequenceNum);
+ mWindow->finishEvent(*upSequenceNum);
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not go to the focused window until the motion is processed.
+ * If then a new motion comes in, then the pending key event should be going to the currently
+ * focused window right away.
+ */
+TEST_F(InputDispatcherSingleWindowAnr,
+ PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) {
+ mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+ tapOnWindow();
+ std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection is async, so it will succeed
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE));
+ // At this point, key is still pending, and should not be sent to the application yet.
+ std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ // Now tap down again. It should cause the pending key to go to the focused window right away.
+ tapOnWindow();
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd
+ // the other events yet. We can finish events in any order.
+ mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN
+ mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP
+ mWindow->consumeMotionDown();
+ mWindow->consumeMotionUp();
+ mWindow->assertNoEvents();
+}
+
+class InputDispatcherMultiWindowAnr : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApplication = new FakeApplicationHandle();
+ mApplication->setDispatchingTimeout(10ms);
+ mUnfocusedWindow =
+ new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT);
+ mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+ // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+ // window.
+ // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
+ mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+ InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH |
+ InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+ mFocusedWindow =
+ new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->setDispatchingTimeout(10ms);
+ mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+ mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+ InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+ // Set focused application.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+ mFocusedWindow->setFocus(true);
+
+ // Expect one focus window exist in display.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(true);
+ }
+
+ virtual void TearDown() override {
+ InputDispatcherTest::TearDown();
+
+ mUnfocusedWindow.clear();
+ mFocusedWindow.clear();
+ }
+
+protected:
+ sp<FakeApplicationHandle> mApplication;
+ sp<FakeWindowHandle> mUnfocusedWindow;
+ sp<FakeWindowHandle> mFocusedWindow;
+ static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20};
+ static constexpr PointF FOCUSED_WINDOW_LOCATION = {75, 75};
+ static constexpr PointF LOCATION_OUTSIDE_ALL_WINDOWS = {40, 40};
+
+ void tapOnFocusedWindow() { tap(FOCUSED_WINDOW_LOCATION); }
+
+ void tapOnUnfocusedWindow() { tap(UNFOCUSED_WINDOW_LOCATION); }
+
+private:
+ void tap(const PointF& location) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ location));
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ location));
+ }
+};
+
+// If we have 2 windows that are both unresponsive, the one with the shortest timeout
+// should be ANR'd first.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mFocusedWindow->consumeMotionDown();
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ // We consumed all events, so no ANR
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(unfocusedSequenceNum);
+ std::optional<uint32_t> focusedSequenceNum = mFocusedWindow->receiveEvent();
+ ASSERT_TRUE(focusedSequenceNum);
+
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ mFocusedWindow->finishEvent(*focusedSequenceNum);
+ mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// If we have 2 windows with identical timeouts that are both unresponsive,
+// it doesn't matter which order they should have ANR.
+// But we should receive ANR for both.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) {
+ // Set the timeout for unfocused window to match the focused window
+ mUnfocusedWindow->setDispatchingTimeout(10ms);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+ tapOnFocusedWindow();
+ // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData1 =
+ mFakePolicy->getNotifyAnrData(10ms);
+ std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData2 =
+ mFakePolicy->getNotifyAnrData(0ms);
+
+ // We don't know which window will ANR first. But both of them should happen eventually.
+ ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second ||
+ mFocusedWindow->getToken() == anrData2.second);
+ ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second ||
+ mUnfocusedWindow->getToken() == anrData2.second);
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If a window is already not responding, the second tap on the same window should be ignored.
+// We should also log an error to account for the dropped event (not tested here).
+// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events.
+TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
+ tapOnFocusedWindow();
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+ // Receive the events, but don't respond
+ std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN
+ ASSERT_TRUE(downEventSequenceNum);
+ std::optional<uint32_t> upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP
+ ASSERT_TRUE(upEventSequenceNum);
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ // Tap once again
+ // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+ // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
+ // valid touch target
+ mUnfocusedWindow->assertNoEvents();
+
+ // Consume the first tap
+ mFocusedWindow->finishEvent(*downEventSequenceNum);
+ mFocusedWindow->finishEvent(*upEventSequenceNum);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // The second tap did not go to the focused window
+ mFocusedWindow->assertNoEvents();
+ // should not have another ANR after the window just became healthy again
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If you tap outside of all windows, there will not be ANR
+TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ LOCATION_OUTSIDE_ALL_WINDOWS));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Since the focused window is paused, tapping on it should not produce any events
+TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
+ mFocusedWindow->setPaused(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ FOCUSED_WINDOW_LOCATION));
+
+ std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // Should not ANR because the window is paused, and touches shouldn't go to it
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+
+ mFocusedWindow->assertNoEvents();
+ mUnfocusedWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ * If a different window becomes focused at this time, the key should go to that window instead.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) {
+ // Set a long ANR timeout to prevent it from triggering
+ mFocusedWindow->setDispatchingTimeout(2s);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+ tapOnUnfocusedWindow();
+ std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(downSequenceNum);
+ std::optional<uint32_t> upSequenceNum = mUnfocusedWindow->receiveEvent();
+ ASSERT_TRUE(upSequenceNum);
+ // Don't finish the events yet, and send a key
+ // Injection will succeed because we will eventually give up and send the key to the focused
+ // window even if motions are still being processed.
+
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+ // Key will not be sent to the window, yet, because the window is still processing events
+ // and the key remains pending, waiting for the touch events to be processed
+ std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
+ ASSERT_FALSE(keySequenceNum);
+
+ // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
+ mFocusedWindow->setFocus(false);
+ mUnfocusedWindow->setFocus(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+ // Focus events should precede the key events
+ mUnfocusedWindow->consumeFocusEvent(true);
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Finish the tap events, which should unblock dispatcher
+ mUnfocusedWindow->finishEvent(*downSequenceNum);
+ mUnfocusedWindow->finishEvent(*upSequenceNum);
+
+ // Now that all queues are cleared and no backlog in the connections, the key event
+ // can finally go to the newly focused "mUnfocusedWindow".
+ mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->assertNoEvents();
+ mUnfocusedWindow->assertNoEvents();
+}
+
+// When the touch stream is split across 2 windows, and one of them does not respond,
+// then ANR should be raised and the touch should be canceled for the unresponsive window.
+// The other window should not be affected by that.
+TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) {
+ // Touch Window 1
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+ mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+ ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+ motionArgs =
+ generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ const std::chrono::duration timeout =
+ mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+ mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+ mFocusedWindow->getToken());
+
+ mUnfocusedWindow->consumeMotionDown();
+ mFocusedWindow->consumeMotionDown();
+ // Focused window may or may not receive ACTION_MOVE
+ // But it should definitely receive ACTION_CANCEL due to the ANR
+ InputEvent* event;
+ std::optional<int32_t> moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event);
+ ASSERT_TRUE(moveOrCancelSequenceNum);
+ mFocusedWindow->finishEvent(*moveOrCancelSequenceNum);
+ ASSERT_NE(nullptr, event);
+ ASSERT_EQ(event->getType(), AINPUT_EVENT_TYPE_MOTION);
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ if (motionEvent.getAction() == AMOTION_EVENT_ACTION_MOVE) {
+ mFocusedWindow->consumeMotionCancel();
+ } else {
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
+ }
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mUnfocusedWindow->assertNoEvents();
+ mFocusedWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index b0d3e3b..ec3dfc8 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -2,9 +2,14 @@
name: "libpowermanager",
srcs: [
- "IPowerManager.cpp",
- "Temperature.cpp",
+ "BatterySaverPolicyConfig.cpp",
"CoolingDevice.cpp",
+ "PowerHalController.cpp",
+ "PowerHalLoader.cpp",
+ "PowerHalWrapper.cpp",
+ "PowerSaveState.cpp",
+ "Temperature.cpp",
+ "WorkSource.cpp",
":libpowermanager_aidl",
],
@@ -17,9 +22,13 @@
},
shared_libs: [
- "libutils",
"libbinder",
- "liblog"
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-cpp",
],
cflags: [
@@ -34,22 +43,3 @@
"include",
],
}
-
-cc_test {
- name: "thermalmanager-test",
- srcs: ["IThermalManagerTest.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
- shared_libs: [
- "libbase",
- "libhidlbase",
- "liblog",
- "libpowermanager",
- "libbinder",
- "libutils",
- ],
-}
diff --git a/services/powermanager/BatterySaverPolicyConfig.cpp b/services/powermanager/BatterySaverPolicyConfig.cpp
new file mode 100644
index 0000000..ee55b6b
--- /dev/null
+++ b/services/powermanager/BatterySaverPolicyConfig.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BatterySaverPolicyConfig"
+
+#include <android/BatterySaverPolicyConfig.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t BatterySaverPolicyConfig::readDeviceSpecificSettings(const android::Parcel *parcel) {
+ int32_t num = 0;
+ status_t ret = parcel->readInt32(&num);
+ if (ret != OK) {
+ return ret;
+ }
+ for (int i = 0; i < num; i++) {
+ String16 key, val;
+ ret = parcel->readString16(&key) ?:
+ parcel->readString16(&val);
+ if (ret != OK) {
+ return ret;
+ }
+ mDeviceSpecificSettings.emplace_back(key, val);
+ }
+ return ret;
+}
+
+status_t BatterySaverPolicyConfig::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readFloat(&mAdjustBrightnessFactor)
+ ?: parcel->readBool(&mAdvertiseIsEnabled)
+ ?: parcel->readBool(&mDeferFullBackup)
+ ?: parcel->readBool(&mDeferKeyValueBackup)
+ ?: readDeviceSpecificSettings(parcel)
+ ?: parcel->readBool(&mDisableAnimation)
+ ?: parcel->readBool(&mDisableAod)
+ ?: parcel->readBool(&mDisableLaunchBoost)
+ ?: parcel->readBool(&mDisableOptionalSensors)
+ ?: parcel->readBool(&mDisableSoundTrigger)
+ ?: parcel->readBool(&mDisableVibration)
+ ?: parcel->readBool(&mEnableAdjustBrightness)
+ ?: parcel->readBool(&mEnableDataSaver)
+ ?: parcel->readBool(&mEnableFirewall)
+ ?: parcel->readBool(&mEnableNightMode)
+ ?: parcel->readBool(&mEnableQuickDoze)
+ ?: parcel->readBool(&mForceAllAppsStandby)
+ ?: parcel->readBool(&mForceBackgroundCheck)
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode));
+}
+
+status_t BatterySaverPolicyConfig::writeDeviceSpecificSettings(android::Parcel *parcel) const {
+ status_t ret = parcel->writeInt32(mDeviceSpecificSettings.size());
+ if (ret != OK) {
+ return ret;
+ }
+ for (auto& settings : mDeviceSpecificSettings) {
+ ret = parcel->writeString16(settings.first) ?:
+ parcel->writeString16(settings.second);
+ if (ret != OK) {
+ return ret;
+ }
+ }
+ return ret;
+}
+
+status_t BatterySaverPolicyConfig::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeFloat(mAdjustBrightnessFactor)
+ ?: parcel->writeBool(mAdvertiseIsEnabled)
+ ?: parcel->writeBool(mDeferFullBackup)
+ ?: parcel->writeBool(mDeferKeyValueBackup)
+ ?: writeDeviceSpecificSettings(parcel)
+ ?: parcel->writeBool(mDisableAnimation)
+ ?: parcel->writeBool(mDisableAod)
+ ?: parcel->writeBool(mDisableLaunchBoost)
+ ?: parcel->writeBool(mDisableOptionalSensors)
+ ?: parcel->writeBool(mDisableSoundTrigger)
+ ?: parcel->writeBool(mDisableVibration)
+ ?: parcel->writeBool(mEnableAdjustBrightness)
+ ?: parcel->writeBool(mEnableDataSaver)
+ ?: parcel->writeBool(mEnableFirewall)
+ ?: parcel->writeBool(mEnableNightMode)
+ ?: parcel->writeBool(mEnableQuickDoze)
+ ?: parcel->writeBool(mForceAllAppsStandby)
+ ?: parcel->writeBool(mForceBackgroundCheck)
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode));
+}
+
+} // namespace android::os
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
deleted file mode 100644
index ea3a831..0000000
--- a/services/powermanager/IPowerManager.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IPowerManager"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-
-#include <powermanager/IPowerManager.h>
-
-namespace android {
-
-class BpPowerManager : public BpInterface<IPowerManager>
-{
-public:
- explicit BpPowerManager(const sp<IBinder>& impl)
- : BpInterface<IPowerManager>(impl)
- {
- }
-
- virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- data.writeString16(tag);
- data.writeString16(packageName);
- data.writeInt32(0); // no WorkSource
- data.writeString16(NULL, 0); // no history tag
- return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
- const String16& packageName, int uid, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
-
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- data.writeString16(tag);
- data.writeString16(packageName);
- data.writeInt32(uid); // uid to blame for the work
- return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeStrongBinder(lock);
- data.writeInt32(flags);
- return remote()->transact(RELEASE_WAKE_LOCK, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids,
- bool isOneWay) {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeStrongBinder(lock);
- data.writeInt32Array(len, uids);
- return remote()->transact(UPDATE_WAKE_LOCK_UIDS, data, &reply,
- isOneWay ? IBinder::FLAG_ONEWAY : 0);
- }
-
- virtual status_t powerHint(int hintId, int param)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(hintId);
- data.writeInt32(param);
- // This FLAG_ONEWAY is in the .aidl, so there is no way to disable it
- return remote()->transact(POWER_HINT, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt64(event_time_ms);
- data.writeInt32(reason);
- data.writeInt32(flags);
- return remote()->transact(GO_TO_SLEEP, data, &reply, 0);
- }
-
- virtual status_t reboot(bool confirm, const String16& reason, bool wait)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(confirm);
- data.writeString16(reason);
- data.writeInt32(wait);
- return remote()->transact(REBOOT, data, &reply, 0);
- }
-
- virtual status_t shutdown(bool confirm, const String16& reason, bool wait)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeInt32(confirm);
- data.writeString16(reason);
- data.writeInt32(wait);
- return remote()->transact(SHUTDOWN, data, &reply, 0);
- }
-
- virtual status_t crash(const String16& message)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
- data.writeString16(message);
- return remote()->transact(CRASH, data, &reply, 0);
- }
-};
-
-IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager");
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
new file mode 100644
index 0000000..178f545
--- /dev/null
+++ b/services/powermanager/PowerHalController.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalController"
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHalLoader.h>
+#include <utils/Log.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+std::unique_ptr<HalWrapper> HalConnector::connect() {
+ sp<IPower> halAidl = PowerHalLoader::loadAidl();
+ if (halAidl) {
+ return std::make_unique<AidlHalWrapper>(halAidl);
+ }
+ sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
+ sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
+ if (halHidlV1_1) {
+ return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
+ }
+ if (halHidlV1_0) {
+ return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0);
+ }
+ return nullptr;
+}
+
+void HalConnector::reset() {
+ PowerHalLoader::unloadAll();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalController::init() {
+ initHal();
+}
+
+// Check validity of current handle to the power HAL service, and create a new
+// one if necessary.
+std::shared_ptr<HalWrapper> PowerHalController::initHal() {
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ if (mConnectedHal == nullptr) {
+ mConnectedHal = mHalConnector->connect();
+ if (mConnectedHal == nullptr) {
+ // Unable to connect to Power HAL service. Fallback to default.
+ return mDefaultHal;
+ }
+ }
+ return mConnectedHal;
+}
+
+// Check if a call to Power HAL function failed; if so, log the failure and
+// invalidate the current Power HAL handle.
+HalResult PowerHalController::processHalResult(HalResult result, const char* fnName) {
+ if (result == HalResult::FAILED) {
+ ALOGE("%s() failed: power HAL service not available.", fnName);
+ std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+ // Drop Power HAL handle. This will force future api calls to reconnect.
+ mConnectedHal = nullptr;
+ mHalConnector->reset();
+ }
+ return result;
+}
+
+HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->setBoost(boost, durationMs);
+ return processHalResult(result, "setBoost");
+}
+
+HalResult PowerHalController::setMode(Mode mode, bool enabled) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ auto result = handle->setMode(mode, enabled);
+ return processHalResult(result, "setMode");
+}
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
new file mode 100644
index 0000000..1f1b43a
--- /dev/null
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalLoader"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <powermanager/PowerHalLoader.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T, typename F>
+sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) {
+ if (!exists) {
+ return nullptr;
+ }
+ if (hal) {
+ return hal;
+ }
+ hal = loadFn();
+ if (hal) {
+ ALOGV("Successfully connected to Power HAL %s service.", halName);
+ } else {
+ ALOGV("Power HAL %s service not available.", halName);
+ exists = false;
+ }
+ return hal;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::mutex PowerHalLoader::gHalMutex;
+sp<IPower> PowerHalLoader::gHalAidl = nullptr;
+sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
+sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
+
+void PowerHalLoader::unloadAll() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ gHalAidl = nullptr;
+ gHalHidlV1_0 = nullptr;
+ gHalHidlV1_1 = nullptr;
+}
+
+sp<IPower> PowerHalLoader::loadAidl() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return waitForVintfService<IPower>(); };
+ return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ return loadHidlV1_0Locked();
+}
+
+sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_1::IPower::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
+}
+
+sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() {
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_0::IPower::getService(); };
+ return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
new file mode 100644
index 0000000..5f4bfed
--- /dev/null
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HalWrapper"
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using namespace android::hardware::power;
+
+namespace android {
+
+namespace power {
+
+// -------------------------------------------------------------------------------------------------
+
+inline HalResult toHalResult(const binder::Status& result) {
+ return result.isOk() ? HalResult::SUCCESSFUL : HalResult::FAILED;
+}
+
+template <typename T>
+inline HalResult toHalResult(const hardware::Return<T>& result) {
+ return result.isOk() ? HalResult::SUCCESSFUL : HalResult::FAILED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+ ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
+ toString(boost).c_str(), durationMs);
+ return HalResult::UNSUPPORTED;
+}
+
+HalResult EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+ ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
+ enabled ? "true" : "false");
+ return HalResult::UNSUPPORTED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
+ if (boost == Boost::INTERACTION) {
+ return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
+ } else {
+ ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+}
+
+HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+ uint32_t data = enabled ? 1 : 0;
+ switch (mode) {
+ case Mode::LAUNCH:
+ return sendPowerHint(V1_0::PowerHint::LAUNCH, data);
+ case Mode::LOW_POWER:
+ return sendPowerHint(V1_0::PowerHint::LOW_POWER, data);
+ case Mode::SUSTAINED_PERFORMANCE:
+ return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data);
+ case Mode::VR:
+ return sendPowerHint(V1_0::PowerHint::VR_MODE, data);
+ case Mode::INTERACTIVE:
+ return setInteractive(enabled);
+ case Mode::DOUBLE_TAP_TO_WAKE:
+ return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled);
+ default:
+ ALOGV("Skipped setMode %s because Power HAL AIDL not available",
+ toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+}
+
+HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ return toHalResult(mHandleV1_0->powerHint(hintId, data));
+}
+
+HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) {
+ return toHalResult(mHandleV1_0->setInteractive(enabled));
+}
+
+HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
+ return toHalResult(mHandleV1_0->setFeature(feature, enabled));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+ return toHalResult(mHandleV1_1->powerHintAsync(hintId, data));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+ std::unique_lock<std::mutex> lock(mBoostMutex);
+ // Quick return if boost is not supported by HAL
+ if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
+ mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
+ ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+
+ if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
+ if (!isSupportedRet.isOk()) {
+ ALOGV("Skipped setBoost %s because Power HAL is not available to check "
+ "support",
+ toString(boost).c_str());
+ return HalResult::FAILED;
+ }
+
+ mBoostSupportedArray[static_cast<int32_t>(boost)] =
+ isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
+ toString(boost).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+ }
+ lock.unlock();
+
+ return toHalResult(mHandle->setBoost(boost, durationMs));
+}
+
+HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) {
+ std::unique_lock<std::mutex> lock(mModeMutex);
+ // Quick return if mode is not supported by HAL
+ if (mode > Mode::DISPLAY_INACTIVE ||
+ mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
+ ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+
+ if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
+ if (!isSupportedRet.isOk()) {
+ ALOGV("Skipped setMode %s because Power HAL is not available to check "
+ "support",
+ toString(mode).c_str());
+ return HalResult::FAILED;
+ }
+
+ mModeSupportedArray[static_cast<int32_t>(mode)] =
+ isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setMode %s because Power HAL doesn't support it",
+ toString(mode).c_str());
+ return HalResult::UNSUPPORTED;
+ }
+ }
+ lock.unlock();
+
+ return toHalResult(mHandle->setMode(mode, enabled));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace power
+
+} // namespace android
diff --git a/services/powermanager/PowerSaveState.cpp b/services/powermanager/PowerSaveState.cpp
new file mode 100644
index 0000000..6d1830a
--- /dev/null
+++ b/services/powermanager/PowerSaveState.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerSaveState"
+
+#include <android/PowerSaveState.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t PowerSaveState::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->readBool(&mBatterySaverEnabled)
+ ?: parcel->readBool(&mGlobalBatterySaverEnabled)
+ ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode))
+ ?: parcel->readFloat(&mBrightnessFactor);
+}
+
+status_t PowerSaveState::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeBool(mBatterySaverEnabled)
+ ?: parcel->writeBool(mGlobalBatterySaverEnabled)
+ ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode))
+ ?: parcel->writeFloat(mBrightnessFactor);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/TEST_MAPPING b/services/powermanager/TEST_MAPPING
new file mode 100644
index 0000000..caaec55
--- /dev/null
+++ b/services/powermanager/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libpowermanager_test"
+ }
+ ]
+}
diff --git a/services/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp
new file mode 100644
index 0000000..1006a06
--- /dev/null
+++ b/services/powermanager/WorkSource.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "WorkSource"
+
+#include <android/WorkSource.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android::os {
+
+status_t WorkSource::readFromParcel(const android::Parcel *parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+ int32_t num;
+ status_t ret = parcel->readInt32(&num)
+ ?: parcel->readInt32Vector(&mUids)
+ ?: parcel->readString16Vector(&mNames);
+
+ return ret;
+}
+
+status_t WorkSource::writeToParcel(android::Parcel *parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ return parcel->writeInt32(mUids.size())
+ ?: parcel->writeInt32Vector(mUids)
+ ?: parcel->writeString16Vector(mNames);
+}
+
+} // namespace android::os
diff --git a/services/powermanager/include/android/BatterySaverPolicyConfig.h b/services/powermanager/include/android/BatterySaverPolicyConfig.h
new file mode 100644
index 0000000..728c8a0
--- /dev/null
+++ b/services/powermanager/include/android/BatterySaverPolicyConfig.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H
+#define ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+/**
+ * BatterySaverPolicyConfig is a structure of configs to set Battery Saver policy flags.
+ * This file needs to be kept in sync with
+ * frameworks/base/core/java/android/os/BatterySaverPolicyConfig.java
+ */
+struct BatterySaverPolicyConfig : public android::Parcelable {
+
+ BatterySaverPolicyConfig(float adjustBrightnessFactor = 1.0f,
+ bool advertiseIsEnabled = false,
+ bool deferFullBackup = false,
+ bool deferKeyValueBackup = false,
+ std::vector<std::pair<String16, String16>> deviceSpecificSettings = {},
+ bool disableAnimation = false,
+ bool disableAod = false,
+ bool disableLaunchBoost = false,
+ bool disableOptionalSensors = false,
+ bool disableSoundTrigger = false,
+ bool disableVibration = false,
+ bool enableAdjustBrightness = false,
+ bool enableDataSaver = false,
+ bool enableFirewall = false,
+ bool enableNightMode = false,
+ bool enableQuickDoze = false,
+ bool forceAllAppsStandby = false,
+ bool forceBackgroundCheck = false,
+ LocationMode locationMode = static_cast<LocationMode>(0))
+ : mAdjustBrightnessFactor(adjustBrightnessFactor),
+ mAdvertiseIsEnabled(advertiseIsEnabled),
+ mDeferFullBackup(deferFullBackup),
+ mDeferKeyValueBackup(deferKeyValueBackup),
+ mDeviceSpecificSettings(deviceSpecificSettings),
+ mDisableAnimation(disableAnimation),
+ mDisableAod(disableAod),
+ mDisableLaunchBoost(disableLaunchBoost),
+ mDisableOptionalSensors(disableOptionalSensors),
+ mDisableSoundTrigger(disableSoundTrigger),
+ mDisableVibration(disableVibration),
+ mEnableAdjustBrightness(enableAdjustBrightness),
+ mEnableDataSaver(enableDataSaver),
+ mEnableFirewall(enableFirewall),
+ mEnableNightMode(enableNightMode),
+ mEnableQuickDoze(enableQuickDoze),
+ mForceAllAppsStandby(forceAllAppsStandby),
+ mForceBackgroundCheck(forceBackgroundCheck),
+ mLocationMode(locationMode) {
+ }
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+ bool operator == (const BatterySaverPolicyConfig &bsp) const {
+ return fabs(mAdjustBrightnessFactor - bsp.mAdjustBrightnessFactor) == 0.0f &&
+ mAdvertiseIsEnabled == bsp.mAdvertiseIsEnabled &&
+ mDeferFullBackup == bsp.mDeferFullBackup &&
+ mDeferKeyValueBackup == bsp.mDeferKeyValueBackup &&
+ mDeviceSpecificSettings == bsp.mDeviceSpecificSettings &&
+ mDisableAnimation == bsp.mDisableAnimation &&
+ mDisableAod == bsp.mDisableAod &&
+ mDisableLaunchBoost == bsp.mDisableLaunchBoost &&
+ mDisableOptionalSensors == bsp.mDisableOptionalSensors &&
+ mDisableSoundTrigger == bsp.mDisableSoundTrigger &&
+ mDisableVibration == bsp.mDisableVibration &&
+ mEnableAdjustBrightness == bsp.mEnableAdjustBrightness &&
+ mEnableDataSaver == bsp.mEnableDataSaver &&
+ mEnableFirewall == bsp.mEnableFirewall &&
+ mEnableNightMode == bsp.mEnableNightMode &&
+ mEnableQuickDoze == bsp.mEnableQuickDoze &&
+ mForceAllAppsStandby == bsp.mForceAllAppsStandby &&
+ mForceBackgroundCheck == bsp.mForceBackgroundCheck &&
+ mLocationMode == bsp.mLocationMode;
+ }
+
+private:
+ status_t readDeviceSpecificSettings(const android::Parcel *parcel);
+ status_t writeDeviceSpecificSettings(android::Parcel *parcel) const;
+ /** Adjust screen brightness factor */
+ float mAdjustBrightnessFactor;
+ /** Is advertise enabled */
+ bool mAdvertiseIsEnabled;
+ /** Defer full backup */
+ bool mDeferFullBackup;
+ /** Defer key value backup */
+ bool mDeferKeyValueBackup;
+ /** Device specific settings */
+ std::vector<std::pair<String16, String16>> mDeviceSpecificSettings;
+ /** Disable animation */
+ bool mDisableAnimation;
+ /** Disable Aod */
+ bool mDisableAod;
+ /** Disable launch boost */
+ bool mDisableLaunchBoost;
+ /** Disable optional sensors */
+ bool mDisableOptionalSensors;
+ /** Disable sound trigger */
+ bool mDisableSoundTrigger;
+ /** Disable vibration */
+ bool mDisableVibration;
+ /** Enable adjust brightness */
+ bool mEnableAdjustBrightness;
+ /** Enable data saver */
+ bool mEnableDataSaver;
+ /** Enable firewall */
+ bool mEnableFirewall;
+ /** Enable night mode */
+ bool mEnableNightMode;
+ /** Enable quick doze */
+ bool mEnableQuickDoze;
+ /** Force all Apps standby */
+ bool mForceAllAppsStandby;
+ /** Force Background check */
+ bool mForceBackgroundCheck;
+ /** Location mode */
+ LocationMode mLocationMode;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H */
diff --git a/services/powermanager/include/android/LocationMode.h b/services/powermanager/include/android/LocationMode.h
new file mode 100644
index 0000000..42933d4
--- /dev/null
+++ b/services/powermanager/include/android/LocationMode.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_LOCATION_MODE_H
+#define ANDROID_OS_LOCATION_MODE_H
+
+namespace android::os {
+
+enum class LocationMode : int32_t {
+ NO_CHANGE = IPowerManager::LOCATION_MODE_NO_CHANGE,
+ GPS_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
+ ALL_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF,
+ FOREGROUND_ONLY = IPowerManager::LOCATION_MODE_FOREGROUND_ONLY,
+ THROTTLE_REQUESTS_WHEN_SCREEN_OFF =
+ IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+ MIN = IPowerManager::LOCATION_MODE_NO_CHANGE,
+ MAX = IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_LOCATION_MODE_H */
diff --git a/services/powermanager/include/android/PowerSaveState.h b/services/powermanager/include/android/PowerSaveState.h
new file mode 100644
index 0000000..b421f6a
--- /dev/null
+++ b/services/powermanager/include/android/PowerSaveState.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_POWER_SAVE_STATE_H
+#define ANDROID_OS_POWER_SAVE_STATE_H
+
+#include <math.h>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+enum class LocationMode : int32_t;
+/**
+ * PowerSaveState is a structure to encapsulate PowerSaveState status.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/PowerSaveState.java
+ */
+struct PowerSaveState : public android::Parcelable {
+
+ PowerSaveState(bool batterySaverEnabled = false,
+ bool globalBatterySaverEnabled = false,
+ LocationMode locationMode = static_cast<LocationMode>(0),
+ float brightnessFactor = 0.5f)
+ : mBatterySaverEnabled(batterySaverEnabled),
+ mGlobalBatterySaverEnabled(globalBatterySaverEnabled),
+ mLocationMode(locationMode),
+ mBrightnessFactor(brightnessFactor) {
+ }
+
+ bool getBatterySaverEnabled() const { return mBatterySaverEnabled; }
+ bool getGlobalBatterySaverEnabled() const { return mGlobalBatterySaverEnabled; }
+ LocationMode getLocationMode() const { return mLocationMode; }
+ float getBrightnessFactor() const { return mBrightnessFactor; }
+ bool operator == (const PowerSaveState &ps) const {
+ return mBatterySaverEnabled == ps.mBatterySaverEnabled &&
+ mGlobalBatterySaverEnabled == ps.mGlobalBatterySaverEnabled &&
+ mLocationMode == ps.mLocationMode &&
+ fabs(mBrightnessFactor - ps.mBrightnessFactor) == 0.0f;
+ }
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+ /** Whether we should enable battery saver for this service. */
+ bool mBatterySaverEnabled;
+ /** Whether battery saver mode is enabled. */
+ bool mGlobalBatterySaverEnabled;
+ /** Location mode */
+ LocationMode mLocationMode;
+ /** Screen brightness factor. */
+ float mBrightnessFactor;
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_POWER_SAVE_STATE_H */
diff --git a/services/powermanager/include/android/WorkSource.h b/services/powermanager/include/android/WorkSource.h
new file mode 100644
index 0000000..f12847d
--- /dev/null
+++ b/services/powermanager/include/android/WorkSource.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_WORKSOURCE_H
+#define ANDROID_OS_WORKSOURCE_H
+
+#include <optional>
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android::os {
+
+/**
+ * WorkSource is a structure to describes the source of some work that may be done by someone else.
+ * This file needs to be kept in sync with frameworks/base/core/java/android/os/WorkSource.java
+ */
+struct WorkSource : public android::Parcelable {
+ WorkSource(
+ std::vector<int32_t> uids = {},
+ std::optional<std::vector<std::optional<String16>>> names = std::nullopt)
+ : mUids(uids),
+ mNames(names) {
+ }
+ std::vector<int32_t> getUids() const { return mUids; }
+ std::optional<std::vector<std::optional<String16>>> getNames() const { return mNames; }
+ bool operator == (const WorkSource &ws) const {
+ return mUids == ws.mUids && mNames == ws.mNames;
+ }
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+private:
+ /** WorkSource UID array */
+ std::vector<int32_t> mUids = {};
+ /** WorkSource Tag array */
+ std::optional<std::vector<std::optional<String16>>> mNames = {};
+};
+
+} // namespace android::os
+
+#endif /* ANDROID_OS_WORKSOURCE_H */
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
new file mode 100644
index 0000000..49abc11
--- /dev/null
+++ b/services/powermanager/tests/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "libpowermanager_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "IThermalManagerTest.cpp",
+ "PowerHalControllerTest.cpp",
+ "PowerHalLoaderTest.cpp",
+ "PowerHalWrapperAidlTest.cpp",
+ "PowerHalWrapperHidlV1_0Test.cpp",
+ "PowerHalWrapperHidlV1_1Test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpowermanager",
+ "libutils",
+ "android.hardware.power@1.0",
+ "android.hardware.power@1.1",
+ "android.hardware.power-cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
similarity index 100%
rename from services/powermanager/IThermalManagerTest.cpp
rename to services/powermanager/tests/IThermalManagerTest.cpp
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
new file mode 100644
index 0000000..141b244
--- /dev/null
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalControllerTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalController.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class TestPowerHalConnector : public HalConnector {
+public:
+ TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {}
+ virtual ~TestPowerHalConnector() = default;
+
+ virtual std::unique_ptr<HalWrapper> connect() override {
+ mCountMutex.lock();
+ ++mConnectedCount;
+ mCountMutex.unlock();
+ return std::make_unique<HidlHalWrapperV1_0>(mHal);
+ }
+
+ void reset() override {
+ mCountMutex.lock();
+ ++mResetCount;
+ mCountMutex.unlock();
+ }
+
+ int getConnectCount() { return mConnectedCount; }
+
+ int getResetCount() { return mResetCount; }
+
+private:
+ sp<IPower> mHal = nullptr;
+ std::mutex mCountMutex;
+ int mConnectedCount = 0;
+ int mResetCount = 0;
+};
+
+class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector {
+public:
+ AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {}
+
+ std::unique_ptr<HalWrapper> connect() override {
+ // Call parent to update counter, but ignore connected HalWrapper.
+ TestPowerHalConnector::connect();
+ return nullptr;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalControllerTest : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIPowerV1_0>();
+ std::unique_ptr<TestPowerHalConnector> halConnector =
+ std::make_unique<TestPowerHalConnector>(mMockHal);
+ mHalConnector = halConnector.get();
+ mHalController = std::make_unique<PowerHalController>(std::move(halConnector));
+ }
+
+protected:
+ sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+ TestPowerHalConnector* mHalConnector = nullptr;
+ std::unique_ptr<PowerHalController> mHalController = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ mHalController->init();
+ mHalController->init();
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) {
+ std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector =
+ std::make_unique<AlwaysFailingTestPowerHalConnector>();
+ AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get();
+ PowerHalController halController(std::move(halConnector));
+
+ int powerHalConnectCount = failingHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ // Still works with EmptyPowerHalWrapper as fallback ignoring every api call
+ // and logging.
+ auto result = halController.setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = halController.setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+
+ // PowerHalConnector was called every time to attempt to reconnect with
+ // underlying service.
+ powerHalConnectCount = failingHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 2);
+ // PowerHalConnector was never reset.
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ {
+ InSequence seg;
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+ }
+
+ auto result = mHalController->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+ .WillByDefault([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4));
+
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mHalController->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mHalController->setMode(Mode::LOW_POWER, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+
+ // PowerHalConnector was called only twice: on first api call and after failed
+ // call.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 2);
+ // PowerHalConnector was reset once after failed call.
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 1);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+
+ // PowerHalConnector was called only once and never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(10));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // PowerHalConnector was called only by the first thread to use the api and
+ // never reset.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 1);
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) {
+ int powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_EQ(powerHalConnectCount, 0);
+
+ ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+ .WillByDefault([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(40));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ threads.push_back(std::thread([&]() {
+ auto result = mHalController->setMode(Mode::VR, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ // PowerHalConnector was called at least once by the first thread.
+ // Reset and reconnect calls were made at most 10 times, once after each
+ // failure.
+ powerHalConnectCount = mHalConnector->getConnectCount();
+ EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11)));
+ int powerHalResetCount = mHalConnector->getResetCount();
+ EXPECT_THAT(powerHalResetCount, Le(10));
+}
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
new file mode 100644
index 0000000..058e1b5
--- /dev/null
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalLoaderTest"
+
+#include <android-base/logging.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalLoader.h>
+
+#include <future>
+
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerAidl = android::hardware::power::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+sp<T> loadHal();
+
+template <>
+sp<IPowerAidl> loadHal<IPowerAidl>() {
+ return PowerHalLoader::loadAidl();
+}
+
+template <>
+sp<IPowerV1_0> loadHal<IPowerV1_0>() {
+ return PowerHalLoader::loadHidlV1_0();
+}
+
+template <>
+sp<IPowerV1_1> loadHal<IPowerV1_1>() {
+ return PowerHalLoader::loadHidlV1_1();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+class PowerHalLoaderTest : public Test {
+public:
+ sp<T> load() { return ::loadHal<T>(); }
+ void unload() { PowerHalLoader::unloadAll(); }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes;
+TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_EQ(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestUnload) {
+ sp<TypeParam> firstHal = this->load();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+ this->unload();
+ sp<TypeParam> secondHal = this->load();
+ ASSERT_NE(secondHal, nullptr);
+ ASSERT_NE(firstHal, secondHal);
+}
+
+TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) {
+ std::vector<std::future<sp<TypeParam>>> futures;
+ for (int i = 0; i < 10; i++) {
+ futures.push_back(
+ std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this));
+ }
+
+ futures[0].wait();
+ sp<TypeParam> firstHal = futures[0].get();
+ if (firstHal == nullptr) {
+ ALOGE("Power HAL not available. Skipping test.");
+ return;
+ }
+
+ for (int i = 1; i < 10; i++) {
+ futures[i].wait();
+ sp<TypeParam> currentHal = futures[i].get();
+ ASSERT_EQ(firstHal, currentHal);
+ }
+}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..a765659
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalWrapperAidlTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+#include <thread>
+
+using android::binder::Status;
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPower : public IPower {
+public:
+ MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override));
+ MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
+ MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
+ MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPower>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperAidlTest::SetUp() {
+ mMockHal = new StrictMock<MockIPower>();
+ mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ }
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10));
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(-1)));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::FAILED, result);
+ result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10));
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->setMode(Mode::LAUNCH, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
new file mode 100644
index 0000000..6693d0b
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalWrapperHidlV1_0Test"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_0Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_0Test::SetUp() {
+ mMockHal = new StrictMock<MockIPowerV1_0>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_0>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostSuccessful) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
+ auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
+ auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..55bbd6d
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalWrapperHidlV1_1Test"
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_0 = android::hardware::power::V1_0::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPowerV1_0 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class MockIPowerV1_1 : public IPowerV1_1 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHint hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+ (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_1Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr;
+ sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_1Test::SetUp() {
+ mMockHalV1_0 = new StrictMock<MockIPowerV1_0>();
+ mMockHalV1_1 = new StrictMock<MockIPowerV1_1>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
+ ASSERT_NE(mWrapper, nullptr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1));
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
+ auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(),
+ powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHalV1_0.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_EQ(HalResult::SUCCESSFUL, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHint, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_EQ(HalResult::FAILED, result);
+}
+
+TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
+ auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+ ASSERT_EQ(HalResult::UNSUPPORTED, result);
+}
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index d7e621c..c232f0b 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -36,7 +36,7 @@
// Increase the value of the sensor's nominal resolution to ensure that
// sensor accuracy improvements, like runtime calibration, are not masked
// during requantization.
- double incRes = 0.25 * resolution;
+ double incRes = 0.125 * resolution;
*value = round(static_cast<double>(*value) / incRes) * incRes;
}
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index ccf05d9..9b30dce 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -37,6 +37,7 @@
mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false) {
mChannel = new BitTube(mService->mSocketBufferSize);
+ mTargetSdk = SensorService::getTargetSdkVersion(opPackageName);
#if DEBUG_CONNECTIONS
mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
mTotalAcksNeeded = mTotalAcksReceived = 0;
@@ -439,8 +440,17 @@
bool success = true;
const auto iter = mHandleToAppOp.find(event.sensor);
if (iter != mHandleToAppOp.end()) {
- int32_t appOpMode = mService->sAppOpsManager.noteOp((*iter).second, mUid, mOpPackageName);
- success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+ // Special handling for step count/detect backwards compatibility: if the app's target SDK
+ // is pre-Q, still permit delivering events to the app even if permission isn't granted
+ // (since this permission was only introduced in Q)
+ if ((event.type == SENSOR_TYPE_STEP_COUNTER || event.type == SENSOR_TYPE_STEP_DETECTOR) &&
+ mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
+ success = true;
+ } else {
+ int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
+ mOpPackageName);
+ success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+ }
}
return success;
}
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 13cee6f..8d5fcf7 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -175,6 +175,7 @@
int mEventsDropped;
String8 mPackageName;
const String16 mOpPackageName;
+ int mTargetSdk;
#if DEBUG_CONNECTIONS
int mEventsReceived, mEventsSent, mEventsSentFromCache;
int mTotalAcksNeeded, mTotalAcksReceived;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index ffcd0a0..3f88f77 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -699,6 +699,9 @@
if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
return PERMISSION_DENIED;
}
+ if (args.size() == 0) {
+ return BAD_INDEX;
+ }
if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
return BAD_VALUE;
}
@@ -1802,36 +1805,28 @@
const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
IPCThreadState::self()->getCallingUid(), opPackageName);
bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED;
+ int targetSdkVersion = getTargetSdkVersion(opPackageName);
bool canAccess = false;
- if (hasPermissionForSensor(sensor)) {
+ if (targetSdkVersion > 0 && targetSdkVersion <= __ANDROID_API_P__ &&
+ (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
+ sensor.getType() == SENSOR_TYPE_STEP_DETECTOR)) {
+ // Allow access to step sensors if the application targets pre-Q, which is before the
+ // requirement to hold the AR permission to access Step Counter and Step Detector events
+ // was introduced.
+ canAccess = true;
+ } else if (hasPermissionForSensor(sensor)) {
// Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
if (opCode < 0 || appOpAllowed) {
canAccess = true;
}
- } else if (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
- sensor.getType() == SENSOR_TYPE_STEP_DETECTOR) {
- int targetSdkVersion = getTargetSdkVersion(opPackageName);
- // Allow access to the sensor if the application targets pre-Q, which is before the
- // requirement to hold the AR permission to access Step Counter and Step Detector events
- // was introduced, and the user hasn't revoked the app op.
- //
- // Verifying the app op is required to ensure that the user hasn't revoked the necessary
- // permissions to access the Step Detector and Step Counter when the application targets
- // pre-Q. Without this check, if the user revokes the pre-Q install-time GMS Core AR
- // permission, the app would still be able to receive Step Counter and Step Detector events.
- if (appOpAllowed &&
- targetSdkVersion > 0 &&
- targetSdkVersion <= __ANDROID_API_P__) {
- canAccess = true;
- }
}
if (canAccess) {
sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName);
} else {
- ALOGE("%s a sensor (%s) without holding its required permission: %s",
- operation, sensor.getName().string(), sensor.getRequiredPermission().string());
+ ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
+ operation, sensor.getName().string(), sensor.getRequiredPermission().string());
}
return canAccess;
diff --git a/services/sensorservice/hidl/Android.bp b/services/sensorservice/hidl/Android.bp
index d0c83d6..0e1af59 100644
--- a/services/sensorservice/hidl/Android.bp
+++ b/services/sensorservice/hidl/Android.bp
@@ -10,6 +10,7 @@
"-Wall",
"-Werror",
],
+ header_libs: ["jni_headers"],
shared_libs: [
"libbase",
"libhidlbase",
@@ -24,6 +25,7 @@
export_include_dirs: [
"include/"
],
+ export_header_lib_headers: ["jni_headers"],
local_include_dirs: [
"include/sensorservicehidl/"
]
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 0e69f60..26bfb49 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -148,10 +148,10 @@
virtual status_t updateActiveBuffer() = 0;
virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
- // We generate InputWindowHandles for all buffered layers regardless of whether they
+ // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
// have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
// detection.
- bool needsInputInfo() const override { return true; }
+ bool needsInputInfo() const override { return !mPotentialCursor; }
protected:
struct BufferInfo {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e8f54f5..b07c904 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -921,9 +921,8 @@
const nsecs_t renderEngineStart = systemTime();
status_t status =
- renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers,
- buf->getNativeBuffer(), /*useFramebufferCache=*/true,
- std::move(fd), &readyFence);
+ renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf,
+ /*useFramebufferCache=*/true, std::move(fd), &readyFence);
if (status != NO_ERROR && mClientCompositionRequestCache) {
// If rendering was not successful, remove the request from the cache.
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 730f297..9aa274b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -116,6 +116,10 @@
}
}
+void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
+ mDeviceProductInfo = std::move(info);
+}
+
uint32_t DisplayDevice::getPageFlipCount() const {
return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
}
@@ -269,6 +273,12 @@
StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
static_cast<int32_t>(mPowerMode));
StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
+ StringAppendF(&result, "deviceProductInfo=");
+ if (mDeviceProductInfo) {
+ mDeviceProductInfo->dump(result);
+ } else {
+ result.append("{}");
+ }
getCompositionDisplay()->dump(result);
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index cb467ea..81b3ccf 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -59,12 +59,14 @@
class DisplaySurface;
} // namespace compositionengine
-class DisplayDevice : public LightRefBase<DisplayDevice> {
+class DisplayDevice : public RefBase {
public:
constexpr static float sDefaultMinLumiance = 0.0;
constexpr static float sDefaultMaxLumiance = 500.0;
explicit DisplayDevice(DisplayDeviceCreationArgs& args);
+
+ // Must be destroyed on the main thread because it may call into HWComposer.
virtual ~DisplayDevice();
std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
@@ -136,6 +138,11 @@
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
+ void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
+ const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
+ return mDeviceProductInfo;
+ }
+
/* ------------------------------------------------------------------------
* Display power mode management.
*/
@@ -182,6 +189,8 @@
// TODO(b/74619554): Remove special cases for primary display.
const bool mIsPrimary;
+
+ std::optional<DeviceProductInfo> mDeviceProductInfo;
};
struct DisplayDeviceState {
@@ -189,7 +198,7 @@
DisplayId id;
DisplayConnectionType type;
hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
-
+ std::optional<DeviceProductInfo> deviceProductInfo;
bool operator==(const Physical& other) const {
return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
}
@@ -239,21 +248,18 @@
class DisplayRenderArea : public RenderArea {
public:
- DisplayRenderArea(const sp<const DisplayDevice>& display,
- RotationFlags rotation = ui::Transform::ROT_0)
- : DisplayRenderArea(display, display->getBounds(),
- static_cast<uint32_t>(display->getWidth()),
- static_cast<uint32_t>(display->getHeight()),
- display->getCompositionDataSpace(), rotation) {}
-
- DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
- bool allowSecureLayers = true)
- : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
- display->getViewport(), applyDeviceOrientation(rotation, display)),
- mDisplay(std::move(display)),
- mSourceCrop(sourceCrop),
- mAllowSecureLayers(allowSecureLayers) {}
+ static std::unique_ptr<RenderArea> create(wp<const DisplayDevice> displayWeak,
+ const Rect& sourceCrop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace, RotationFlags rotation,
+ bool allowSecureLayers = true) {
+ if (auto display = displayWeak.promote()) {
+ // Using new to access a private constructor.
+ return std::unique_ptr<DisplayRenderArea>(
+ new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
+ rotation, allowSecureLayers));
+ }
+ return nullptr;
+ }
const ui::Transform& getTransform() const override { return mTransform; }
Rect getBounds() const override { return mDisplay->getBounds(); }
@@ -300,6 +306,14 @@
}
private:
+ DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace, RotationFlags rotation, bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getViewport(),
+ applyDeviceOrientation(rotation, display)),
+ mDisplay(std::move(display)),
+ mSourceCrop(sourceCrop),
+ mAllowSecureLayers(allowSecureLayers) {}
+
static RotationFlags applyDeviceOrientation(RotationFlags orientationFlag,
const sp<const DisplayDevice>& device) {
uint32_t inverseRotate90 = 0;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index dbdffec..a3f1b52 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1676,6 +1676,7 @@
if (found == mReturnData.end()) {
outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+ return;
}
ReturnData& data = found->second;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 4dfc743..52a6380 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -71,12 +71,8 @@
DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
DeviceProductInfo info;
- std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin());
- info.name[edid.displayName.size()] = '\0';
-
- const auto productId = std::to_string(edid.productId);
- std::copy(productId.begin(), productId.end(), info.productId.begin());
- info.productId[productId.size()] = '\0';
+ info.name.assign(edid.displayName);
+ info.productId = std::to_string(edid.productId);
info.manufacturerPnpId = edid.pnpId;
constexpr uint8_t kModelYearFlag = 0xff;
@@ -99,8 +95,6 @@
if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
info.relativeAddress = {address.a, address.b, address.c, address.d};
- } else {
- info.relativeAddress = DeviceProductInfo::NO_RELATIVE_ADDRESS;
}
return info;
}
@@ -238,7 +232,6 @@
constexpr size_t kDescriptorCount = 4;
constexpr size_t kDescriptorLength = 18;
- static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE);
for (size_t i = 0; i < kDescriptorCount; i++) {
if (view.size() < kDescriptorLength) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 8ac0561..08559bd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -894,6 +894,10 @@
mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
if (validTypes & HdrMetadata::HDR10PLUS) {
+ if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) {
+ return Error::BAD_PARAMETER;
+ }
+
std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
perFrameMetadataBlobs.push_back(
{Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index c355ebd..b800038 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -64,6 +64,8 @@
const uint32_t id;
};
+// See the comment for SurfaceFlinger::getHwComposer for the thread safety rules for accessing
+// this class.
class HWComposer {
public:
struct DeviceRequestedChanges {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 1d8179c..4b4c050 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -85,6 +85,7 @@
const bool expectsExpensiveRendering = !mExpensiveDisplays.empty();
if (mNotifiedExpensiveRendering != expectsExpensiveRendering) {
+ std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper == nullptr) {
return;
@@ -108,6 +109,7 @@
}
if (mSendUpdateImminent.load()) {
+ std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper == nullptr) {
return;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 34e63e7..95eb0e2 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -19,6 +19,8 @@
#include <atomic>
#include <unordered_set>
+#include <utils/Mutex.h>
+
#include "../Scheduler/OneShotTimer.h"
#include "DisplayIdentification.h"
@@ -56,10 +58,11 @@
void notifyDisplayUpdateImminent() override;
private:
- HalWrapper* getPowerHal();
+ HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
+ bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
+ std::mutex mPowerHalMutex;
std::atomic_bool mBootFinished = false;
- bool mReconnectPowerHal = false;
std::unordered_set<DisplayId> mExpensiveDisplays;
bool mNotifiedExpensiveRendering = false;
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index b986f38..2dfb9a9 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -25,7 +25,7 @@
#include "FrameTracer.h"
#include <android-base/stringprintf.h>
-#include <perfetto/trace/clock_snapshot.pbzero.h>
+#include <perfetto/common/builtin_clock.pbzero.h>
#include <algorithm>
#include <mutex>
@@ -34,7 +34,6 @@
namespace android {
-using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
void FrameTracer::initialize() {
std::call_once(mInitializationFlag, [this]() {
perfetto::TracingInitArgs args;
@@ -136,7 +135,7 @@
uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
FrameEvent::BufferEventType type, nsecs_t duration) {
auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(Clock::MONOTONIC);
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
packet->set_timestamp(timestamp);
auto* event = packet->set_graphics_frame_event()->set_buffer_event();
event->set_buffer_id(static_cast<uint32_t>(bufferID));
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3255aac..f36b67d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -866,13 +866,12 @@
}
// If we still have pending updates, we need to ensure SurfaceFlinger
- // will keep calling doTransaction, and so we set the transaction flags.
+ // will keep calling doTransaction, and so we force a traversal.
// However, our pending states won't clear until a frame is available,
- // and so there is no need to specifically trigger a wakeup. Rather
- // we set the flags and wait for something else to wake us up.
+ // and so there is no need to specifically trigger a wakeup.
if (!mPendingStates.empty()) {
setTransactionFlags(eTransactionNeeded);
- mFlinger->setTransactionFlagsNoWake(eTraversalNeeded);
+ mFlinger->setTraversalNeeded();
}
mCurrentState.modified = false;
@@ -2239,6 +2238,7 @@
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
[&]() { return layerInfo->mutable_position(); });
@@ -2408,7 +2408,15 @@
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
- info.visible = canReceiveInput();
+ // For compatibility reasons we let layers which can receive input
+ // receive input before they have actually submitted a buffer. Because
+ // of this we use canReceiveInput instead of isVisible to check the
+ // policy-visibility, ignoring the buffer state. However for layers with
+ // hasInputInfo()==false we can use the real visibility state.
+ // We are just using these layers for occlusion detection in
+ // InputDispatcher, and obviously if they aren't visible they can't occlude
+ // anything.
+ info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index f2bc65d..c5a4689 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -3,8 +3,8 @@
alecmouri@google.com
chaviw@google.com
lpy@google.com
-marissaw@google.com
racarr@google.com
steventhomas@google.com
stoza@google.com
vhau@google.com
+vishnun@google.com
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 27353d8..398fd40 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -36,6 +36,7 @@
#include "DisplayDevice.h"
#include "Layer.h"
+#include "Promise.h"
#include "Scheduler/DispSync.h"
#include "SurfaceFlinger.h"
@@ -336,8 +337,20 @@
return;
}
- const auto device = mFlinger.getDefaultDisplayDevice();
- const auto orientation = ui::Transform::toRotationFlags(device->getOrientation());
+ wp<const DisplayDevice> displayWeak;
+
+ ui::LayerStack layerStack;
+ ui::Transform::RotationFlags orientation;
+ ui::Size displaySize;
+
+ {
+ // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread
+ const sp<const DisplayDevice> display = mFlinger.getDefaultDisplayDevice();
+ displayWeak = display;
+ layerStack = display->getLayerStack();
+ orientation = ui::Transform::toRotationFlags(display->getOrientation());
+ displaySize = display->getSize();
+ }
std::vector<RegionSamplingThread::Descriptor> descriptors;
Region sampleRegion;
@@ -346,20 +359,18 @@
descriptors.emplace_back(descriptor);
}
- const Rect sampledArea = sampleRegion.bounds();
-
auto dx = 0;
auto dy = 0;
switch (orientation) {
case ui::Transform::ROT_90:
- dx = device->getWidth();
+ dx = displaySize.getWidth();
break;
case ui::Transform::ROT_180:
- dx = device->getWidth();
- dy = device->getHeight();
+ dx = displaySize.getWidth();
+ dy = displaySize.getHeight();
break;
case ui::Transform::ROT_270:
- dy = device->getHeight();
+ dy = displaySize.getHeight();
break;
default:
break;
@@ -368,8 +379,13 @@
ui::Transform t(orientation);
auto screencapRegion = t.transform(sampleRegion);
screencapRegion = screencapRegion.translate(dx, dy);
- DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(),
- sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation);
+
+ const Rect sampledBounds = sampleRegion.bounds();
+
+ SurfaceFlinger::RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
+ ui::Dataspace::V0_SRGB, orientation);
+ });
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
@@ -393,9 +409,9 @@
constexpr bool roundOutwards = true;
Rect transformed = transform.transform(bounds, roundOutwards);
- // If this layer doesn't intersect with the larger sampledArea, skip capturing it
+ // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
Rect ignore;
- if (!transformed.intersect(sampledArea, &ignore)) return;
+ if (!transformed.intersect(sampledBounds, &ignore)) return;
// If the layer doesn't intersect a sampling area, skip capturing it
bool intersectsAnyArea = false;
@@ -411,22 +427,22 @@
bounds.top, bounds.right, bounds.bottom);
visitor(layer);
};
- mFlinger.traverseLayersInDisplay(device, filterVisitor);
+ mFlinger.traverseLayersInLayerStack(layerStack, filterVisitor);
};
sp<GraphicBuffer> buffer = nullptr;
- if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() &&
- mCachedBuffer->getHeight() == sampledArea.getHeight()) {
+ if (mCachedBuffer && mCachedBuffer->getWidth() == sampledBounds.getWidth() &&
+ mCachedBuffer->getHeight() == sampledBounds.getHeight()) {
buffer = mCachedBuffer;
} else {
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
- buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(),
+ buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
}
bool ignored;
- mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */,
- true /* regionSampling */, ignored);
+ mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+ false /* identityTransform */, true /* regionSampling */, ignored);
std::vector<Descriptor> activeDescriptors;
for (const auto& descriptor : descriptors) {
@@ -437,7 +453,7 @@
ALOGV("Sampling %zu descriptors", activeDescriptors.size());
std::vector<float> lumas =
- sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation);
+ sampleBuffer(buffer, sampledBounds.leftTop(), activeDescriptors, orientation);
if (lumas.size() != activeDescriptors.size()) {
ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
activeDescriptors.size());
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 6b0455a..a6246d9 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -23,11 +23,9 @@
static float getCaptureFillValue(CaptureFill captureFill);
- RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
- ui::Dataspace reqDataSpace, const Rect& displayViewport,
- RotationFlags rotation = ui::Transform::ROT_0)
- : mReqWidth(reqWidth),
- mReqHeight(reqHeight),
+ RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
+ const Rect& displayViewport, RotationFlags rotation = ui::Transform::ROT_0)
+ : mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
mRotationFlags(rotation),
@@ -70,8 +68,8 @@
RotationFlags getRotationFlags() const { return mRotationFlags; }
// Returns the size of the physical render area.
- int getReqWidth() const { return static_cast<int>(mReqWidth); }
- int getReqHeight() const { return static_cast<int>(mReqHeight); }
+ int getReqWidth() const { return mReqSize.width; }
+ int getReqHeight() const { return mReqSize.height; }
// Returns the composition data space of the render area.
ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
@@ -86,8 +84,7 @@
const Rect& getDisplayViewport() const { return mDisplayViewport; }
private:
- const uint32_t mReqWidth;
- const uint32_t mReqHeight;
+ const ui::Size mReqSize;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const RotationFlags mRotationFlags;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index acd76b0..228b8a0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -136,7 +136,7 @@
class LayerHistoryV2 : public android::scheduler::LayerHistory {
public:
- LayerHistoryV2();
+ LayerHistoryV2(const scheduler::RefreshRateConfigs&);
virtual ~LayerHistoryV2();
// Layers are unregistered when the weak reference expires.
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 316000c..ee612b0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -58,37 +58,32 @@
return atoi(value);
}
-void trace(const wp<Layer>& weak, LayerHistory::LayerVoteType type, int fps) {
+void trace(const wp<Layer>& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type,
+ int fps) {
const auto layer = weak.promote();
if (!layer) return;
- const auto makeTag = [layer](LayerHistory::LayerVoteType vote) {
- return "LFPS " + RefreshRateConfigs::layerVoteTypeString(vote) + " " + layer->getName();
+ const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
+ ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
};
- const auto noVoteTag = makeTag(LayerHistory::LayerVoteType::NoVote);
- const auto heuristicVoteTag = makeTag(LayerHistory::LayerVoteType::Heuristic);
- const auto explicitDefaultVoteTag = makeTag(LayerHistory::LayerVoteType::ExplicitDefault);
- const auto explicitExactOrMultipleVoteTag =
- makeTag(LayerHistory::LayerVoteType::ExplicitExactOrMultiple);
- const auto minVoteTag = makeTag(LayerHistory::LayerVoteType::Min);
- const auto maxVoteTag = makeTag(LayerHistory::LayerVoteType::Max);
-
- ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
- ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
- ATRACE_INT(explicitDefaultVoteTag.c_str(),
- type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0);
- ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(),
- type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0);
- ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
- ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
+ traceType(LayerHistory::LayerVoteType::NoVote, 1);
+ traceType(LayerHistory::LayerVoteType::Heuristic, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
+ traceType(LayerHistory::LayerVoteType::Min, 1);
+ traceType(LayerHistory::LayerVoteType::Max, 1);
ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
}
} // namespace
-LayerHistoryV2::LayerHistoryV2()
- : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
+ LayerInfoV2::setTraceEnabled(mTraceEnabled);
+ LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs);
+}
+
LayerHistoryV2::~LayerHistoryV2() = default;
void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
@@ -151,7 +146,7 @@
summary.push_back({strong->getName(), type, refreshRate, weight});
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(layer, type, static_cast<int>(std::round(refreshRate)));
+ trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
}
}
@@ -190,7 +185,7 @@
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
+ trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
}
info->onLayerInactive(now);
@@ -217,4 +212,5 @@
info->clearHistory(systemTime());
}
}
+
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index cb81ca2..820624b 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -98,9 +98,9 @@
return false;
}
- // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
if (mElements.size() < HISTORY_SIZE &&
- mElements.back() - mElements.front() < HISTORY_TIME.count()) {
+ mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
return false;
}
@@ -124,7 +124,7 @@
private:
std::deque<nsecs_t> mElements;
- static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
};
friend class LayerHistoryTest;
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 82da007..44f20d0 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -15,24 +15,31 @@
*/
// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "LayerInfoV2.h"
#include <algorithm>
#include <utility>
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+
#undef LOG_TAG
#define LOG_TAG "LayerInfoV2"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
namespace android::scheduler {
+const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
+bool LayerInfoV2::sTraceEnabled = false;
+
LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
LayerHistory::LayerVoteType defaultVote)
: mName(name),
mHighRefreshRatePeriod(highRefreshRatePeriod),
mDefaultVote(defaultVote),
- mLayerVote({defaultVote, 0.0f}) {}
+ mLayerVote({defaultVote, 0.0f}),
+ mRefreshRateHistory(name) {}
void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
LayerUpdateType updateType, bool pendingConfigChange) {
@@ -92,24 +99,28 @@
}
bool LayerInfoV2::hasEnoughDataForHeuristic() const {
- // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
if (mFrameTimes.size() < 2) {
+ ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
return false;
}
if (!isFrameTimeValid(mFrameTimes.front())) {
+ ALOGV("stale frames still captured");
return false;
}
- if (mFrameTimes.size() < HISTORY_SIZE &&
- mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
+ const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
+ if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
+ ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
+ totalDuration / 1e9f);
return false;
}
return true;
}
-std::pair<nsecs_t, bool> LayerInfoV2::calculateAverageFrameTime() const {
+std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
nsecs_t totalPresentTimeDeltas = 0;
nsecs_t totalQueueTimeDeltas = 0;
bool missingPresentTime = false;
@@ -117,15 +128,20 @@
for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
// Ignore frames captured during a config change
if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
- continue;
+ return std::nullopt;
}
totalQueueTimeDeltas +=
std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
numFrames++;
- if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
+ if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
missingPresentTime = true;
+ // If there are no presentation timestamps and we haven't calculated
+ // one in the past then we can't calculate the refresh rate
+ if (mLastRefreshRate.reported == 0) {
+ return std::nullopt;
+ }
continue;
}
@@ -140,58 +156,46 @@
// when implementing render ahead for specific refresh rates. When hwui no longer provides
// presentation timestamps we look at the queue time to see if the current refresh rate still
// matches the content.
+
const auto averageFrameTime =
static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
numFrames;
- return {static_cast<nsecs_t>(averageFrameTime), missingPresentTime};
+ return static_cast<nsecs_t>(averageFrameTime);
}
-bool LayerInfoV2::isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const {
- for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
- // Ignore frames captured during a config change
- if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
- continue;
- }
- const auto presentTimeDeltas = [&] {
- const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
- : (it + 1)->presetTime - it->presetTime;
- return std::max(delta, mHighRefreshRatePeriod);
- }();
-
- if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
- return false;
- }
- }
-
- return true;
-}
-
-std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) {
static constexpr float MARGIN = 1.0f; // 1Hz
-
if (!hasEnoughDataForHeuristic()) {
ALOGV("Not enough data");
return std::nullopt;
}
- const auto [averageFrameTime, missingPresentTime] = calculateAverageFrameTime();
+ const auto averageFrameTime = calculateAverageFrameTime();
+ if (averageFrameTime.has_value()) {
+ const auto refreshRate = 1e9f / *averageFrameTime;
+ const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
+ if (refreshRateConsistent) {
+ const auto knownRefreshRate =
+ sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
- // If there are no presentation timestamps provided we can't calculate the refresh rate
- if (missingPresentTime && mLastReportedRefreshRate == 0) {
- return std::nullopt;
+ // To avoid oscillation, use the last calculated refresh rate if it is
+ // close enough
+ if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
+ mLastRefreshRate.reported != knownRefreshRate) {
+ mLastRefreshRate.calculated = refreshRate;
+ mLastRefreshRate.reported = knownRefreshRate;
+ }
+
+ ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
+ refreshRate, mLastRefreshRate.reported);
+ } else {
+ ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
+ refreshRate, mLastRefreshRate.reported);
+ }
}
- if (!isRefreshRateStable(averageFrameTime, missingPresentTime)) {
- return std::nullopt;
- }
-
- const auto refreshRate = 1e9f / averageFrameTime;
- if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
- mLastReportedRefreshRate = refreshRate;
- }
-
- ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
- return mLastReportedRefreshRate;
+ return mLastRefreshRate.reported == 0 ? std::nullopt
+ : std::make_optional(mLastRefreshRate.reported);
}
std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
@@ -202,15 +206,24 @@
if (isAnimating(now)) {
ALOGV("%s is animating", mName.c_str());
+ mLastRefreshRate.animatingOrInfrequent = true;
return {LayerHistory::LayerVoteType::Max, 0};
}
if (!isFrequent(now)) {
ALOGV("%s is infrequent", mName.c_str());
+ mLastRefreshRate.animatingOrInfrequent = true;
return {LayerHistory::LayerVoteType::Min, 0};
}
- auto refreshRate = calculateRefreshRateIfPossible();
+ // If the layer was previously tagged as animating or infrequent, we clear
+ // the history as it is likely the layer just changed its behavior
+ // and we should not look at stale data
+ if (mLastRefreshRate.animatingOrInfrequent) {
+ clearHistory(now);
+ }
+
+ auto refreshRate = calculateRefreshRateIfPossible(now);
if (refreshRate.has_value()) {
ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
@@ -220,4 +233,65 @@
return {LayerHistory::LayerVoteType::Max, 0};
}
+const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+ if (mTraceTags.count(type) == 0) {
+ const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
+ mTraceTags.emplace(type, tag);
+ }
+
+ return mTraceTags.at(type).c_str();
+}
+
+LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData
+LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const {
+ const std::string prefix = "LFPS ";
+ const std::string suffix = "Heuristic ";
+ return {.min = prefix + mName + suffix + "min",
+ .max = prefix + mName + suffix + "max",
+ .consistent = prefix + mName + suffix + "consistent",
+ .average = prefix + mName + suffix + "average"};
+}
+
+void LayerInfoV2::RefreshRateHistory::clear() {
+ mRefreshRates.clear();
+}
+
+bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
+ mRefreshRates.push_back({refreshRate, now});
+ while (mRefreshRates.size() >= HISTORY_SIZE ||
+ now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
+ mRefreshRates.pop_front();
+ }
+
+ if (CC_UNLIKELY(sTraceEnabled)) {
+ if (!mHeuristicTraceTagData.has_value()) {
+ mHeuristicTraceTagData = makeHeuristicTraceTagData();
+ }
+
+ ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
+ }
+
+ return isConsistent();
+}
+
+bool LayerInfoV2::RefreshRateHistory::isConsistent() const {
+ if (mRefreshRates.empty()) return true;
+
+ const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
+ const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
+ const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
+
+ if (CC_UNLIKELY(sTraceEnabled)) {
+ if (!mHeuristicTraceTagData.has_value()) {
+ mHeuristicTraceTagData = makeHeuristicTraceTagData();
+ }
+
+ ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
+ ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
+ ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
+ }
+
+ return consistent;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 9e6783f..33dc66f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -56,6 +56,12 @@
friend class LayerHistoryTestV2;
public:
+ static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
+
+ static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
+ sRefreshRateConfigs = &refreshRateConfigs;
+ }
+
LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
LayerHistory::LayerVoteType defaultVote);
@@ -86,6 +92,9 @@
// updated time, the updated time is the present time.
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
+ // Returns a C string for tracing a vote
+ const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+
void onLayerInactive(nsecs_t now) {
// Mark mFrameTimeValidSince to now to ignore all previous frame times.
// We are not deleting the old frame to keep track of whether we should treat the first
@@ -93,7 +102,8 @@
// posting infrequent updates.
const auto timePoint = std::chrono::nanoseconds(now);
mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
- mLastReportedRefreshRate = 0.0f;
+ mLastRefreshRate = {};
+ mRefreshRateHistory.clear();
}
void clearHistory(nsecs_t now) {
@@ -109,12 +119,73 @@
bool pendingConfigChange;
};
+ // Holds information about the calculated and reported refresh rate
+ struct RefreshRateHeuristicData {
+ // Rate calculated on the layer
+ float calculated = 0.0f;
+ // Last reported rate for LayerInfoV2::getRefreshRate()
+ float reported = 0.0f;
+ // Whether the last reported rate for LayerInfoV2::getRefreshRate()
+ // was due to animation or infrequent updates
+ bool animatingOrInfrequent = false;
+ };
+
+ // Holds information about the layer vote
+ struct LayerVote {
+ LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+ float fps = 0.0f;
+ };
+
+ // Class to store past calculated refresh rate and determine whether
+ // the refresh rate calculated is consistent with past values
+ class RefreshRateHistory {
+ public:
+ static constexpr auto HISTORY_SIZE = 90;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
+
+ RefreshRateHistory(const std::string& name) : mName(name) {}
+
+ // Clears History
+ void clear();
+
+ // Adds a new refresh rate and returns true if it is consistent
+ bool add(float refreshRate, nsecs_t now);
+
+ private:
+ friend class LayerHistoryTestV2;
+
+ // Holds the refresh rate when it was calculated
+ struct RefreshRateData {
+ float refreshRate = 0.0f;
+ nsecs_t timestamp = 0;
+
+ bool operator<(const RefreshRateData& other) const {
+ return refreshRate < other.refreshRate;
+ }
+ };
+
+ // Holds tracing strings
+ struct HeuristicTraceTagData {
+ std::string min;
+ std::string max;
+ std::string consistent;
+ std::string average;
+ };
+
+ bool isConsistent() const;
+ HeuristicTraceTagData makeHeuristicTraceTagData() const;
+
+ const std::string mName;
+ mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
+ std::deque<RefreshRateData> mRefreshRates;
+ static constexpr float MARGIN_FPS = 1.0;
+ };
+
bool isFrequent(nsecs_t now) const;
bool isAnimating(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const;
- std::optional<float> calculateRefreshRateIfPossible();
- std::pair<nsecs_t, bool> calculateAverageFrameTime() const;
- bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const;
+ std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
+ std::optional<nsecs_t> calculateAverageFrameTime() const;
bool isFrameTimeValid(const FrameTimeData&) const;
const std::string mName;
@@ -123,23 +194,27 @@
const nsecs_t mHighRefreshRatePeriod;
LayerHistory::LayerVoteType mDefaultVote;
+ LayerVote mLayerVote;
+
nsecs_t mLastUpdatedTime = 0;
nsecs_t mLastAnimationTime = 0;
- float mLastReportedRefreshRate = 0.0f;
-
- // Holds information about the layer vote
- struct {
- LayerHistory::LayerVoteType type;
- float fps;
- } mLayerVote;
+ RefreshRateHeuristicData mLastRefreshRate;
std::deque<FrameTimeData> mFrameTimes;
std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
std::chrono::steady_clock::now();
- static constexpr size_t HISTORY_SIZE = 90;
- static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+ static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+
+ RefreshRateHistory mRefreshRateHistory;
+
+ mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
+
+ // Shared for all LayerInfo instances
+ static const RefreshRateConfigs* sRefreshRateConfigs;
+ static bool sTraceEnabled;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 3c8bd68..a6036c6 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -115,12 +115,24 @@
}
const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
- const std::vector<LayerRequirement>& layers, bool touchActive, bool idle,
- bool* touchConsidered) const {
+ const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
ALOGV("getRefreshRateForContent %zu layers", layers.size());
- if (touchConsidered) *touchConsidered = false;
+ if (outSignalsConsidered) *outSignalsConsidered = {};
+ const auto setTouchConsidered = [&] {
+ if (outSignalsConsidered) {
+ outSignalsConsidered->touch = true;
+ }
+ };
+
+ const auto setIdleConsidered = [&] {
+ if (outSignalsConsidered) {
+ outSignalsConsidered->idle = true;
+ }
+ };
+
std::lock_guard lock(mLock);
int noVoteLayers = 0;
@@ -150,9 +162,9 @@
// Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
// selected a refresh rate to see if we should apply touch boost.
- if (touchActive && !hasExplicitVoteLayers) {
+ if (globalSignals.touch && !hasExplicitVoteLayers) {
ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
- if (touchConsidered) *touchConsidered = true;
+ setTouchConsidered();
return getMaxRefreshRateByPolicyLocked();
}
@@ -162,8 +174,10 @@
const Policy* policy = getCurrentPolicyLocked();
const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max;
- if (!touchActive && idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+ if (!globalSignals.touch && globalSignals.idle &&
+ !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
+ setIdleConsidered();
return getMinRefreshRateByPolicyLocked();
}
@@ -242,7 +256,7 @@
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
- const auto layerScore = [&]() {
+ const auto layerScore = [&] {
// Calculate how many display vsyncs we need to present a single frame for this
// layer
const auto [displayFramesQuot, displayFramesRem] =
@@ -307,9 +321,9 @@
// actually increase the refresh rate over the normal selection.
const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
- if (touchActive && explicitDefaultVoteLayers == 0 &&
+ if (globalSignals.touch && explicitDefaultVoteLayers == 0 &&
bestRefreshRate->fps < touchRefreshRate.fps) {
- if (touchConsidered) *touchConsidered = true;
+ setTouchConsidered();
ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
return touchRefreshRate;
}
@@ -384,7 +398,8 @@
RefreshRateConfigs::RefreshRateConfigs(
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- HwcConfigIndexType currentConfigId) {
+ HwcConfigIndexType currentConfigId)
+ : mKnownFrameRates(constructKnownFrameRates(configs)) {
LOG_ALWAYS_FATAL_IF(configs.empty());
LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
@@ -544,4 +559,64 @@
&mAppRequestRefreshRates);
}
+std::vector<float> RefreshRateConfigs::constructKnownFrameRates(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+ std::vector<float> knownFrameRates = {24.0f, 30.0f, 45.0f, 60.0f, 72.0f};
+ knownFrameRates.reserve(knownFrameRates.size() + configs.size());
+
+ // Add all supported refresh rates to the set
+ for (const auto& config : configs) {
+ const auto refreshRate = 1e9f / config->getVsyncPeriod();
+ knownFrameRates.emplace_back(refreshRate);
+ }
+
+ // Sort and remove duplicates
+ const auto frameRatesEqual = [](float a, float b) { return std::abs(a - b) <= 0.01f; };
+ std::sort(knownFrameRates.begin(), knownFrameRates.end());
+ knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+ frameRatesEqual),
+ knownFrameRates.end());
+ return knownFrameRates;
+}
+
+float RefreshRateConfigs::findClosestKnownFrameRate(float frameRate) const {
+ if (frameRate <= *mKnownFrameRates.begin()) {
+ return *mKnownFrameRates.begin();
+ }
+
+ if (frameRate >= *std::prev(mKnownFrameRates.end())) {
+ return *std::prev(mKnownFrameRates.end());
+ }
+
+ auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate);
+
+ const auto distance1 = std::abs(frameRate - *lowerBound);
+ const auto distance2 = std::abs(frameRate - *std::prev(lowerBound));
+ return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
+}
+
+RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
+ std::lock_guard lock(mLock);
+ const auto& deviceMin = getMinRefreshRate();
+ const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
+ const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+
+ // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
+ // the min allowed refresh rate is higher than the device min, we do not want to enable the
+ // timer.
+ if (deviceMin < minByPolicy) {
+ return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+ }
+ if (minByPolicy == maxByPolicy) {
+ // Do not sent the call to toggle off kernel idle timer if the device min and policy min and
+ // max are all the same. This saves us extra unnecessary calls to sysprop.
+ if (deviceMin == minByPolicy) {
+ return RefreshRateConfigs::KernelIdleTimerAction::NoChange;
+ }
+ return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+ }
+ // Turn on the timer in all other cases.
+ return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ff1eabd..8a51b85 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -29,7 +29,6 @@
#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
-class RefreshRateConfigsTest;
using namespace std::chrono_literals;
@@ -83,11 +82,13 @@
return configId != other.configId || hwcConfig != other.hwcConfig;
}
+ bool operator<(const RefreshRate& other) const { return getFps() < other.getFps(); }
+
bool operator==(const RefreshRate& other) const { return !(*this != other); }
private:
friend RefreshRateConfigs;
- friend RefreshRateConfigsTest;
+ friend class RefreshRateConfigsTest;
// The tolerance within which we consider FPS approximately equals.
static constexpr float FPS_EPSILON = 0.001f;
@@ -212,14 +213,22 @@
const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
EXCLUDES(mLock);
+ // Global state describing signals that affect refresh rate choice.
+ struct GlobalSignals {
+ // Whether the user touched the screen recently. Used to apply touch boost.
+ bool touch = false;
+ // True if the system hasn't seen any buffers posted to layers recently.
+ bool idle = false;
+ };
+
// Returns the refresh rate that fits best to the given layers.
// layers - The layer requirements to consider.
- // touchActive - Whether the user touched the screen recently. Used to apply touch boost.
- // idle - True if the system hasn't seen any buffers posted to layers recently.
- // touchConsidered - An output param that tells the caller whether the refresh rate was chosen
- // based on touch boost.
+ // globalSignals - global state of touch and idle
+ // outSignalsConsidered - An output param that tells the caller whether the refresh rate was
+ // chosen based on touch boost and/or idle timer.
const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- bool touchActive, bool idle, bool* touchConsidered) const
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
// Returns all the refresh rates supported by the device. This won't change at runtime.
@@ -258,11 +267,29 @@
// Returns a string that represents the layer vote type
static std::string layerVoteTypeString(LayerVoteType vote);
+ // Returns a known frame rate that is the closest to frameRate
+ float findClosestKnownFrameRate(float frameRate) const;
+
RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
HwcConfigIndexType currentConfigId);
+ // Class to enumerate options around toggling the kernel timer on and off. We have an option
+ // for no change to avoid extra calls to kernel.
+ enum class KernelIdleTimerAction {
+ NoChange, // Do not change the idle timer.
+ TurnOff, // Turn off the idle timer.
+ TurnOn // Turn on the idle timer.
+ };
+ // Checks whether kernel idle timer should be active depending the policy decisions around
+ // refresh rates.
+ KernelIdleTimerAction getIdleTimerAction() const;
+
private:
+ friend class RefreshRateConfigsTest;
+
void constructAvailableRefreshRates() REQUIRES(mLock);
+ static std::vector<float> constructKnownFrameRates(
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs);
void getSortedRefreshRateList(
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
@@ -320,6 +347,10 @@
const RefreshRate* mMaxSupportedRefreshRate;
mutable std::mutex mLock;
+
+ // A sorted list of known frame rates that a Heuristic layer will choose
+ // from based on the closest value.
+ const std::vector<float> mKnownFrameRates;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e250bbf..5c0ba01 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -111,7 +111,7 @@
using namespace sysprop;
if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(refreshRateConfig);
} else {
mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
}
@@ -228,8 +228,36 @@
mConnections[handle].thread->onScreenReleased();
}
-void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ // Cache the last reported config for primary display.
+ mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+ onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
+}
+
+void Scheduler::dispatchCachedReportedConfig() {
+ const auto configId = *mFeatures.configId;
+ const auto vsyncPeriod =
+ mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod();
+
+ // If there is no change from cached config, there is no need to dispatch an event
+ if (configId == mFeatures.cachedConfigChangedParams->configId &&
+ vsyncPeriod == mFeatures.cachedConfigChangedParams->vsyncPeriod) {
+ return;
+ }
+
+ mFeatures.cachedConfigChangedParams->configId = configId;
+ mFeatures.cachedConfigChangedParams->vsyncPeriod = vsyncPeriod;
+ onNonPrimaryDisplayConfigChanged(mFeatures.cachedConfigChangedParams->handle,
+ mFeatures.cachedConfigChangedParams->displayId,
+ mFeatures.cachedConfigChangedParams->configId,
+ mFeatures.cachedConfigChangedParams->vsyncPeriod);
+}
+
+void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
+ PhysicalDisplayId displayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
RETURN_IF_INVALID_HANDLE(handle);
mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
}
@@ -446,13 +474,21 @@
mFeatures.contentDetectionV1 =
!summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
- newConfigId = calculateRefreshRateConfigIndexType();
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
+ newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
if (mFeatures.configId == newConfigId) {
+ // We don't need to change the config, but we might need to send an event
+ // about a config change, since it was suppressed due to a previous idleConsidered
+ if (!consideredSignals.idle) {
+ dispatchCachedReportedConfig();
+ }
return;
}
mFeatures.configId = newConfigId;
auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ConfigEvent::None
+ : ConfigEvent::Changed);
}
}
@@ -522,21 +558,20 @@
}
void Scheduler::idleTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */);
+ handleTimerStateChanged(&mFeatures.idleTimer, state);
ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
}
void Scheduler::touchTimerCallback(TimerState state) {
const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
- if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) {
+ if (handleTimerStateChanged(&mFeatures.touch, touch)) {
mLayerHistory->clear();
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
void Scheduler::displayPowerTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.displayPowerTimer, state,
- true /* eventOnContentDetection */);
+ handleTimerStateChanged(&mFeatures.displayPowerTimer, state);
ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
@@ -553,33 +588,37 @@
}
template <class T>
-bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
- ConfigEvent event = ConfigEvent::None;
+bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
HwcConfigIndexType newConfigId;
- bool touchConsidered = false;
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (*currentState == newState) {
- return touchConsidered;
+ return false;
}
*currentState = newState;
- newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered);
+ newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
if (mFeatures.configId == newConfigId) {
- return touchConsidered;
+ // We don't need to change the config, but we might need to send an event
+ // about a config change, since it was suppressed due to a previous idleConsidered
+ if (!consideredSignals.idle) {
+ dispatchCachedReportedConfig();
+ }
+ return consideredSignals.touch;
}
mFeatures.configId = newConfigId;
- if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) {
- event = ConfigEvent::Changed;
- }
}
const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
- return touchConsidered;
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ConfigEvent::None
+ : ConfigEvent::Changed);
+ return consideredSignals.touch;
}
-HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) {
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
+ scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
ATRACE_CALL();
- if (touchConsidered) *touchConsidered = false;
+ if (consideredSignals) *consideredSignals = {};
// If Display Power is not in normal operation we want to be in performance mode. When coming
// back to normal mode, a grace period is given with DisplayPowerTimer.
@@ -600,6 +639,7 @@
// If timer has expired as it means there is no new content on the screen.
if (idle) {
+ if (consideredSignals) consideredSignals->idle = true;
return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
}
@@ -615,7 +655,8 @@
}
return mRefreshRateConfigs
- .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered)
+ .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
+ consideredSignals)
.getConfigId();
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ebee9e3..730ea8f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -55,13 +55,24 @@
virtual void kernelTimerChanged(bool expired) = 0;
};
-class Scheduler {
+class IPhaseOffsetControl {
+public:
+ virtual ~IPhaseOffsetControl() = default;
+ virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
+};
+
+class Scheduler : public IPhaseOffsetControl {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
// Indicates whether to start the transaction early, or at vsync time.
- enum class TransactionStart { EARLY, NORMAL };
+ enum class TransactionStart {
+ Early, // DEPRECATED. Start the transaction early. Times out on its own
+ EarlyStart, // Start the transaction early and keep this config until EarlyEnd
+ EarlyEnd, // End the early config started at EarlyStart
+ Normal // Start the transaction at the normal time
+ };
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
@@ -81,14 +92,16 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId,
- nsecs_t vsyncPeriod);
-
+ void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod)
+ EXCLUDES(mFeatureStateLock);
+ void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
// Modifies phase offset in the event thread.
- void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
+ void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;
void getDisplayStatInfo(DisplayStatInfo* stats);
@@ -179,16 +192,19 @@
// handles various timer features to change the refresh rate.
template <class T>
- bool handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
+ bool handleTimerStateChanged(T* currentState, T newState);
void setVsyncPeriod(nsecs_t period);
// This function checks whether individual features that are affecting the refresh rate
// selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
// for the suggested refresh rate.
- HwcConfigIndexType calculateRefreshRateConfigIndexType(bool* touchConsidered = nullptr)
+ HwcConfigIndexType calculateRefreshRateConfigIndexType(
+ scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
REQUIRES(mFeatureStateLock);
+ void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
+
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
sp<EventThreadConnection> connection;
@@ -240,6 +256,16 @@
LayerHistory::Summary contentRequirements;
bool isDisplayPowerStateNormal = true;
+
+ // Used to cache the last parameters of onPrimaryDisplayConfigChanged
+ struct ConfigChangedParams {
+ ConnectionHandle handle;
+ PhysicalDisplayId displayId;
+ HwcConfigIndexType configId;
+ nsecs_t vsyncPeriod;
+ };
+
+ std::optional<ConfigChangedParams> cachedConfigChangedParams;
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index abeacfe..a596bce 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -338,10 +338,14 @@
}
auto& callback = it->second;
- if (callback->wakeupTime()) {
+ auto const wakeupTime = callback->wakeupTime();
+ if (wakeupTime) {
callback->disarm();
- mIntendedWakeupTime = kInvalidTime;
- rearmTimer(mTimeKeeper->now());
+
+ if (*wakeupTime == mIntendedWakeupTime) {
+ mIntendedWakeupTime = kInvalidTime;
+ rearmTimer(mTimeKeeper->now());
+ }
return CancelResult::Cancelled;
}
return CancelResult::TooLate;
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 40a992c..2567c04 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -25,16 +25,17 @@
#include <cutils/properties.h>
#include <utils/Trace.h>
+#include <chrono>
#include <cinttypes>
#include <mutex>
namespace android::scheduler {
-VSyncModulator::VSyncModulator(Scheduler& scheduler,
+VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
Scheduler::ConnectionHandle appConnectionHandle,
Scheduler::ConnectionHandle sfConnectionHandle,
const OffsetsConfig& config)
- : mScheduler(scheduler),
+ : mPhaseOffsetControl(phaseOffsetControl),
mAppConnectionHandle(appConnectionHandle),
mSfConnectionHandle(sfConnectionHandle),
mOffsetsConfig(config) {
@@ -50,13 +51,35 @@
}
void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
- if (transactionStart == Scheduler::TransactionStart::EARLY) {
+ switch (transactionStart) {
+ case Scheduler::TransactionStart::EarlyStart:
+ ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
+ mExplicitEarlyWakeup = true;
+ break;
+ case Scheduler::TransactionStart::EarlyEnd:
+ ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
+ mExplicitEarlyWakeup = false;
+ break;
+ case Scheduler::TransactionStart::Normal:
+ case Scheduler::TransactionStart::Early:
+ // Non explicit don't change the explicit early wakeup state
+ break;
+ }
+
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+ }
+
+ if (!mExplicitEarlyWakeup &&
+ (transactionStart == Scheduler::TransactionStart::Early ||
+ transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
+ mEarlyTxnStartTime = std::chrono::steady_clock::now();
}
// An early transaction stays an early transaction.
if (transactionStart == mTransactionStart ||
- mTransactionStart == Scheduler::TransactionStart::EARLY) {
+ mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
return;
}
mTransactionStart = transactionStart;
@@ -64,8 +87,9 @@
}
void VSyncModulator::onTransactionHandled() {
- if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
- mTransactionStart = Scheduler::TransactionStart::NORMAL;
+ mTxnAppliedTime = std::chrono::steady_clock::now();
+ if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
+ mTransactionStart = Scheduler::TransactionStart::Normal;
updateOffsets();
}
@@ -87,9 +111,15 @@
void VSyncModulator::onRefreshed(bool usedRenderEngine) {
bool updateOffsetsNeeded = false;
- if (mRemainingEarlyFrameCount > 0) {
- mRemainingEarlyFrameCount--;
- updateOffsetsNeeded = true;
+
+ // Apply a margin to account for potential data races
+ // This might make us stay in early offsets for one
+ // additional frame but it's better to be conservative here.
+ if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
+ if (mRemainingEarlyFrameCount > 0) {
+ mRemainingEarlyFrameCount--;
+ updateOffsetsNeeded = true;
+ }
}
if (usedRenderEngine) {
mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
@@ -111,8 +141,8 @@
const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
// Early offsets are used if we're in the middle of a refresh rate
// change, or if we recently begin a transaction.
- if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
- mRefreshRateChangePending) {
+ if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
+ mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
return mOffsetsConfig.early;
} else if (mRemainingRenderEngineUsageCount > 0) {
return mOffsetsConfig.earlyGl;
@@ -129,8 +159,8 @@
void VSyncModulator::updateOffsetsLocked() {
const Offsets& offsets = getNextOffsets();
- mScheduler.setPhaseOffset(mSfConnectionHandle, offsets.sf);
- mScheduler.setPhaseOffset(mAppConnectionHandle, offsets.app);
+ mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
+ mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);
mOffsets = offsets;
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 704a5d5..ab678c9 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -16,6 +16,7 @@
#pragma once
+#include <chrono>
#include <mutex>
#include "Scheduler.h"
@@ -37,6 +38,9 @@
// switch in and out of gl composition.
static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+ // Margin used to account for potential data races
+ static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms;
+
public:
// Wrapper for a collection of surfaceflinger/app offsets for a particular
// configuration.
@@ -61,7 +65,7 @@
bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
};
- VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
+ VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle,
ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
@@ -90,13 +94,14 @@
Offsets getOffsets() const EXCLUDES(mMutex);
private:
+ friend class VSyncModulatorTest;
// Returns the next offsets that we should be using
const Offsets& getNextOffsets() const REQUIRES(mMutex);
// Updates offsets and persists them into the scheduler framework.
void updateOffsets() EXCLUDES(mMutex);
void updateOffsetsLocked() REQUIRES(mMutex);
- Scheduler& mScheduler;
+ IPhaseOffsetControl& mPhaseOffsetControl;
const ConnectionHandle mAppConnectionHandle;
const ConnectionHandle mSfConnectionHandle;
@@ -106,10 +111,13 @@
Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
std::atomic<Scheduler::TransactionStart> mTransactionStart =
- Scheduler::TransactionStart::NORMAL;
+ Scheduler::TransactionStart::Normal;
std::atomic<bool> mRefreshRateChangePending = false;
+ std::atomic<bool> mExplicitEarlyWakeup = false;
std::atomic<int> mRemainingEarlyFrameCount = 0;
std::atomic<int> mRemainingRenderEngineUsageCount = 0;
+ std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {};
+ std::atomic<std::chrono::steady_clock::time_point> mTxnAppliedTime = {};
bool mTraceDetailedInfo = false;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 16d102c..c743de0 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -278,7 +278,9 @@
return false;
}
- if (mSupportKernelIdleTimer) {
+ const bool periodIsChanging =
+ mPeriodTransitioningTo && (*mPeriodTransitioningTo != getPeriod());
+ if (mSupportKernelIdleTimer && !periodIsChanging) {
// Clear out the Composer-provided period and use the allowance logic below
HwcVsyncPeriod = {};
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 51cf77e..e68c484 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -23,11 +23,12 @@
#include "SurfaceFlinger.h"
+#include <android-base/properties.h>
#include <android/configuration.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/1.0/IPower.h>
+#include <android/hardware/power/Boost.h>
#include <android/native_window.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -147,7 +148,7 @@
using namespace android::hardware::configstore::V1_0;
using namespace android::sysprop;
-using android::hardware::power::V1_0::PowerHint;
+using android::hardware::power::Boost;
using base::StringAppendF;
using ui::ColorMode;
using ui::Dataspace;
@@ -258,7 +259,7 @@
const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");
-const char* KERNEL_IDLE_TIMER_PROP = "vendor.display.enable_kernel_idle_timer";
+const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
@@ -422,6 +423,10 @@
const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
+ mGraphicBufferProducerListSizeLogThreshold =
+ std::max(static_cast<int>(0.95 *
+ static_cast<double>(mMaxGraphicBufferProducerListSize)),
+ 1);
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
@@ -444,6 +449,9 @@
}
useFrameRateApi = use_frame_rate_api(true);
+
+ mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false);
+ base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
}
SurfaceFlinger::~SurfaceFlinger() = default;
@@ -867,7 +875,7 @@
info->density /= ACONFIGURATION_DENSITY_MEDIUM;
info->secure = display->isSecure();
- info->deviceProductInfo = getDeviceProductInfoLocked(*display);
+ info->deviceProductInfo = display->getDeviceProductInfo();
return NO_ERROR;
}
@@ -1077,8 +1085,8 @@
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
.getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
- mUpcomingActiveConfig.configId, vsyncPeriod);
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ mUpcomingActiveConfig.configId, vsyncPeriod);
}
}
@@ -1334,30 +1342,6 @@
return NO_ERROR;
}
-std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked(
- const DisplayDevice& display) const {
- // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to
- // avoid repetitive HAL IPC and EDID parsing.
- const auto displayId = display.getId();
- LOG_FATAL_IF(!displayId);
-
- const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
- LOG_FATAL_IF(!hwcDisplayId);
-
- uint8_t port;
- DisplayIdentificationData data;
- if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
- ALOGV("%s: No identification data.", __FUNCTION__);
- return {};
- }
-
- const auto info = parseDisplayIdentificationData(port, data);
- if (!info) {
- return {};
- }
- return info->deviceProductInfo;
-}
-
status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
@@ -1525,10 +1509,10 @@
.get();
}
-status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) {
- PowerHint powerHint = static_cast<PowerHint>(hintId);
+status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
+ Boost powerBoost = static_cast<Boost>(boostId);
- if (powerHint == PowerHint::INTERACTION) {
+ if (powerBoost == Boost::INTERACTION) {
mScheduler->notifyTouchEvent();
}
@@ -1690,7 +1674,7 @@
if (const auto displayId = getInternalDisplayIdLocked()) {
sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
if (display && display->isPoweredOn()) {
- setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
}
}
}
@@ -2017,8 +2001,9 @@
bool flushedATransaction = flushTransactionQueues();
- bool runHandleTransaction = transactionFlags &&
- ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction);
+ bool runHandleTransaction =
+ (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
+ flushedATransaction;
if (runHandleTransaction) {
handleTransaction(eTransactionMask);
@@ -2441,8 +2426,10 @@
}
DisplayDeviceState state;
- state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
- event.hwcDisplayId};
+ state.physical = {.id = displayId,
+ .type = getHwComposer().getDisplayConnectionType(displayId),
+ .hwcDisplayId = event.hwcDisplayId,
+ .deviceProductInfo = info->deviceProductInfo};
state.isSecure = true; // All physical displays are currently considered secure.
state.displayName = info->name;
@@ -2558,6 +2545,7 @@
LOG_ALWAYS_FATAL_IF(!displayId);
auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
display->setActiveConfig(activeConfigId);
+ display->setDeviceProductInfo(state.physical->deviceProductInfo);
}
display->setLayerStack(state.layerStack);
@@ -2764,7 +2752,8 @@
* (perform the transaction for each of them if needed)
*/
- if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
+ if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
+ mForceTraversal = false;
mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2777,7 +2766,6 @@
mInputInfoChanged = true;
}
});
- mTraversalNeededMainThread = false;
}
/*
@@ -2995,8 +2983,8 @@
// anyway since there are no connected apps at this point.
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, primaryDisplayId.value, currentConfig,
- vsyncPeriod);
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId.value,
+ currentConfig, vsyncPeriod);
}
void SurfaceFlinger::commitTransaction()
@@ -3201,6 +3189,11 @@
"Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
mGraphicBufferProducerList.size(),
mMaxGraphicBufferProducerListSize, mNumLayers.load());
+ if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
+ ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+ mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
+ mNumLayers.load());
+ }
}
if (const auto display = getDefaultDisplayDeviceLocked()) {
@@ -3235,7 +3228,7 @@
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, Scheduler::TransactionStart::NORMAL);
+ return setTransactionFlags(flags, Scheduler::TransactionStart::Normal);
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
@@ -3248,8 +3241,8 @@
return old;
}
-uint32_t SurfaceFlinger::setTransactionFlagsNoWake(uint32_t flags) {
- return mTransactionFlags.fetch_or(flags);
+void SurfaceFlinger::setTraversalNeeded() {
+ mForceTraversal = true;
}
bool SurfaceFlinger::flushTransactionQueues() {
@@ -3449,18 +3442,39 @@
// so we don't have to wake up again next frame to preform an uneeded traversal.
if (isMainThread && (transactionFlags & eTraversalNeeded)) {
transactionFlags = transactionFlags & (~eTraversalNeeded);
- mTraversalNeededMainThread = true;
+ mForceTraversal = true;
}
+ const auto transactionStart = [](uint32_t flags) {
+ if (flags & eEarlyWakeup) {
+ return Scheduler::TransactionStart::Early;
+ }
+ if (flags & eExplicitEarlyWakeupEnd) {
+ return Scheduler::TransactionStart::EarlyEnd;
+ }
+ if (flags & eExplicitEarlyWakeupStart) {
+ return Scheduler::TransactionStart::EarlyStart;
+ }
+ return Scheduler::TransactionStart::Normal;
+ }(flags);
+
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
}
+ // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
+ if (flags & eEarlyWakeup) {
+ ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+ }
+
+ if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+ ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+ flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+ }
+
// this triggers the transaction
- const auto start = (flags & eEarlyWakeup) ? Scheduler::TransactionStart::EARLY
- : Scheduler::TransactionStart::NORMAL;
- setTransactionFlags(transactionFlags, start);
+ setTransactionFlags(transactionFlags, transactionStart);
if (flags & eAnimation) {
mAnimTransactionPending = true;
@@ -3497,6 +3511,13 @@
break;
}
}
+ } else {
+ // even if a transaction is not needed, we need to update VsyncModulator
+ // about explicit early indications
+ if (transactionStart == Scheduler::TransactionStart::EarlyStart ||
+ transactionStart == Scheduler::TransactionStart::EarlyEnd) {
+ mVSyncModulator->setTransactionStart(transactionStart);
+ }
}
}
@@ -4172,13 +4193,6 @@
static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
}
-void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled) {
- if (mHWCVsyncState != enabled) {
- getHwComposer().setVsyncEnabled(displayId, enabled);
- mHWCVsyncState = enabled;
- }
-}
-
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
if (display->isVirtual()) {
ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
@@ -4207,7 +4221,7 @@
}
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
- setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}
@@ -4226,7 +4240,7 @@
}
// Make sure HWVsync is disabled before turning off the display
- setVsyncEnabledInHWC(*displayId, hal::Vsync::DISABLE);
+ getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE);
getHwComposer().setPowerMode(*displayId, mode);
mVisibleRegionsDirty = true;
@@ -4705,7 +4719,7 @@
StringAppendF(&result, "Composition layers\n");
mDrawingState.traverseInZOrder([&](Layer* layer) {
auto* compositionState = layer->getCompositionState();
- if (!compositionState) return;
+ if (!compositionState || !compositionState->isVisible) return;
android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
layer->getDebugName() ? layer->getDebugName()
@@ -4869,7 +4883,7 @@
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
case GET_DISPLAYED_CONTENT_SAMPLE:
- case NOTIFY_POWER_HINT:
+ case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
// ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
@@ -5333,8 +5347,7 @@
const auto& min = mRefreshRateConfigs->getMinRefreshRate();
if (current != min) {
- const auto kernelTimerEnabled = property_get_bool(KERNEL_IDLE_TIMER_PROP, false);
- const bool timerExpired = kernelTimerEnabled && expired;
+ const bool timerExpired = mKernelIdleTimerEnabled && expired;
if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
@@ -5344,6 +5357,35 @@
}));
}
+void SurfaceFlinger::toggleKernelIdleTimer() {
+ using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+ // If the support for kernel idle timer is disabled in SF code, don't do anything.
+ if (!mSupportKernelIdleTimer) {
+ return;
+ }
+ const KernelIdleTimerAction action = mRefreshRateConfigs->getIdleTimerAction();
+
+ switch (action) {
+ case KernelIdleTimerAction::TurnOff:
+ if (mKernelIdleTimerEnabled) {
+ ATRACE_INT("KernelIdleTimer", 0);
+ base::SetProperty(KERNEL_IDLE_TIMER_PROP, "false");
+ mKernelIdleTimerEnabled = false;
+ }
+ break;
+ case KernelIdleTimerAction::TurnOn:
+ if (!mKernelIdleTimerEnabled) {
+ ATRACE_INT("KernelIdleTimer", 1);
+ base::SetProperty(KERNEL_IDLE_TIMER_PROP, "true");
+ mKernelIdleTimerEnabled = true;
+ }
+ break;
+ case KernelIdleTimerAction::NoChange:
+ break;
+ }
+}
+
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -5373,27 +5415,33 @@
renderAreaRotation = ui::Transform::ROT_0;
}
- sp<DisplayDevice> display;
+ wp<DisplayDevice> displayWeak;
+ ui::LayerStack layerStack;
+ ui::Size reqSize(reqWidth, reqHeight);
{
Mutex::Autolock lock(mStateLock);
-
- display = getDisplayDeviceLocked(displayToken);
+ sp<DisplayDevice> display = getDisplayDeviceLocked(displayToken);
if (!display) return NAME_NOT_FOUND;
+ displayWeak = display;
+ layerStack = display->getLayerStack();
// set the requested width/height to the logical display viewport size
// by default
if (reqWidth == 0 || reqHeight == 0) {
- reqWidth = uint32_t(display->getViewport().width());
- reqHeight = uint32_t(display->getViewport().height());
+ reqSize = display->getViewport().getSize();
}
}
- DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
- renderAreaRotation, captureSecureLayers);
- auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
- std::placeholders::_1);
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
- useIdentityTransform, outCapturedSecureLayers);
+ RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, sourceCrop, reqSize, reqDataspace,
+ renderAreaRotation, captureSecureLayers);
+ });
+
+ auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, visitor);
+ };
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, outBuffer,
+ reqPixelFormat, useIdentityTransform, outCapturedSecureLayers);
}
static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
@@ -5449,19 +5497,20 @@
status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace,
sp<GraphicBuffer>* outBuffer) {
- sp<DisplayDevice> display;
- uint32_t width;
- uint32_t height;
+ ui::LayerStack layerStack;
+ wp<DisplayDevice> displayWeak;
+ ui::Size size;
ui::Transform::RotationFlags captureOrientation;
{
Mutex::Autolock lock(mStateLock);
- display = getDisplayByIdOrLayerStack(displayOrLayerStack);
+ sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack);
if (!display) {
return NAME_NOT_FOUND;
}
+ layerStack = display->getLayerStack();
+ displayWeak = display;
- width = uint32_t(display->getViewport().width());
- height = uint32_t(display->getViewport().height());
+ size = display->getViewport().getSize();
const auto orientation = display->getOrientation();
captureOrientation = ui::Transform::toRotationFlags(orientation);
@@ -5487,14 +5536,19 @@
pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
}
- DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation,
- false /* captureSecureLayers */);
+ RenderAreaFuture renderAreaFuture = promise::defer([=] {
+ return DisplayRenderArea::create(displayWeak, Rect(), size, *outDataspace,
+ captureOrientation, false /* captureSecureLayers */);
+ });
- auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
- std::placeholders::_1);
+ auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+ traverseLayersInLayerStack(layerStack, visitor);
+ };
+
bool ignored = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888,
- false /* useIdentityTransform */,
+
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size, outBuffer,
+ ui::PixelFormat::RGBA_8888, false /* useIdentityTransform */,
ignored /* outCapturedSecureLayers */);
}
@@ -5508,9 +5562,9 @@
class LayerRenderArea : public RenderArea {
public:
LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
- int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
- bool childrenOnly, const Rect& displayViewport)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
+ ui::Size reqSize, Dataspace reqDataSpace, bool childrenOnly,
+ const Rect& displayViewport)
+ : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, displayViewport),
mLayer(layer),
mCrop(crop),
mNeedsFiltering(false),
@@ -5585,8 +5639,7 @@
const bool mChildrenOnly;
};
- int reqWidth = 0;
- int reqHeight = 0;
+ ui::Size reqSize;
sp<Layer> parent;
Rect crop(sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
@@ -5623,8 +5676,7 @@
// crop was not specified, or an invalid frame scale was provided.
return BAD_VALUE;
}
- reqWidth = crop.width() * frameScale;
- reqHeight = crop.height() * frameScale;
+ reqSize = ui::Size(crop.width() * frameScale, crop.height() * frameScale);
for (const auto& handle : excludeHandles) {
sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
@@ -5645,15 +5697,18 @@
} // mStateLock
// really small crop or frameScale
- if (reqWidth <= 0) {
- reqWidth = 1;
+ if (reqSize.width <= 0) {
+ reqSize.width = 1;
}
- if (reqHeight <= 0) {
- reqHeight = 1;
+ if (reqSize.height <= 0) {
+ reqSize.height = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
- displayViewport);
+ RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> {
+ return std::make_unique<LayerRenderArea>(this, parent, crop, reqSize, reqDataspace,
+ childrenOnly, displayViewport);
+ });
+
auto traverseLayers = [parent, childrenOnly,
&excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5676,14 +5731,14 @@
};
bool outCapturedSecureLayers = false;
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false,
- outCapturedSecureLayers);
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, outBuffer,
+ reqPixelFormat, false, outCapturedSecureLayers);
}
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer,
- const ui::PixelFormat reqPixelFormat,
+ ui::Size bufferSize, sp<GraphicBuffer>* outBuffer,
+ ui::PixelFormat reqPixelFormat,
bool useIdentityTransform,
bool& outCapturedSecureLayers) {
ATRACE_CALL();
@@ -5691,16 +5746,16 @@
// TODO(b/116112787) Make buffer usage a parameter.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- *outBuffer =
- getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
- static_cast<android_pixel_format>(reqPixelFormat), 1,
- usage, "screenshot");
+ *outBuffer = getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat),
+ 1, usage, "screenshot");
- return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
- false /* regionSampling */, outCapturedSecureLayers);
+ return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, *outBuffer,
+ useIdentityTransform, false /* regionSampling */,
+ outCapturedSecureLayers);
}
-status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
+status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
const sp<GraphicBuffer>& buffer,
bool useIdentityTransform, bool regionSampling,
@@ -5713,23 +5768,28 @@
do {
std::tie(result, syncFd) =
- schedule([&] {
+ schedule([&]() -> std::pair<status_t, int> {
if (mRefreshPending) {
- ATRACE_NAME("Skipping screenshot for now");
- return std::make_pair(EAGAIN, -1);
+ ALOGW("Skipping screenshot for now");
+ return {EAGAIN, -1};
+ }
+ std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
+ if (!renderArea) {
+ ALOGW("Skipping screen capture because of invalid render area.");
+ return {NO_MEMORY, -1};
}
status_t result = NO_ERROR;
int fd = -1;
Mutex::Autolock lock(mStateLock);
- renderArea.render([&] {
- result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
+ renderArea->render([&] {
+ result = captureScreenImplLocked(*renderArea, traverseLayers, buffer.get(),
useIdentityTransform, forSystem, &fd,
regionSampling, outCapturedSecureLayers);
});
- return std::make_pair(result, fd);
+ return {result, fd};
}).get();
} while (result == EAGAIN);
@@ -5743,8 +5803,9 @@
void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
- bool regionSampling, int* outSyncFd) {
+ const sp<GraphicBuffer>& buffer,
+ bool useIdentityTransform, bool regionSampling,
+ int* outSyncFd) {
ATRACE_CALL();
const auto reqWidth = renderArea.getReqWidth();
@@ -5842,7 +5903,7 @@
status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer,
+ const sp<GraphicBuffer>& buffer,
bool useIdentityTransform, bool forSystem,
int* outSyncFd, bool regionSampling,
bool& outCapturedSecureLayers) {
@@ -5887,17 +5948,17 @@
layersSortedByZ.traverseInReverseZOrder(stateSet, visitor);
}
-void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor) {
+void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack,
+ const LayerVector::Visitor& visitor) {
// We loop through the first level of layers without traversing,
// as we need to determine which layers belong to the requested display.
for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+ if (!layer->belongsToDisplay(layerStack, false)) {
continue;
}
// relative layers are traversed in Layer::traverseInZOrder
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->belongsToDisplay(display->getLayerStack(), false)) {
+ if (!layer->belongsToDisplay(layerStack, false)) {
return;
}
if (!layer->isVisible()) {
@@ -5943,8 +6004,8 @@
const nsecs_t vsyncPeriod = getHwComposer()
.getConfigs(*displayId)[policy->defaultConfig.value()]
->getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
- policy->defaultConfig, vsyncPeriod);
+ mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ policy->defaultConfig, vsyncPeriod);
return NO_ERROR;
}
@@ -5970,14 +6031,14 @@
currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
currentPolicy.appRequestRange.max);
- // TODO(b/140204874): This hack triggers a notification that something has changed, so
- // that listeners that care about a change in allowed configs can get the notification.
- // Giving current ActiveConfig so that most other listeners would just drop the event
+ // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
+ // be depending in this callback.
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
.getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
- display->getActiveConfig(), vsyncPeriod);
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+ display->getActiveConfig(), vsyncPeriod);
+ toggleKernelIdleTimer();
auto configId = mScheduler->getPreferredConfigId();
auto& preferredRefreshRate = configId
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e7bfdc7..6a3937f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -302,7 +302,6 @@
// main thread function to enable/disable h/w composer event
void setPrimaryVsyncEnabledInternal(bool enabled) REQUIRES(mStateLock);
- void setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled);
// called on the main thread by MessageQueue when an internal message
// is received
@@ -499,7 +498,7 @@
status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const override;
status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
- status_t notifyPowerHint(int32_t hintId) override;
+ status_t notifyPowerBoost(int32_t boostId) override;
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
@@ -536,6 +535,13 @@
void repaintEverythingForHWC() override;
// Called when kernel idle timer has expired. Used to update the refresh rate overlay.
void kernelTimerChanged(bool expired) override;
+ // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
+ void toggleKernelIdleTimer();
+ // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
+ // make calls to sys prop each time.
+ bool mKernelIdleTimerEnabled = false;
+ // Keeps track of whether the kernel timer is supported on the SF side.
+ bool mSupportKernelIdleTimer = false;
/* ------------------------------------------------------------------------
* Message handling
*/
@@ -628,12 +634,12 @@
uint32_t peekTransactionFlags();
// Can only be called from the main thread or with mStateLock held
uint32_t setTransactionFlags(uint32_t flags);
- // Set the transaction flags, but don't trigger a wakeup! We use this cases where
- // there are still pending transactions but we know they won't be ready until a frame
+ // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
+ // where there are still pending transactions but we know they won't be ready until a frame
// arrives from a different layer. So we need to ensure we performTransaction from invalidate
// but there is no need to try and wake up immediately to do it. Rather we rely on
- // onFrameAvailable to wake us up.
- uint32_t setTransactionFlagsNoWake(uint32_t flags);
+ // onFrameAvailable or another layer update to wake us up.
+ void setTraversalNeeded();
uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
void commitTransaction() REQUIRES(mStateLock);
void commitOffscreenLayers();
@@ -712,25 +718,25 @@
void startBootAnim();
using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+ using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>;
void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool regionSampling, int* outSyncFd);
- status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
+ sp<GraphicBuffer>* outBuffer, ui::PixelFormat,
bool useIdentityTransform, bool& outCapturedSecureLayers);
- status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
- bool regionSampling, bool& outCapturedSecureLayers);
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, const sp<GraphicBuffer>&,
+ bool useIdentityTransform, bool regionSampling,
+ bool& outCapturedSecureLayers);
sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
status_t captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool forSystem, int* outSyncFd, bool regionSampling,
bool& outCapturedSecureLayers);
- void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor);
+ void traverseLayersInLayerStack(ui::LayerStack, const LayerVector::Visitor&);
sp<StartPropertySetThread> mStartPropertySetThread;
@@ -777,8 +783,6 @@
return getDefaultDisplayDeviceLocked();
}
- std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const;
-
// mark a region of a layer stack dirty. this updates the dirty
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
@@ -1001,7 +1005,7 @@
bool mTransactionPending = false;
bool mAnimTransactionPending = false;
SortedVector<sp<Layer>> mLayersPendingRemoval;
- bool mTraversalNeededMainThread = false;
+ bool mForceTraversal = false;
// global color transform states
Daltonizer mDaltonizer;
@@ -1011,6 +1015,10 @@
// Can't be unordered_set because wp<> isn't hashable
std::set<wp<IBinder>> mGraphicBufferProducerList;
size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
+ // If there are more GraphicBufferProducers tracked by SurfaceFlinger than
+ // this threshold, then begin logging.
+ size_t mGraphicBufferProducerListSizeLogThreshold =
+ static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
void removeGraphicBufferProducerAsync(const wp<IBinder>&);
@@ -1209,6 +1217,7 @@
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
std::atomic<nsecs_t> mExpectedPresentTime = 0;
+ hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
/* ------------------------------------------------------------------------
* Generic Layer Metadata
@@ -1279,10 +1288,6 @@
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
- // Flags to capture the state of Vsync in HWC
- hal::Vsync mHWCVsyncState = hal::Vsync::DISABLE;
- hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
-
// Fields tracking the current jank event: when it started and how many
// janky frames there are.
nsecs_t mMissedFrameJankStart = 0;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index c90b1b8..894ee6d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -87,7 +87,9 @@
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
- StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
+ const float averageTime = iter->second.averageTime();
+ const float averageFPS = averageTime < 1.0f ? 0.0f : 1000.0f / averageTime;
+ StringAppendF(&result, "averageFPS = %.3f\n", averageFPS);
}
for (const auto& ele : deltas) {
StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 0cdff8f..ca24493 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -154,6 +154,9 @@
status_t TransactionCompletedThread::finalizePendingCallbackHandles(
const std::deque<sp<CallbackHandle>>& handles) {
+ if (handles.empty()) {
+ return NO_ERROR;
+ }
std::lock_guard lock(mMutex);
if (!mRunning) {
ALOGE("cannot add presented callback handle because the callback thread isn't running");
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7574ff1..3c4a791 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -62,6 +62,7 @@
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
+ "VSyncModulatorTest.cpp",
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 32d722e..456891e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -233,23 +233,22 @@
constexpr bool forSystem = true;
constexpr bool regionSampling = false;
- DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
- ui::Transform::ROT_0);
+ auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
+ ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
- return mFlinger.traverseLayersInDisplay(mDisplay, visitor);
+ return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(), visitor);
};
// TODO: Eliminate expensive/real allocation if possible.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
- mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
+ mCaptureScreenBuffer = new GraphicBuffer(renderArea->getReqWidth(), renderArea->getReqHeight(),
HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
int fd = -1;
status_t result =
- mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(),
+ mFlinger.captureScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer.get(),
useIdentityTransform, forSystem, &fd, regionSampling);
if (fd >= 0) {
close(fd);
@@ -348,7 +347,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -397,7 +396,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -648,7 +647,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -697,7 +696,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -775,7 +774,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([](const renderengine::DisplaySettings& displaySettings,
const std::vector<const renderengine::LayerSettings*>& layerSettings,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*) -> status_t {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 2a0e913..cc6a60c 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -22,6 +22,8 @@
#include "DisplayHardware/DisplayIdentification.h"
+using ::testing::ElementsAre;
+
namespace android {
namespace {
@@ -312,86 +314,85 @@
using ManufactureYear = DeviceProductInfo::ManufactureYear;
using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;
using ModelYear = DeviceProductInfo::ModelYear;
- using RelativeAddress = DeviceProductInfo::RelativeAddress;
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("", info.name.data());
+ EXPECT_EQ("", info.name);
EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
- EXPECT_STREQ("12610", info.productId.data());
+ EXPECT_EQ("12610", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("HP ZR30w", info.name.data());
+ EXPECT_EQ("HP ZR30w", info.name);
EXPECT_STREQ("HWP", info.manufacturerPnpId.data());
- EXPECT_STREQ("10348", info.productId.data());
+ EXPECT_EQ("10348", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2012, date.year);
EXPECT_EQ(2, date.week);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("SAMSUNG", info.name.data());
+ EXPECT_EQ("SAMSUNG", info.name);
EXPECT_STREQ("SAM", info.manufacturerPnpId.data());
- EXPECT_STREQ("2302", info.productId.data());
+ EXPECT_EQ("2302", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2011, date.year);
EXPECT_EQ(41, date.week);
- EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("Panasonic-TV", info.name.data());
+ EXPECT_EQ("Panasonic-TV", info.name);
EXPECT_STREQ("MEI", info.manufacturerPnpId.data());
- EXPECT_STREQ("41622", info.productId.data());
+ EXPECT_EQ("41622", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate);
EXPECT_EQ(2019, date.year);
- EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("Hisense", info.name.data());
+ EXPECT_EQ("Hisense", info.name);
EXPECT_STREQ("HEC", info.manufacturerPnpId.data());
- EXPECT_STREQ("0", info.productId.data());
+ EXPECT_EQ("0", info.productId);
ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
EXPECT_EQ(2019, date.year);
EXPECT_EQ(18, date.week);
- EXPECT_EQ((RelativeAddress{{1, 2, 3, 4}}), info.relativeAddress);
+ EXPECT_THAT(info.relativeAddress, ElementsAre(1, 2, 3, 4));
}
{
const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid());
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("LP2361", info.name.data());
+ EXPECT_EQ("LP2361", info.name);
EXPECT_STREQ("CTL", info.manufacturerPnpId.data());
- EXPECT_STREQ("9373", info.productId.data());
+ EXPECT_EQ("9373", info.productId);
ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate));
EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year);
- EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+ EXPECT_TRUE(info.relativeAddress.empty());
}
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index ce5f35c..9130b04 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -23,6 +23,7 @@
#include <type_traits>
+#include <android/hardware/power/Boost.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/impl/Display.h>
@@ -57,6 +58,8 @@
namespace hal = android::hardware::graphics::composer::hal;
+using android::hardware::power::Boost;
+
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
@@ -1347,6 +1350,30 @@
}
/* ------------------------------------------------------------------------
+ * SurfaceFlinger::notifyPowerBoost
+ */
+
+TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+ mFlinger.scheduler()->replaceTouchTimer(100);
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
+ std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
+}
+
+/* ------------------------------------------------------------------------
* DisplayDevice::GetBestColorMode
*/
class GetBestColorModeTest : public DisplayTransactionTest {
@@ -1794,7 +1821,7 @@
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- state.physical = {*displayId, *connectionType, *hwcDisplayId};
+ state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
}
state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -1970,7 +1997,9 @@
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
+ expectedPhysical = {.id = *displayId,
+ .type = *connectionType,
+ .hwcDisplayId = *hwcDisplayId};
}
// The display should have been set up in the current display state
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index f376b4a..afd2b71 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -38,7 +38,9 @@
static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
- static constexpr auto PRESENT_TIME_HISTORY_TIME = LayerInfoV2::HISTORY_TIME;
+ static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfoV2::HISTORY_DURATION;
+ static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
+ LayerInfoV2::RefreshRateHistory::HISTORY_DURATION;
static constexpr float LO_FPS = 30.f;
static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
@@ -84,6 +86,23 @@
return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
}
+ void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
+ float desiredRefreshRate, int numFrames) {
+ const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
+ impl::LayerHistoryV2::Summary summary;
+ for (int i = 0; i < numFrames; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += framePeriod;
+
+ summary = history().summarize(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
+ << "Frame rate is " << frameRate;
+ }
+
Hwc2::mock::Display mDisplay;
RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
.setVsyncPeriod(int32_t(LO_FPS_PERIOD))
@@ -346,14 +365,17 @@
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
+ impl::LayerHistoryV2::Summary summary;
+
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -361,28 +383,30 @@
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
// layer1 is still active but infrequent.
history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -396,33 +420,36 @@
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[1].vote);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ summary = history().summarize(time);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer1 expires.
layer1.clear();
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ summary = history().summarize(time);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
EXPECT_EQ(2, layerCount());
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -432,37 +459,41 @@
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer2 expires.
layer2.clear();
- EXPECT_TRUE(history().summarize(time).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(summary.empty());
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
// layer3 becomes active and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer3 expires.
layer3.clear();
- EXPECT_TRUE(history().summarize(time).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(summary.empty());
EXPECT_EQ(0, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -600,6 +631,46 @@
EXPECT_EQ(1, animatingLayerCount(time));
}
+TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+ for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+ recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ }
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayer60_30Hz) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
class LayerHistoryTestV2Parameterized
: public LayerHistoryTestV2,
public testing::WithParamInterface<std::chrono::nanoseconds> {};
@@ -642,7 +713,7 @@
infrequentLayerUpdates++;
}
- if (time - startTime > PRESENT_TIME_HISTORY_TIME.count()) {
+ if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
ASSERT_NE(0, history().summarize(time).size());
ASSERT_GE(2, history().summarize(time).size());
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index c919e93..fed591c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -43,6 +43,14 @@
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
+ float findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, float frameRate) {
+ return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
+ }
+
+ std::vector<float> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) {
+ return refreshRateConfigs.mKnownFrameRates;
+ }
+
// Test config IDs
static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
@@ -335,7 +343,6 @@
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
HWC_CONFIG_ID_72);
@@ -344,17 +351,14 @@
// range.
auto layers = std::vector<LayerRequirement>{};
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
- false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
- false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -365,163 +369,134 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Heuristic";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
lr.name = "45Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
lr.name = "30Hz Heuristic";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
lr.name = "24Hz Heuristic";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.name = "";
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_72_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -531,43 +506,35 @@
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -582,28 +549,24 @@
lr2.desiredRefreshRate = 60.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -620,8 +583,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -630,8 +592,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -640,8 +601,7 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -650,8 +610,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -660,8 +619,7 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -670,8 +628,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
@@ -680,8 +637,7 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -690,8 +646,7 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -700,12 +655,10 @@
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -715,43 +668,35 @@
lr.vote = LayerVoteType::Min;
EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -762,71 +707,57 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Heuristic";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr.desiredRefreshRate = 45.0f;
lr.name = "45Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr.desiredRefreshRate = 30.0f;
lr.name = "30Hz Heuristic";
EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr.desiredRefreshRate = 24.0f;
lr.name = "24Hz Heuristic";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr.desiredRefreshRate = 24.0f;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.name = "24Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -839,56 +770,48 @@
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Max;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 24.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 15.0f;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 30.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 45.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -900,10 +823,8 @@
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = fps;
const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored);
- printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str());
- EXPECT_EQ(mExpected60Config, refreshRate);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+ EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
}
}
@@ -931,7 +852,6 @@
}
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -946,24 +866,21 @@
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 90.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 90.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 90.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, testInPolicy) {
@@ -975,7 +892,6 @@
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -987,15 +903,12 @@
for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
lr.desiredRefreshRate = fps;
const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored);
- printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str());
- EXPECT_EQ(mExpected90Config, refreshRate);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+ EXPECT_EQ(mExpected90Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
}
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -1012,8 +925,7 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1022,8 +934,7 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1031,8 +942,7 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30.0f;
@@ -1041,8 +951,7 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30.0f;
@@ -1050,12 +959,10 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
- /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
@@ -1071,7 +978,7 @@
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1079,7 +986,7 @@
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1087,7 +994,7 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1095,7 +1002,7 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
// The other layer starts to provide buffers
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1105,20 +1012,20 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, touchConsidered) {
- bool touchConsidered;
+ RefreshRateConfigs::GlobalSignals consideredSignals;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- refreshRateConfigs->getBestRefreshRate({}, false, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false}, &consideredSignals);
+ EXPECT_EQ(false, consideredSignals.touch);
- refreshRateConfigs->getBestRefreshRate({}, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(true, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = false}, &consideredSignals);
+ EXPECT_EQ(true, consideredSignals.touch);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
LayerRequirement{.weight = 1.0f}};
@@ -1131,8 +1038,9 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(true, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+ &consideredSignals);
+ EXPECT_EQ(true, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 60.0f;
@@ -1140,8 +1048,9 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+ &consideredSignals);
+ EXPECT_EQ(false, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -1149,8 +1058,9 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(true, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+ &consideredSignals);
+ EXPECT_EQ(true, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 60.0f;
@@ -1158,12 +1068,12 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+ &consideredSignals);
+ EXPECT_EQ(false, consideredSignals.touch);
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
- bool ignored;
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/
HWC_CONFIG_ID_60);
@@ -1199,7 +1109,7 @@
lr.name = ss.str();
const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
EXPECT_FLOAT_EQ(refreshRate.getFps(), test.second)
<< "Expecting " << test.first << "fps => " << test.second << "Hz";
}
@@ -1218,22 +1128,22 @@
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
auto& lr = layers[0];
- bool touchConsidered = false;
+ RefreshRateConfigs::GlobalSignals consideredSignals;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true, /*idle*/ false,
- &touchConsidered));
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = true},
+ &consideredSignals));
+ EXPECT_EQ(false, consideredSignals.touch);
lr.vote = LayerVoteType::ExplicitDefault;
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz ExplicitDefault";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true, /*idle*/ false,
- &touchConsidered));
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = true},
+ &consideredSignals));
+ EXPECT_EQ(false, consideredSignals.touch);
}
TEST_F(RefreshRateConfigsTest,
@@ -1249,20 +1159,17 @@
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
auto& lr = layers[0];
- bool touchConsidered = false;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.desiredRefreshRate = 90.0f;
lr.name = "90Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ true,
- &touchConsidered));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = true}));
lr.vote = LayerVoteType::ExplicitDefault;
lr.desiredRefreshRate = 90.0f;
lr.name = "90Hz ExplicitDefault";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ true,
- &touchConsidered));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = true}));
}
TEST_F(RefreshRateConfigsTest,
@@ -1275,11 +1182,11 @@
{HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
0);
- bool touchConsidered = false;
+ RefreshRateConfigs::GlobalSignals consideredSignals;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate({}, /*touchActive*/ false, /*idle*/ false,
- &touchConsidered));
- EXPECT_EQ(false, touchConsidered);
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false},
+ &consideredSignals));
+ EXPECT_EQ(false, consideredSignals.touch);
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
auto& lr = layers[0];
@@ -1288,36 +1195,31 @@
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz ExplicitExactOrMultiple";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
- &touchConsidered));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::ExplicitDefault;
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz ExplicitDefault";
EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
- &touchConsidered));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Heuristic;
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Heuristic";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
- &touchConsidered));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Max;
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Max";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
- &touchConsidered));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
lr.vote = LayerVoteType::Min;
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Min";
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false, /*idle*/ false,
- &touchConsidered));
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
TEST_F(RefreshRateConfigsTest, groupSwitching) {
@@ -1331,10 +1233,8 @@
layer.desiredRefreshRate = 90.0f;
layer.name = "90Hz ExplicitDefault";
- bool touchConsidered;
ASSERT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs
- ->getBestRefreshRate(layers, false, /*idle*/ false, &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getConfigId());
RefreshRateConfigs::Policy policy;
@@ -1342,8 +1242,7 @@
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
ASSERT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs
- ->getBestRefreshRate(layers, false, /*idle*/ false, &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getConfigId());
}
@@ -1361,20 +1260,15 @@
bool touchActive = false) -> HwcConfigIndexType {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = fps;
- bool touchConsidered;
- return refreshRateConfigs
- ->getBestRefreshRate(layers, touchActive, /*idle*/ false, &touchConsidered)
+ return refreshRateConfigs->getBestRefreshRate(layers, {.touch = touchActive, .idle = false})
.getConfigId();
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
{HWC_CONFIG_ID_60, {30.f, 60.f}, {30.f, 90.f}}),
0);
- bool touchConsidered;
EXPECT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs
- ->getBestRefreshRate({}, /*touchActive=*/false, /*idle*/ false,
- &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false})
.getConfigId());
EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90.f));
@@ -1410,13 +1304,19 @@
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
layers[0].name = "Test layer";
- auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> HwcConfigIndexType {
+ const auto getIdleFrameRate = [&](LayerVoteType voteType,
+ bool touchActive) -> HwcConfigIndexType {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90.f;
- bool touchConsidered;
- return refreshRateConfigs
- ->getBestRefreshRate(layers, touchActive, /*idle=*/true, &touchConsidered)
- .getConfigId();
+ RefreshRateConfigs::GlobalSignals consideredSignals;
+ const auto configId =
+ refreshRateConfigs
+ ->getBestRefreshRate(layers, {.touch = touchActive, .idle = true},
+ &consideredSignals)
+ .getConfigId();
+ // Refresh rate will be chosen by either touch state or idle state
+ EXPECT_EQ(!touchActive, consideredSignals.idle);
+ return configId;
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
@@ -1424,38 +1324,130 @@
0);
// Idle should be lower priority than touch boost.
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::ExplicitDefault, true));
- EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, true));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90,
+ getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_90,
+ getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/true));
// With no layers, idle should still be lower priority than touch boost.
- bool touchConsidered;
EXPECT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs
- ->getBestRefreshRate({}, /*touchActive=*/true, /*idle=*/true,
- &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true})
.getConfigId());
// Idle should be higher precedence than other layer frame rate considerations.
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::ExplicitDefault, false));
- EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, false));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60,
+ getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/false));
+ EXPECT_EQ(HWC_CONFIG_ID_60,
+ getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/false));
// Idle should be applied rather than the current config when there are no layers.
EXPECT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs
- ->getBestRefreshRate({}, /*touchActive=*/false, /*idle=*/true,
- &touchConsidered)
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = true})
.getConfigId());
}
+TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
+ const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, fps);
+ float expectedFrameRate;
+ if (fps < 26.91f) {
+ expectedFrameRate = 24.0f;
+ } else if (fps < 37.51f) {
+ expectedFrameRate = 30.0f;
+ } else if (fps < 52.51f) {
+ expectedFrameRate = 45.0f;
+ } else if (fps < 66.01f) {
+ expectedFrameRate = 60.0f;
+ } else if (fps < 81.01f) {
+ expectedFrameRate = 72.0f;
+ } else {
+ expectedFrameRate = 90.0f;
+ }
+ EXPECT_FLOAT_EQ(expectedFrameRate, knownFrameRate)
+ << "findClosestKnownFrameRate(" << fps << ") = " << knownFrameRate;
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ struct ExpectedRate {
+ float rate;
+ const RefreshRate& expected;
+ };
+
+ /* clang-format off */
+ std::vector<ExpectedRate> knownFrameRatesExpectations = {
+ {24.0f, mExpected60Config},
+ {30.0f, mExpected60Config},
+ {45.0f, mExpected90Config},
+ {60.0f, mExpected60Config},
+ {72.0f, mExpected90Config},
+ {90.0f, mExpected90Config},
+ };
+ /* clang-format on */
+
+ // Make sure the test tests all the known frame rate
+ const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs);
+ const auto equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
+ knownFrameRatesExpectations.begin(),
+ [](float a, const ExpectedRate& b) { return a == b.rate; });
+ EXPECT_TRUE(equal);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::Heuristic;
+ for (const auto& expectedRate : knownFrameRatesExpectations) {
+ layer.desiredRefreshRate = expectedRate.rate;
+ const auto& refreshRate =
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+ EXPECT_EQ(expectedRate.expected, refreshRate);
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
+ EXPECT_TRUE(mExpected60Config < mExpected90Config);
+ EXPECT_FALSE(mExpected60Config < mExpected60Config);
+ EXPECT_FALSE(mExpected90Config < mExpected90Config);
+}
+
+TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
+ using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_90);
+ // SetPolicy(60, 90), current 90Hz => TurnOn.
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(60, 90), current 60Hz => TurnOn.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 90}}), 0);
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(60, 60), current 60Hz => NoChange, avoid extra calls.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+ EXPECT_EQ(KernelIdleTimerAction::NoChange, refreshRateConfigs->getIdleTimerAction());
+
+ // SetPolicy(90, 90), current 90Hz => TurnOff.
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+ EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 41b5d49..b7067a6 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -31,7 +31,7 @@
TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
: Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
} else {
mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
}
@@ -43,7 +43,7 @@
: Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
useContentDetectionV2, true) {
if (mUseContentDetectionV2) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
} else {
mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
}
@@ -72,13 +72,31 @@
auto& mutableEventControlThread() { return mEventControlThread; }
auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+
auto mutableLayerHistory() {
return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
}
+
auto mutableLayerHistoryV2() {
return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
}
+ void replaceTouchTimer(int64_t millis) {
+ if (mTouchTimer) {
+ mTouchTimer.reset();
+ }
+ mTouchTimer.emplace(
+ std::chrono::milliseconds(millis),
+ [this] { touchTimerCallback(TimerState::Reset); },
+ [this] { touchTimerCallback(TimerState::Expired); });
+ mTouchTimer->start();
+ }
+
+ bool isTouchActive() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ return mFeatures.touch == Scheduler::TouchState::Active;
+ }
+
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the Scheduler instance may
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 38bc8a1..f630103 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -321,6 +321,8 @@
return mFlinger->onInitializeDisplays();
}
+ auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
+
// Allow reading display state without locking, as if called on the SF main thread.
auto setPowerModeInternal(const sp<DisplayDevice>& display,
hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
@@ -331,7 +333,7 @@
auto captureScreenImplLocked(const RenderArea& renderArea,
SurfaceFlinger::TraverseLayersFunction traverseLayers,
- ANativeWindowBuffer* buffer, bool useIdentityTransform,
+ const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool forSystem, int* outSyncFd, bool regionSampling) {
bool ignored;
return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
@@ -339,9 +341,9 @@
regionSampling, ignored);
}
- auto traverseLayersInDisplay(const sp<const DisplayDevice>& display,
- const LayerVector::Visitor& visitor) {
- return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor);
+ auto traverseLayersInLayerStack(ui::LayerStack layerStack,
+ const LayerVector::Visitor& visitor) {
+ return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, visitor);
}
auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -633,7 +635,7 @@
if (const auto type = mCreationArgs.connectionType) {
LOG_ALWAYS_FATAL_IF(!displayId);
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
- state.physical = {*displayId, *type, *mHwcDisplayId};
+ state.physical = {.id = *displayId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
}
state.isSecure = mCreationArgs.isSecure;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 7a1c7c6..63a34af 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -833,6 +833,15 @@
ASSERT_EQ(0, globalProto.stats_size());
}
+TEST_F(TimeStatsTest, noInfInAverageFPS) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 1000000);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ EXPECT_THAT(result, HasSubstr("averageFPS = 0.000"));
+}
+
namespace {
std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
const std::vector<int32_t>& frameCounts) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 793cb8b..d940dc5 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -701,6 +701,52 @@
EXPECT_THAT(cb.mCalls.size(), Eq(1));
}
+// b/154303580.
+TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(620);
+
+ EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled);
+
+ mMockClock.advanceBy(80);
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 1280)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+ CountingCallback cb1(mDispatch);
+ CountingCallback cb2(mDispatch);
+
+ EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+
+ mMockClock.setLag(100);
+ mMockClock.advanceBy(620);
+
+ EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled);
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+ mMockClock.advanceToNextCallback();
+
+ EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+ EXPECT_THAT(cb2.mCalls.size(), Eq(1));
+}
+
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
protected:
nsecs_t const mPeriod = 1000;
diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
new file mode 100644
index 0000000..9c1ec07
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2020 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/VSyncModulator.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class MockScheduler : public IPhaseOffsetControl {
+public:
+ void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
+ mPhaseOffset[handle] = phaseOffset;
+ }
+
+ nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; }
+
+private:
+ std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset;
+};
+
+class VSyncModulatorTest : public testing::Test {
+protected:
+ static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION =
+ VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION;
+ // Add a 1ms slack to avoid strange timer race conditions.
+ static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms;
+
+ // Used to enumerate the different offsets we have
+ enum {
+ SF_LATE,
+ APP_LATE,
+ SF_EARLY,
+ APP_EARLY,
+ SF_EARLY_GL,
+ APP_EARLY_GL,
+ };
+
+ std::unique_ptr<VSyncModulator> mVSyncModulator;
+ MockScheduler mMockScheduler;
+ ConnectionHandle mAppConnection{1};
+ ConnectionHandle mSfConnection{2};
+ VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY},
+ {SF_EARLY_GL, APP_EARLY_GL},
+ {SF_LATE, APP_LATE}};
+
+ void SetUp() override {
+ mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection,
+ mSfConnection, mOffsets);
+ mVSyncModulator->setPhaseOffsets(mOffsets);
+
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+ };
+
+ void TearDown() override { mVSyncModulator.reset(); }
+};
+
+TEST_F(VSyncModulatorTest, Normal) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+ }
+}
+
+TEST_F(VSyncModulatorTest, EarlyEnd) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStart) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartWithEarly) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ccbd17f..a972562 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -672,15 +672,30 @@
kPendingLimit, true /* supportKernelIdleTimer */);
bool periodFlushed = true;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(5);
idleReactor.setIgnorePresentFences(true);
- nsecs_t const newPeriod = 5000;
- idleReactor.setPeriod(newPeriod);
-
+ // First, set the same period, which should only be confirmed when we receive two
+ // matching callbacks
+ idleReactor.setPeriod(10000);
EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(idleReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+ // Correct period but incorrect timestamp delta
+ EXPECT_TRUE(idleReactor.addResyncSample(0, 10000, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ // Correct period and correct timestamp delta
+ EXPECT_FALSE(idleReactor.addResyncSample(10000, 10000, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+
+ // Then, set a new period, which should be confirmed as soon as we receive a callback
+ // reporting the new period
+ nsecs_t const newPeriod = 5000;
+ idleReactor.setPeriod(newPeriod);
+ // Incorrect timestamp delta and period
+ EXPECT_TRUE(idleReactor.addResyncSample(20000, 10000, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ // Incorrect timestamp delta but correct period
+ EXPECT_FALSE(idleReactor.addResyncSample(20000, 5000, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
new file mode 100644
index 0000000..e3c254d
--- /dev/null
+++ b/services/vibratorservice/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+ name: "libvibratorservice",
+
+ srcs: [
+ "VibratorCallbackScheduler.cpp",
+ "VibratorHalWrapper.cpp",
+ ],
+
+ aidl: {
+ local_include_dirs: ["include"],
+ include_dirs: [
+ "hardware/interfaces/vibrator/aidl/android/hardware/vibrator",
+ ],
+ export_aidl_headers: true
+ },
+
+ shared_libs: [
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.vibrator-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ local_include_dirs: ["include"],
+
+ export_include_dirs: ["include"],
+}
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
new file mode 100644
index 0000000..3f8cd67
--- /dev/null
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 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 <chrono>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+bool DelayedCallback::isExpired() const {
+ return mExpiration <= std::chrono::steady_clock::now();
+}
+
+DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
+ return mExpiration;
+}
+
+void DelayedCallback::run() const {
+ mCallback();
+}
+
+bool DelayedCallback::operator<(const DelayedCallback& other) const {
+ return mExpiration < other.mExpiration;
+}
+
+bool DelayedCallback::operator>(const DelayedCallback& other) const {
+ return mExpiration > other.mExpiration;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+CallbackScheduler::~CallbackScheduler() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFinished = true;
+ }
+ mCondition.notify_all();
+ if (mCallbackThread && mCallbackThread->joinable()) {
+ mCallbackThread->join();
+ }
+}
+
+void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mCallbackThread == nullptr) {
+ mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this);
+ }
+ mQueue.emplace(DelayedCallback(callback, delay));
+ }
+ mCondition.notify_all();
+}
+
+void CallbackScheduler::loop() {
+ while (true) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mFinished) {
+ // Destructor was called, so let the callback thread die.
+ break;
+ }
+ while (!mQueue.empty() && mQueue.top().isExpired()) {
+ mQueue.top().run();
+ mQueue.pop();
+ }
+ if (mQueue.empty()) {
+ // Wait until a new callback is scheduled.
+ mCondition.wait(mMutex);
+ } else {
+ // Wait until next callback expires, or a new one is scheduled.
+ mCondition.wait_until(mMutex, mQueue.top().getExpiration());
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
new file mode 100644
index 0000000..1420bf5
--- /dev/null
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalWrapper"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <hardware/vibrator.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+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 android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+template <class T>
+HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) {
+ if (cache.has_value()) {
+ // Return copy of cached value.
+ return HalResult<T>::ok(*cache);
+ }
+ HalResult<T> ret = loadFn();
+ if (ret.isOk()) {
+ // Cache copy of returned value.
+ cache.emplace(ret.value());
+ }
+ return ret;
+}
+
+template <class T>
+bool isStaticCastValid(Effect effect) {
+ T castEffect = static_cast<T>(effect);
+ auto iter = hardware::hidl_enum_range<T>();
+ return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+template <typename T>
+HalResult<T> HalResult<T>::ok(T value) {
+ return HalResult(value);
+}
+
+template <typename T>
+HalResult<T> HalResult<T>::failed() {
+ return HalResult(/* unsupported= */ false);
+}
+
+template <typename T>
+HalResult<T> HalResult<T>::unsupported() {
+ return HalResult(/* unsupported= */ true);
+}
+
+template <typename T>
+HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(data);
+ }
+ return HalResult<T>::failed();
+}
+
+template <typename T>
+HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) {
+ switch (status) {
+ case V1_0::Status::OK:
+ return HalResult<T>::ok(data);
+ case V1_0::Status::UNSUPPORTED_OPERATION:
+ return HalResult<T>::unsupported();
+ default:
+ return HalResult<T>::failed();
+ }
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
+ return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed();
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, data) : HalResult<T>::failed();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HalResult<void>::ok() {
+ return HalResult();
+}
+
+HalResult<void> HalResult<void>::failed() {
+ return HalResult(/* failed= */ true);
+}
+
+HalResult<void> HalResult<void>::unsupported() {
+ return HalResult(/* failed= */ false, /* unsupported= */ true);
+}
+
+HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
+ if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed();
+}
+
+HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
+ switch (status) {
+ case V1_0::Status::OK:
+ return HalResult<void>::ok();
+ case V1_0::Status::UNSUPPORTED_OPERATION:
+ return HalResult<void>::unsupported();
+ default:
+ return HalResult<void>::failed();
+ }
+}
+
+template <typename R>
+HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class HalCallbackWrapper : public Aidl::BnVibratorCallback {
+public:
+ HalCallbackWrapper(std::function<void()> completionCallback)
+ : mCompletionCallback(completionCallback) {}
+
+ binder::Status onComplete() override {
+ mCompletionCallback();
+ return binder::Status::ok();
+ }
+
+private:
+ const std::function<void()> mCompletionCallback;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> AidlHalWrapper::ping() {
+ return IInterface::asBinder(mHandle)->pingBinder() ? HalResult<void>::ok()
+ : HalResult<void>::failed();
+}
+
+HalResult<void> AidlHalWrapper::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ HalResult<Capabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+ auto ret = HalResult<void>::fromStatus(mHandle->on(timeout.count(), cb));
+ if (!supportsCallback && ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, timeout);
+ }
+
+ return ret;
+}
+
+HalResult<void> AidlHalWrapper::off() {
+ return HalResult<void>::fromStatus(mHandle->off());
+}
+
+HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) {
+ float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max();
+ return HalResult<void>::fromStatus(mHandle->setAmplitude(convertedAmplitude));
+}
+
+HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
+ return HalResult<void>::fromStatus(mHandle->setExternalControl(enabled));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+ return HalResult<void>::fromStatus(mHandle->alwaysOnEnable(id, effect, strength));
+}
+
+HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
+ return HalResult<void>::fromStatus(mHandle->alwaysOnDisable(id));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilities() {
+ std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+ return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this),
+ mCapabilities);
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() {
+ std::lock_guard<std::mutex> lock(mSupportedEffectsMutex);
+ return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal,
+ this),
+ mSupportedEffects);
+}
+
+HalResult<milliseconds> AidlHalWrapper::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ HalResult<Capabilities> capabilities = getCapabilities();
+ bool supportsCallback = capabilities.isOk() &&
+ static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK);
+ auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
+
+ int32_t lengthMs;
+ auto result = mHandle->perform(effect, strength, cb, &lengthMs);
+ milliseconds length = milliseconds(lengthMs);
+
+ auto ret = HalResult<milliseconds>::fromStatus(result, length);
+ if (!supportsCallback && ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, length);
+ }
+
+ return ret;
+}
+
+HalResult<void> AidlHalWrapper::performComposedEffect(
+ const std::vector<CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) {
+ // This method should always support callbacks, so no need to double check.
+ auto cb = new HalCallbackWrapper(completionCallback);
+ return HalResult<void>::fromStatus(mHandle->compose(primitiveEffects, cb));
+}
+
+HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
+ int32_t capabilities = 0;
+ auto result = mHandle->getCapabilities(&capabilities);
+ return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
+}
+
+HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
+ std::vector<Effect> supportedEffects;
+ auto result = mHandle->getSupportedEffects(&supportedEffects);
+ return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_0::ping() {
+ auto result = mHandleV1_0->ping();
+ return HalResult<void>::fromReturn(result);
+}
+
+HalResult<void> HidlHalWrapperV1_0::on(milliseconds timeout,
+ const std::function<void()>& completionCallback) {
+ auto result = mHandleV1_0->on(timeout.count());
+ auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+ if (ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, timeout);
+ }
+ return ret;
+}
+
+HalResult<void> HidlHalWrapperV1_0::off() {
+ auto result = mHandleV1_0->off();
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+HalResult<void> HidlHalWrapperV1_0::setAmplitude(int32_t amplitude) {
+ auto result = mHandleV1_0->setAmplitude(static_cast<uint8_t>(amplitude));
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+HalResult<void> HidlHalWrapperV1_0::setExternalControl(bool) {
+ ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> HidlHalWrapperV1_0::alwaysOnEnable(int32_t, Effect, EffectStrength) {
+ ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> HidlHalWrapperV1_0::alwaysOnDisable(int32_t) {
+ ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilities() {
+ std::lock_guard<std::mutex> lock(mCapabilitiesMutex);
+ return loadCached<Capabilities>(std::bind(&HidlHalWrapperV1_0::getCapabilitiesInternal, this),
+ mCapabilities);
+}
+
+HalResult<std::vector<Effect>> HidlHalWrapperV1_0::getSupportedEffects() {
+ ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available");
+ return HalResult<std::vector<Effect>>::unsupported();
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternalV1_0(effect, strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<void> HidlHalWrapperV1_0::performComposedEffect(const std::vector<CompositeEffect>&,
+ const std::function<void()>&) {
+ ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
+ return HalResult<void>::unsupported();
+}
+
+HalResult<Capabilities> HidlHalWrapperV1_0::getCapabilitiesInternal() {
+ hardware::Return<bool> result = mHandleV1_0->supportsAmplitudeControl();
+ Capabilities capabilities =
+ result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
+ return HalResult<Capabilities>::fromReturn(result, capabilities);
+}
+
+template <class I, class T>
+HalResult<milliseconds> HidlHalWrapperV1_0::performInternal(
+ perform_fn<I, T> performFn, sp<I> handle, T effect, EffectStrength strength,
+ const std::function<void()>& completionCallback) {
+ V1_0::Status status;
+ int32_t lengthMs;
+ auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+ status = retStatus;
+ lengthMs = retLengthMs;
+ };
+
+ V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
+ auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
+ milliseconds length = milliseconds(lengthMs);
+
+ auto ret = HalResult<milliseconds>::fromReturn(result, status, length);
+ if (ret.isOk()) {
+ mCallbackScheduler->schedule(completionCallback, length);
+ }
+
+ return ret;
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_0::performInternalV1_0(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ V1_0::Effect e = static_cast<V1_0::Effect>(effect);
+ return performInternal(&V1_0::IVibrator::perform, mHandleV1_0, e, strength, completionCallback);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternalV1_0(effect, strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternalV1_1(effect, strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_1::performInternalV1_1(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ V1_1::Effect_1_1 e = static_cast<V1_1::Effect_1_1>(effect);
+ return performInternal(&V1_1::IVibrator::perform_1_1, mHandleV1_1, e, strength,
+ completionCallback);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternalV1_0(effect, strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternalV1_1(effect, strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_2::Effect>(effect)) {
+ return performInternalV1_2(effect, strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_2::performInternalV1_2(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ V1_2::Effect e = static_cast<V1_2::Effect>(effect);
+ return performInternal(&V1_2::IVibrator::perform_1_2, mHandleV1_2, e, strength,
+ completionCallback);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
+ auto result = mHandleV1_3->setExternalControl(static_cast<uint32_t>(enabled));
+ return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ if (isStaticCastValid<V1_0::Effect>(effect)) {
+ return performInternalV1_0(effect, strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
+ return performInternalV1_1(effect, strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_2::Effect>(effect)) {
+ return performInternalV1_2(effect, strength, completionCallback);
+ }
+ if (isStaticCastValid<V1_3::Effect>(effect)) {
+ return performInternalV1_3(effect, strength, completionCallback);
+ }
+
+ ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
+ Aidl::toString(effect).c_str());
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
+ HalResult<Capabilities> parentResult = HidlHalWrapperV1_2::getCapabilitiesInternal();
+ if (!parentResult.isOk()) {
+ // Loading for previous HAL versions already failed, so propagate failure.
+ return parentResult;
+ }
+
+ Capabilities capabilities = parentResult.value();
+ auto result = mHandleV1_3->supportsExternalControl();
+ capabilities |= result.withDefault(false) ? Capabilities::EXTERNAL_CONTROL : Capabilities::NONE;
+ return HalResult<Capabilities>::fromReturn(result, capabilities);
+}
+
+HalResult<milliseconds> HidlHalWrapperV1_3::performInternalV1_3(
+ Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
+ V1_3::Effect e = static_cast<V1_3::Effect>(effect);
+ return performInternal(&V1_3::IVibrator::perform_1_3, mHandleV1_3, e, strength,
+ completionCallback);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
new file mode 100644
index 0000000..2c194b5
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+#define ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
+
+#include <android-base/thread_annotations.h>
+#include <chrono>
+#include <condition_variable>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+namespace vibrator {
+
+// Wrapper for a callback to be executed after a delay.
+class DelayedCallback {
+public:
+ using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;
+
+ DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
+ : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
+ ~DelayedCallback() = default;
+
+ void run() const;
+ bool isExpired() const;
+ Timestamp getExpiration() const;
+
+ // Compare by expiration time, where A < B when A expires first.
+ bool operator<(const DelayedCallback& other) const;
+ bool operator>(const DelayedCallback& other) const;
+
+private:
+ std::function<void()> mCallback;
+ Timestamp mExpiration;
+};
+
+// Schedules callbacks to be executed after a delay.
+class CallbackScheduler {
+public:
+ CallbackScheduler() : mCallbackThread(nullptr), mFinished(false) {}
+ virtual ~CallbackScheduler();
+
+ virtual void schedule(std::function<void()> callback, std::chrono::milliseconds delay);
+
+private:
+ std::condition_variable_any mCondition;
+ std::mutex mMutex;
+
+ // Lazily instantiated only at the first time this scheduler is used.
+ std::unique_ptr<std::thread> mCallbackThread;
+
+ // Used to quit the callback thread when this instance is being destroyed.
+ bool mFinished GUARDED_BY(mMutex);
+
+ // Priority queue with reverse comparator, so tasks that expire first will be on top.
+ std::priority_queue<DelayedCallback, std::vector<DelayedCallback>,
+ std::greater<DelayedCallback>>
+ mQueue GUARDED_BY(mMutex);
+
+ void loop();
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
new file mode 100644
index 0000000..0f9aacb
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_OS_VIBRATORHALWRAPPER_H
+#define ANDROID_OS_VIBRATORHALWRAPPER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+// Result of a call to the Vibrator HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T value);
+ static HalResult<T> failed();
+ static HalResult<T> unsupported();
+
+ static HalResult<T> fromStatus(binder::Status status, T data);
+ static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret,
+ hardware::vibrator::V1_0::Status status, T data);
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+
+private:
+ std::optional<T> mValue;
+ bool mUnsupported;
+
+ explicit HalResult(T value) : mValue(std::make_optional(value)), mUnsupported(false) {}
+ explicit HalResult(bool unsupported) : mValue(), mUnsupported(unsupported) {}
+};
+
+// Empty result of a call to the Vibrator HAL wrapper.
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok();
+ static HalResult<void> failed();
+ static HalResult<void> unsupported();
+
+ static HalResult<void> fromStatus(binder::Status status);
+ static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status);
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret);
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+
+private:
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool failed = false, bool unsupported = false)
+ : mFailed(failed), mUnsupported(unsupported) {}
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Vibrator HAL capabilities.
+enum class Capabilities : int32_t {
+ NONE = 0,
+ ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK,
+ PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK,
+ AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL,
+ EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL,
+ EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL,
+ COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS,
+ ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL
+};
+
+inline Capabilities operator|(Capabilities lhs, Capabilities rhs) {
+ using underlying = typename std::underlying_type<Capabilities>::type;
+ return static_cast<Capabilities>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator|=(Capabilities& lhs, Capabilities rhs) {
+ return lhs = lhs | rhs;
+}
+
+inline Capabilities operator&(Capabilities lhs, Capabilities rhs) {
+ using underlying = typename std::underlying_type<Capabilities>::type;
+ return static_cast<Capabilities>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs));
+}
+
+inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) {
+ return lhs = lhs & rhs;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// Wrapper for Vibrator HAL handlers.
+class HalWrapper {
+public:
+ explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler)
+ : mCallbackScheduler(std::move(scheduler)) {}
+ virtual ~HalWrapper() = default;
+
+ virtual HalResult<void> ping() = 0;
+
+ virtual HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) = 0;
+ virtual HalResult<void> off() = 0;
+
+ virtual HalResult<void> setAmplitude(int32_t amplitude) = 0;
+ virtual HalResult<void> setExternalControl(bool enabled) = 0;
+
+ virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) = 0;
+ virtual HalResult<void> alwaysOnDisable(int32_t id) = 0;
+
+ virtual HalResult<Capabilities> getCapabilities() = 0;
+ virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) = 0;
+
+ virtual HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) = 0;
+
+protected:
+ // Shared pointer to allow CallbackScheduler to outlive this wrapper.
+ const std::shared_ptr<CallbackScheduler> mCallbackScheduler;
+};
+
+// Wrapper for the AIDL Vibrator HAL.
+class AidlHalWrapper : public HalWrapper {
+public:
+ AidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::IVibrator> handle)
+ : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
+ virtual ~AidlHalWrapper() = default;
+
+ virtual HalResult<void> ping() override;
+
+ virtual HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override;
+ virtual HalResult<void> off() override;
+
+ virtual HalResult<void> setAmplitude(int32_t amplitude) override;
+ virtual HalResult<void> setExternalControl(bool enabled) override;
+
+ virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override;
+ virtual HalResult<void> alwaysOnDisable(int32_t id) override;
+
+ virtual HalResult<Capabilities> getCapabilities() override;
+ virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override;
+
+ virtual HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) override;
+
+private:
+ const sp<hardware::vibrator::IVibrator> mHandle;
+ std::mutex mCapabilitiesMutex;
+ std::mutex mSupportedEffectsMutex;
+ std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+ std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
+ GUARDED_BY(mSupportedEffectsMutex);
+
+ // Loads directly from IVibrator handle, skipping caches.
+ HalResult<Capabilities> getCapabilitiesInternal();
+ HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.0.
+class HidlHalWrapperV1_0 : public HalWrapper {
+public:
+ HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_0::IVibrator> handle)
+ : HalWrapper(std::move(scheduler)), mHandleV1_0(std::move(handle)) {}
+ virtual ~HidlHalWrapperV1_0() = default;
+
+ virtual HalResult<void> ping() override;
+
+ virtual HalResult<void> on(std::chrono::milliseconds timeout,
+ const std::function<void()>& completionCallback) override;
+ virtual HalResult<void> off() override;
+
+ virtual HalResult<void> setAmplitude(int32_t amplitude) override;
+ virtual HalResult<void> setExternalControl(bool enabled) override;
+
+ virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect,
+ hardware::vibrator::EffectStrength strength) override;
+ virtual HalResult<void> alwaysOnDisable(int32_t id) override;
+
+ virtual HalResult<Capabilities> getCapabilities() override;
+ virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override;
+
+ virtual HalResult<void> performComposedEffect(
+ const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
+ const std::function<void()>& completionCallback) override;
+
+protected:
+ const sp<hardware::vibrator::V1_0::IVibrator> mHandleV1_0;
+ std::mutex mCapabilitiesMutex;
+ std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
+
+ // Loads directly from IVibrator handle, skipping the mCapabilities cache.
+ virtual HalResult<Capabilities> getCapabilitiesInternal();
+
+ template <class I, class T>
+ using perform_fn =
+ hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
+ hardware::vibrator::V1_0::IVibrator::perform_cb);
+
+ template <class I, class T>
+ HalResult<std::chrono::milliseconds> performInternal(
+ perform_fn<I, T> performFn, sp<I> handle, T effect,
+ hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback);
+
+ HalResult<std::chrono::milliseconds> performInternalV1_0(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback);
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.1.
+class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
+public:
+ HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
+ : HidlHalWrapperV1_0(std::move(scheduler), handleV1_0),
+ mHandleV1_1(hardware::vibrator::V1_1::IVibrator::castFrom(handleV1_0)) {}
+ virtual ~HidlHalWrapperV1_1() = default;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override;
+
+protected:
+ const sp<hardware::vibrator::V1_1::IVibrator> mHandleV1_1;
+
+ HalResult<std::chrono::milliseconds> performInternalV1_1(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback);
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.2.
+class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
+public:
+ HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
+ : HidlHalWrapperV1_1(std::move(scheduler), handleV1_0),
+ mHandleV1_2(hardware::vibrator::V1_2::IVibrator::castFrom(handleV1_0)) {}
+ virtual ~HidlHalWrapperV1_2() = default;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override;
+
+protected:
+ const sp<hardware::vibrator::V1_2::IVibrator> mHandleV1_2;
+
+ HalResult<std::chrono::milliseconds> performInternalV1_2(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback);
+};
+
+// Wrapper for the HDIL Vibrator HAL v1.3.
+class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
+public:
+ HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
+ sp<hardware::vibrator::V1_0::IVibrator> handleV1_0)
+ : HidlHalWrapperV1_2(std::move(scheduler), handleV1_0),
+ mHandleV1_3(hardware::vibrator::V1_3::IVibrator::castFrom(handleV1_0)) {}
+ virtual ~HidlHalWrapperV1_3() = default;
+
+ virtual HalResult<void> setExternalControl(bool enabled) override;
+
+ virtual HalResult<std::chrono::milliseconds> performEffect(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback) override;
+
+protected:
+ const sp<hardware::vibrator::V1_3::IVibrator> mHandleV1_3;
+
+ virtual HalResult<Capabilities> getCapabilitiesInternal() override;
+ HalResult<std::chrono::milliseconds> performInternalV1_3(
+ hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
+ const std::function<void()>& completionCallback);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATORHALWRAPPER_H
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
new file mode 100644
index 0000000..fa399ad
--- /dev/null
+++ b/services/vibratorservice/test/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "libvibratorservice_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "VibratorCallbackSchedulerTest.cpp",
+ "VibratorHalWrapperAidlTest.cpp",
+ "VibratorHalWrapperHidlV1_0Test.cpp",
+ "VibratorHalWrapperHidlV1_1Test.cpp",
+ "VibratorHalWrapperHidlV1_2Test.cpp",
+ "VibratorHalWrapperHidlV1_3Test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libvibratorservice",
+ "libutils",
+ "android.hardware.vibrator-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
new file mode 100644
index 0000000..aaeb8f9
--- /dev/null
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalWrapperAidlTest"
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <condition_variable>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+
+using std::chrono::milliseconds;
+using std::chrono::steady_clock;
+using std::chrono::time_point;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorCallbackSchedulerTest : public Test {
+public:
+ void SetUp() override {
+ mScheduler = std::make_unique<vibrator::CallbackScheduler>();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mExpiredCallbacks.clear();
+ }
+
+protected:
+ std::mutex mMutex;
+ std::condition_variable_any mCondition;
+ std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr;
+ std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex);
+
+ std::function<void()> createCallback(int32_t id) {
+ return [=]() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mExpiredCallbacks.push_back(id);
+ }
+ mCondition.notify_all();
+ };
+ }
+
+ std::vector<int32_t> getExpiredCallbacks() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return std::vector<int32_t>(mExpiredCallbacks);
+ }
+
+ bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) {
+ time_point<steady_clock> expiration = steady_clock::now() + timeout;
+ while (steady_clock::now() < expiration) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (callbackCount <= mExpiredCallbacks.size()) {
+ return true;
+ }
+ mCondition.wait_until(mMutex, expiration);
+ }
+ return false;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) {
+ mScheduler->schedule(createCallback(1), 15ms);
+
+ // Not triggered before delay.
+ ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ ASSERT_TRUE(getExpiredCallbacks().empty());
+
+ ASSERT_TRUE(waitForCallbacks(1, 10ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) {
+ mScheduler->schedule(createCallback(1), 10ms);
+ mScheduler->schedule(createCallback(2), 5ms);
+ mScheduler->schedule(createCallback(3), 1ms);
+
+ ASSERT_TRUE(waitForCallbacks(3, 15ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) {
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 5; i++) {
+ threads.push_back(std::thread(
+ [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ ASSERT_TRUE(waitForCallbacks(5, 25ms));
+ ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4));
+}
+
+TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
+ mScheduler->schedule(createCallback(1), 5ms);
+ mScheduler.reset(nullptr);
+
+ // Should time out waiting for callback to run.
+ ASSERT_FALSE(waitForCallbacks(1, 10ms));
+ ASSERT_TRUE(getExpiredCallbacks().empty());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
new file mode 100644
index 0000000..0f2d7bc
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalWrapperAidlTest"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+using android::binder::Status;
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorCallback;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockBinder : public BBinder {
+public:
+ MOCK_METHOD(status_t, linkToDeath,
+ (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override));
+ MOCK_METHOD(status_t, unlinkToDeath,
+ (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient),
+ (override));
+ MOCK_METHOD(status_t, pingBinder, (), (override));
+};
+
+class MockIVibrator : public IVibrator {
+public:
+ MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override));
+ MOCK_METHOD(Status, off, (), (override));
+ MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override));
+ MOCK_METHOD(Status, perform,
+ (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret),
+ (override));
+ MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, setAmplitude, (float amplitude), (override));
+ MOCK_METHOD(Status, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override));
+ MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret),
+ (override));
+ MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override));
+ MOCK_METHOD(Status, compose,
+ (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb),
+ (override));
+ MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override));
+ MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override));
+ MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override));
+ MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
+ MOCK_METHOD(std::string, getInterfaceHash, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperAidlTest : public Test {
+public:
+ void SetUp() override {
+ mMockBinder = new StrictMock<MockBinder>();
+ mMockHal = new StrictMock<MockIVibrator>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibrator>> mMockHal = nullptr;
+ sp<StrictMock<MockBinder>> mMockBinder = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+ACTION(TriggerCallbackInArg1) {
+ if (arg1 != nullptr) {
+ arg1->onComplete();
+ }
+}
+
+ACTION(TriggerCallbackInArg2) {
+ if (arg2 != nullptr) {
+ arg2->onComplete();
+ }
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPing) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), onAsBinder())
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(mMockBinder.get()));
+ EXPECT_CALL(*mMockBinder.get(), pingBinder()).Times(Exactly(1));
+ }
+
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(100), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(100ms, callback).isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(10), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status()));
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), on(Eq(11), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), on(Eq(12), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(11ms, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestOff) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(3))
+ .WillOnce(Return(Status()))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+
+ ASSERT_TRUE(mWrapper->off().isOk());
+ ASSERT_TRUE(mWrapper->off().isUnsupported());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.1, 1e-2))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.2, 1e-2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.5, 1e-2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 10).isOk());
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 5).isUnsupported());
+ ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 2).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+ .Times(Exactly(2))
+ .WillOnce(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(),
+ alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->alwaysOnEnable(3, Effect::POP, EffectStrength::STRONG);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3)))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk());
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(2).isUnsupported());
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported());
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) {
+ std::vector<Effect> supportedEffects;
+ supportedEffects.push_back(Effect::CLICK);
+ supportedEffects.push_back(Effect::TICK);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed());
+
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) {
+ std::vector<Effect> supportedEffects;
+ supportedEffects.push_back(Effect::CLICK);
+ supportedEffects.push_back(Effect::TICK);
+
+ EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getSupportedEffects();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(supportedEffects, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1000ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status())));
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) {
+ std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+ singleEffect.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(
+ Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performComposedEffect(emptyEffects, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(singleEffect, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // Callback not triggered for unsupported
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performComposedEffect(multipleEffects, callback);
+ ASSERT_TRUE(result.isFailed());
+ // Callback not triggered on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
new file mode 100644
index 0000000..7eb4059
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalWrapperHidlV1_0Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_0 : public V1_0::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<void>, ping, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_0Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_0>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) {
+ EXPECT_CALL(*mMockHal.get(), ping())
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<void>(); })
+ .WillRepeatedly([]() {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ ASSERT_TRUE(mWrapper->ping().isOk());
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1))))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+ });
+ EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint32_t) {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
+ ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
+ EXPECT_CALL(*mMockHal.get(), off())
+ .Times(Exactly(4))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+ .WillOnce([]() {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ })
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ ASSERT_TRUE(mWrapper->off().isOk());
+ ASSERT_TRUE(mWrapper->off().isUnsupported());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+ ASSERT_TRUE(mWrapper->off().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(static_cast<uint8_t>(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
+ });
+ EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4))))
+ .Times(Exactly(1))
+ .WillRepeatedly([](uint8_t) {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->setAmplitude(1).isOk());
+ ASSERT_TRUE(mWrapper->setAmplitude(2).isUnsupported());
+ ASSERT_TRUE(mWrapper->setAmplitude(3).isFailed());
+ ASSERT_TRUE(mWrapper->setAmplitude(4).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) {
+ ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) {
+ ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(2))
+ .WillOnce([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ })
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+ return hardware::Return<bool>(false);
+ });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) {
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
+ return hardware::Return<bool>(true);
+ });
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) {
+ ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 10);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using TICK that is only available in v1.1
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
+ std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
+ singleEffect.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
+ multipleEffects.push_back(
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported());
+ ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported());
+
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
new file mode 100644
index 0000000..d887efc
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalWrapperHidlV1_1Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_1 : public V1_1::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_1Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_1>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_1::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 0);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using THUD that is only available in v1.2
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
new file mode 100644
index 0000000..26d9350
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalWrapperHidlV1_2Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_2 : public V1_2::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_2,
+ (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_2Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_2>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 10);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ // Using TEXTURE_TICK that is only available in v1.3
+ auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isUnsupported());
+ // No callback is triggered.
+ ASSERT_EQ(0, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
new file mode 100644
index 0000000..5de6257
--- /dev/null
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorHalWrapperHidlV1_3Test"
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorCallbackScheduler.h>
+#include <vibratorservice/VibratorHalWrapper.h>
+
+#include "test_utils.h"
+
+namespace V1_0 = android::hardware::vibrator::V1_0;
+namespace V1_1 = android::hardware::vibrator::V1_1;
+namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
+
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIVibratorV1_3 : public V1_3::IVibrator {
+public:
+ MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
+ MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
+ MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override));
+ MOCK_METHOD(hardware::Return<void>, perform,
+ (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_1,
+ (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_2,
+ (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+ MOCK_METHOD(hardware::Return<void>, perform_1_3,
+ (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorHalWrapperHidlV1_3Test : public Test {
+public:
+ void SetUp() override {
+ mMockHal = new StrictMock<MockIVibratorV1_3>();
+ mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
+ mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
+ std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
+ });
+ EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
+ .Times(Exactly(2))
+ .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
+ .WillRepeatedly([]() {
+ return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
+ ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+ ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL,
+ result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(true);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::NONE, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+ ASSERT_TRUE(mWrapper->getCapabilities().isFailed());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
+ return hardware::Return<bool>(false);
+ });
+ }
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() {
+ return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(true); });
+ EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
+ .Times(Exactly(1))
+ .WillRepeatedly([]() { return hardware::Return<bool>(false); });
+ }
+
+ // Call to supportsAmplitudeControl failed.
+ auto result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isFailed());
+
+ // Call to supportsExternalControl failed.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isFailed());
+
+ // Returns successful result from third call.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+
+ // Returns cached successful result.
+ result = mWrapper->getCapabilities();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
+ MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
+
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+}
+
+TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::OK, 10);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
+ .Times(Exactly(1))
+ .WillRepeatedly(vibrator::TriggerSchedulerCallback());
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
+ _))
+ .Times(Exactly(1))
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
+ return hardware::Return<void>();
+ });
+ EXPECT_CALL(*mMockHal.get(),
+ perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG),
+ _))
+ .Times(Exactly(2))
+ .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
+ cb(V1_0::Status::BAD_VALUE, 0);
+ return hardware::Return<void>();
+ })
+ .WillRepeatedly(
+ [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+ }
+
+ std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+ auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+ auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(10ms, result.value());
+ ASSERT_EQ(1, *callbackCounter.get());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
+ ASSERT_TRUE(result.isUnsupported());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
+ ASSERT_TRUE(result.isFailed());
+
+ // Callback not triggered for unsupported and on failure
+ ASSERT_EQ(1, *callbackCounter.get());
+}
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
new file mode 100644
index 0000000..8d0b22e
--- /dev/null
+++ b/services/vibratorservice/test/test_utils.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef VIBRATORSERVICE_UNITTEST_UTIL_H_
+#define VIBRATORSERVICE_UNITTEST_UTIL_H_
+
+#include <android/hardware/vibrator/IVibrator.h>
+
+#include <vibratorservice/VibratorHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+using ::android::hardware::vibrator::CompositeEffect;
+using ::android::hardware::vibrator::CompositePrimitive;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockCallbackScheduler : public vibrator::CallbackScheduler {
+public:
+ MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay),
+ (override));
+};
+
+ACTION(TriggerSchedulerCallback) {
+ arg0();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+class TestFactory {
+public:
+ static CompositeEffect createCompositeEffect(CompositePrimitive primitive,
+ std::chrono::milliseconds delay, float scale) {
+ CompositeEffect effect;
+ effect.primitive = primitive;
+ effect.delayMs = delay.count();
+ effect.scale = scale;
+ return effect;
+ }
+
+ static std::function<void()> createCountingCallback(int32_t* counter) {
+ return [counter]() { *counter += 1; };
+ }
+
+private:
+ TestFactory() = delete;
+ ~TestFactory() = delete;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace vibrator
+
+} // namespace android
+
+#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file
diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp
index dcaa663..9cf4905 100644
--- a/services/vr/virtual_touchpad/Android.bp
+++ b/services/vr/virtual_touchpad/Android.bp
@@ -14,6 +14,7 @@
]
header_libraries = [
+ "jni_headers",
"libdvr_headers",
]
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 7bcb2c1..74ef0e7 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -161,7 +161,7 @@
}
const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
- "ro.hardware." HWVULKAN_HARDWARE_MODULE_ID,
+ "ro.hardware.vulkan",
"ro.board.platform",
}};
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index d3ed88d..c7ff640 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -970,7 +970,6 @@
strerror(-err), err);
}
- // TODO(b/143294545): Return something better than "whole window"
pRects[0].offset.x = 0;
pRects[0].offset.y = 0;
pRects[0].extent = VkExtent2D{static_cast<uint32_t>(width),
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index cf370fa..ef0719d 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -22,7 +22,7 @@
import xml.etree.ElementTree as element_tree
# Extensions unsupported on Android.
-_BLACKLISTED_EXTENSIONS = [
+_BLOCKED_EXTENSIONS = [
'VK_EXT_acquire_xlib_display',
'VK_EXT_direct_mode_display',
'VK_EXT_display_control',
@@ -192,7 +192,7 @@
if cmd not in extension_dict:
return True
else:
- if extension_dict[cmd] not in _BLACKLISTED_EXTENSIONS:
+ if extension_dict[cmd] not in _BLOCKED_EXTENSIONS:
return True
return False