diff options
118 files changed, 5611 insertions, 1447 deletions
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp index 1ebdc47ebc..ec3bc616ad 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -49,13 +49,20 @@ cc_library { "vibrator/CommandAlwaysOnDisable.cpp", "vibrator/CommandAlwaysOnEnable.cpp", "vibrator/CommandCompose.cpp", + "vibrator/CommandComposePwle.cpp", + "vibrator/CommandGetBandwidthAmplitudeMap.cpp", "vibrator/CommandGetCapabilities.cpp", "vibrator/CommandGetCompositionDelayMax.cpp", "vibrator/CommandGetCompositionSizeMax.cpp", + "vibrator/CommandGetFrequencyMinimum.cpp", + "vibrator/CommandGetFrequencyResolution.cpp", "vibrator/CommandGetPrimitiveDuration.cpp", + "vibrator/CommandGetPwleCompositionSizeMax.cpp", + "vibrator/CommandGetPwlePrimitiveDurationMax.cpp", "vibrator/CommandGetQFactor.cpp", "vibrator/CommandGetResonantFrequency.cpp", "vibrator/CommandGetSupportedAlwaysOnEffects.cpp", + "vibrator/CommandGetSupportedBraking.cpp", "vibrator/CommandGetSupportedEffects.cpp", "vibrator/CommandGetSupportedPrimitives.cpp", "vibrator/CommandOff.cpp", diff --git a/cmds/idlcli/vibrator/CommandComposePwle.cpp b/cmds/idlcli/vibrator/CommandComposePwle.cpp new file mode 100644 index 0000000000..b8308ce16f --- /dev/null +++ b/cmds/idlcli/vibrator/CommandComposePwle.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> + +#include <charconv> + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +using aidl::ActivePwle; +using aidl::Braking; +using aidl::BrakingPwle; +using aidl::PrimitivePwle; + +class CommandComposePwle : public Command { + std::string getDescription() const override { return "Compose PWLE vibration."; } + + std::string getUsageSummary() const override { + return "[options] a <active pwle params> b <braking pwle params> ..."; + } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"-b", {"Block for duration of vibration."}}, + {"a <startAmplitude> <startFrequency> <endAmplitude> <endFrequency> <duration>", + {"Enter the active PWLE segment parameters"}}, + {"b <brakingMethod> <duration>", {"Enter the braking PWLE segment parameters"}}, + {"...", {"May repeat multiple times."}}, + }; + return details; + } + + int getIntFromString(std::string input, int *output) { + int rc = 0; + int value; + const auto res = std::from_chars(input.data(), input.data() + input.size(), value); + if (res.ec == std::errc::invalid_argument) { + std::cerr << "Invalid int argument: " << input << std::endl; + rc = (int)std::errc::invalid_argument; + } else if (res.ec == std::errc::result_out_of_range) { + std::cerr << "Result out of range: " << input << std::endl; + rc = (int)std::errc::result_out_of_range; + } + *output = value; + return rc; + } + + float getFloatFromString(std::string_view input, float *output) { + int rc = 0; + errno = 0; + // from_chars doesn't support conversion to float so we need to first + // convert the string_view to string and use the C-string for strtof + float value = strtof(std::string(input).c_str(), NULL); + + if (input == "0.0" || input == "0") { + return rc; + } + + if (value <= 0.0) { + std::cerr << "Invalid float argument: " << input << std::endl; + rc = EINVAL; + } else if (errno == ERANGE) { + std::cerr << "Result out of range: " << input << std::endl; + rc = errno; + } else { + *output = value; + } + return rc; + } + + 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 (args.empty()) { + std::cerr << "Missing arguments! Please see usage" << std::endl; + return USAGE; + } + while (!args.empty()) { + PrimitivePwle pwle; + auto nextArg = args.pop(); + + if (*nextArg == "a") { + auto startAmplitude = args.pop(); + float startAmp; + if (getFloatFromString(*startAmplitude, &startAmp)) + return USAGE; + + auto startFrequency = args.pop(); + float startFreq; + if (getFloatFromString(*startFrequency, &startFreq)) + return USAGE; + + auto endAmplitude = args.pop(); + float endAmp; + if (getFloatFromString(*endAmplitude, &endAmp)) + return USAGE; + + auto endFrequency = args.pop(); + float endFreq; + if (getFloatFromString(*endFrequency, &endFreq)) + return USAGE; + + auto duration = args.pop(); + int dur; + if (getIntFromString(*duration, &dur)) + return USAGE; + + ActivePwle active = {startAmp, startFreq, endAmp, endFreq, dur}; + pwle = active; + } else if (*nextArg == "b") { + auto brakingMethod = args.pop(); + Braking brakingMeth; + if (getIntFromString(*brakingMethod, (int *)&brakingMeth)) + return USAGE; + + auto duration = args.pop(); + int dur; + if (getIntFromString(*duration, &dur)) + return USAGE; + + BrakingPwle braking = {brakingMeth, dur}; + pwle = braking; + } else { + std::cerr << "Invalid arguments! Please see usage" << std::endl; + return USAGE; + } + mCompositePwle.emplace_back(std::move(pwle)); + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + auto hal = getHal<aidl::IVibrator>(); + + if (!hal) { + return UNAVAILABLE; + } + + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + std::shared_ptr<VibratorCallback> callback; + + if (mBlocking) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); + } + + auto status = hal->call(&aidl::IVibrator::composePwle, mCompositePwle, callback); + + if (status.isOk() && callback) { + callback->waitForComplete(); + } + + std::cout << "Status: " << status.getDescription() << std::endl; + + return status.isOk() ? OK : ERROR; + } + + bool mBlocking; + std::vector<PrimitivePwle> mCompositePwle; +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandComposePwle>("composePwle"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp new file mode 100644 index 0000000000..aa01a11237 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetBandwidthAmplitudeMap.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 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 CommandGetBandwidthAmplitudeMap : public Command { + std::string getDescription() const override { + return "Retrieves vibrator bandwidth amplitude map."; + } + + 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<float> bandwidthAmplitude; + float frequencyMinimumHz; + float frequencyResolutionHz; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = + hal->call(&aidl::IVibrator::getBandwidthAmplitudeMap, &bandwidthAmplitude); + statusStr = status.getDescription(); + ret = (status.isOk() ? OK : ERROR); + + status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz); + ret = (status.isOk() ? OK : ERROR); + + status = + hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz); + ret = (status.isOk() ? OK : ERROR); + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Bandwidth Amplitude Map: " << std::endl; + float frequency = frequencyMinimumHz; + for (auto &e : bandwidthAmplitude) { + std::cout << frequency << ":" << e << std::endl; + frequency += frequencyResolutionHz; + } + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetBandwidthAmplitudeMap>( + "getBandwidthAmplitudeMap"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp new file mode 100644 index 0000000000..504c6482ad --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetFrequencyMinimum.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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 CommandGetFrequencyMinimum : public Command { + std::string getDescription() const override { + return "Retrieves vibrator minimum frequency in Hz."; + } + + 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; + float frequencyMinimumHz; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getFrequencyMinimum, &frequencyMinimumHz); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Minimum Frequency: " << frequencyMinimumHz << " Hz" << std::endl; + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetFrequencyMinimum>("getFrequencyMinimum"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp new file mode 100644 index 0000000000..de358385a0 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetFrequencyResolution.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 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 CommandGetFrequencyResolution : public Command { + std::string getDescription() const override { + return "Retrieves vibrator frequency resolution in Hz."; + } + + 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; + float frequencyResolutionHz; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = + hal->call(&aidl::IVibrator::getFrequencyResolution, &frequencyResolutionHz); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Frequency Resolution: " << frequencyResolutionHz << " Hz" << std::endl; + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetFrequencyResolution>( + "getFrequencyResolution"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp new file mode 100644 index 0000000000..b2c35519eb --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetPwleCompositionSizeMax.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandGetPwleCompositionSizeMax : public Command { + std::string getDescription() const override { + return "Retrieves vibrator PWLE composition size max."; + } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + int32_t maxSize; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getPwleCompositionSizeMax, &maxSize); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Max Size: " << maxSize << std::endl; + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetPwleCompositionSizeMax>( + "getPwleCompositionSizeMax"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp new file mode 100644 index 0000000000..90819731c4 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetPwlePrimitiveDurationMax.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandGetPwlePrimitiveDurationMax : public Command { + std::string getDescription() const override { + return "Retrieves vibrator PWLE primitive duration size max in milliseconds."; + } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + int32_t maxDurationMs; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getPwlePrimitiveDurationMax, &maxDurationMs); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Primitive duration max: " << maxDurationMs << " ms" << std::endl; + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetPwlePrimitiveDurationMax>( + "getPwlePrimitiveDurationMax"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp b/cmds/idlcli/vibrator/CommandGetSupportedBraking.cpp new file mode 100644 index 0000000000..b326e07c22 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetSupportedBraking.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::Braking; + +class CommandGetSupportedBraking : public Command { + std::string getDescription() const override { return "List of supported braking mechanisms."; } + + 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<Braking> braking; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getSupportedBraking, &braking); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Braking Mechanisms:" << std::endl; + for (auto &e : braking) { + std::cout << " " << toString(e) << std::endl; + } + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetSupportedBraking>("getSupportedBraking"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 5c2211ff9a..a5462367f8 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -189,8 +189,8 @@ cc_binary { "liblog", "libutils", ], - static_libs: [ - "libapexd", + required: [ + "apexd" ], } diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index 379cf92043..c04b558e2d 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -20,6 +20,7 @@ #include <sys/stat.h> #include <sys/wait.h> +#include <array> #include <fstream> #include <sstream> @@ -31,10 +32,6 @@ #include <libdm/dm.h> #include <selinux/android.h> -#include <apex_file_repository.h> -#include <apex_constants.h> -#include <apexd.h> - #include "installd_constants.h" #include "otapreopt_utils.h" @@ -64,47 +61,14 @@ static void CloseDescriptor(const char* descriptor_string) { } } -static std::vector<apex::ApexFile> ActivateApexPackages() { - // The logic here is (partially) copied and adapted from - // system/apex/apexd/apexd.cpp. - // - // Only scan the APEX directory under /system, /system_ext and /vendor (within the chroot dir). - std::vector<std::string> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir, - apex::kApexPackageVendorDir}; - // Initialize ApexFileRepository used internally in ScanPackagesDirAndActivate. - // This is a quick fix to fix apex activation in otapreopt_chroot. - apex::ApexFileRepository::GetInstance().AddPreInstalledApex(apex_dirs); - for (const auto& dir : apex_dirs) { - // Cast call to void to suppress warn_unused_result. - static_cast<void>(apex::ScanPackagesDirAndActivate(dir.c_str())); - } - return apex::GetActivePackages(); -} - -static void CreateApexInfoList(const std::vector<apex::ApexFile>& apex_files) { - // Setup the apex-info-list.xml file - const std::string apex_info_file = std::string(apex::kApexRoot) + "/" + apex::kApexInfoList; - std::fstream xml(apex_info_file.c_str(), std::ios::out | std::ios::trunc); - if (!xml.is_open()) { - PLOG(ERROR) << "Failed to open " << apex_info_file; - exit(216); - } - - // we do not care about inactive apexs - std::vector<apex::ApexFile> inactive; - apex::CollectApexInfoList(xml, apex_files, inactive); - xml.flush(); - xml.close(); -} +static void ActivateApexPackages() { + std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--otachroot-bootstrap"}; + std::string apexd_error_msg; -static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) { - for (const apex::ApexFile& apex_file : active_packages) { - const std::string& package_path = apex_file.GetPath(); - base::Result<void> status = apex::DeactivatePackage(package_path); - if (!status.ok()) { - LOG(ERROR) << "Failed to deactivate " << package_path << ": " - << status.error(); - } + bool exec_result = Exec(apexd_cmd, &apexd_error_msg); + if (!exec_result) { + PLOG(ERROR) << "Running otapreopt failed: " << apexd_error_msg; + exit(220); } } @@ -269,8 +233,7 @@ static int otapreopt_chroot(const int argc, char **arg) { // Try to mount APEX packages in "/apex" in the chroot dir. We need at least // the ART APEX, as it is required by otapreopt to run dex2oat. - std::vector<apex::ApexFile> active_packages = ActivateApexPackages(); - CreateApexInfoList(active_packages); + ActivateApexPackages(); // Check that an ART APEX has been activated; clean up and exit // early otherwise. @@ -278,16 +241,27 @@ static int otapreopt_chroot(const int argc, char **arg) { "com.android.art", "com.android.runtime", }; - for (std::string_view apex : kRequiredApexs) { - if (std::none_of(active_packages.begin(), active_packages.end(), - [&](const apex::ApexFile& package) { - return package.GetManifest().name() == apex; - })) { - LOG(FATAL_WITHOUT_ABORT) << "No activated " << apex << " APEX package."; - DeactivateApexPackages(active_packages); - exit(217); + std::array<bool, arraysize(kRequiredApexs)> found_apexs{ false, false }; + DIR* apex_dir = opendir("/apex"); + if (apex_dir == nullptr) { + PLOG(ERROR) << "unable to open /apex"; + exit(220); + } + for (dirent* entry = readdir(apex_dir); entry != nullptr; entry = readdir(apex_dir)) { + for (int i = 0; i < found_apexs.size(); i++) { + if (kRequiredApexs[i] == std::string_view(entry->d_name)) { + found_apexs[i] = true; + break; + } } } + closedir(apex_dir); + auto it = std::find(found_apexs.cbegin(), found_apexs.cend(), false); + if (it != found_apexs.cend()) { + LOG(ERROR) << "No activated " << kRequiredApexs[std::distance(found_apexs.cbegin(), it)] + << " package!"; + exit(221); + } // Setup /linkerconfig. Doing it after the chroot means it doesn't need its own category if (selinux_android_restorecon("/linkerconfig", 0) < 0) { @@ -323,9 +297,6 @@ static int otapreopt_chroot(const int argc, char **arg) { LOG(ERROR) << "Running otapreopt failed: " << error_msg; } - // Tear down the work down by the apexd logic. (i.e. deactivate packages). - DeactivateApexPackages(active_packages); - if (!exec_result) { exit(213); } diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp index 17ea90359c..a27fd103a4 100644 --- a/cmds/installd/run_dex2oat.cpp +++ b/cmds/installd/run_dex2oat.cpp @@ -86,7 +86,7 @@ void RunDex2Oat::Initialize(const UniqueFile& output_oat, bool generate_compact_dex, bool use_jitzygote_image, const char* compilation_reason) { - PrepareBootImageAndBootClasspathFlags(use_jitzygote_image); + PrepareBootImageFlags(use_jitzygote_image); PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex, dex_metadata, profile, swap_fd, class_loader_context, @@ -112,7 +112,7 @@ void RunDex2Oat::Initialize(const UniqueFile& output_oat, RunDex2Oat::~RunDex2Oat() {} -void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) { +void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote_image) { std::string boot_image; if (use_jitzygote_image) { boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage); @@ -120,23 +120,6 @@ void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"); } AddArg(boot_image); - - // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query - // BOOTCLASSPATH. - char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); - if (dex2oat_bootclasspath != nullptr) { - AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath)); - } - - std::string updatable_bcp_packages = - MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file", - "--updatable-bcp-packages-file=%s"); - if (updatable_bcp_packages.empty()) { - // Make dex2oat fail by providing non-existent file name. - updatable_bcp_packages = - "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt"; - } - AddArg(updatable_bcp_packages); } void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat, diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h index 325a3a2599..475e12413d 100644 --- a/cmds/installd/run_dex2oat.h +++ b/cmds/installd/run_dex2oat.h @@ -56,7 +56,7 @@ class RunDex2Oat { void Exec(int exit_code); protected: - void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image); + void PrepareBootImageFlags(bool use_jitzygote_image); void PrepareInputFileFlags(const UniqueFile& output_oat, const UniqueFile& output_vdex, const UniqueFile& output_image, diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp index 3813cf7c09..0a638cd51b 100644 --- a/cmds/installd/run_dex2oat_test.cpp +++ b/cmds/installd/run_dex2oat_test.cpp @@ -175,8 +175,6 @@ class RunDex2OatTest : public testing::Test { default_expected_flags_["--swap-fd"] = FLAG_UNUSED; default_expected_flags_["--class-loader-context"] = FLAG_UNUSED; default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED; - default_expected_flags_["--updatable-bcp-packages-file"] = - "=/nonx/updatable-bcp-packages.txt"; // Arch default_expected_flags_["--instruction-set"] = "=arm64"; @@ -320,28 +318,6 @@ TEST_F(RunDex2OatTest, WithOnlyClassLoaderContextFds) { VerifyExpectedFlags(); } -TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) { - ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH")); - ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false)) - << "Failed to setenv: " << strerror(errno); - - CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); - - SetExpectedFlagUsed("-Xbootclasspath", ":foobar"); - VerifyExpectedFlags(); - - ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH")) - << "Failed to setenv: " << strerror(errno); -} - -TEST_F(RunDex2OatTest, UpdatableBootClassPath) { - setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file"); - CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); - - SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file"); - VerifyExpectedFlags(); -} - TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) { auto args = RunDex2OatArgs::MakeDefaultTestArgs(); args->generate_compact_dex = false; diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index fbf1e0c4b6..e27202597c 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -351,7 +351,7 @@ protected: uid = kTestAppUid; } if (class_loader_context == nullptr) { - class_loader_context = "&"; + class_loader_context = "PCL[]"; } int32_t dexopt_needed = 0; // does not matter; std::optional<std::string> out_path; // does not matter @@ -478,7 +478,7 @@ protected: bool should_binder_call_succeed, /*out */ binder::Status* binder_result) { std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt; - std::string class_loader_context = "&"; + std::string class_loader_context = "PCL[]"; int32_t target_sdk_version = 0; // default std::string profile_name = "primary.prof"; std::optional<std::string> dm_path_opt = dm_path ? std::make_optional<std::string>(dm_path) : std::nullopt; diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h index fa7759393c..509ee0e49b 100644 --- a/include/android/multinetwork.h +++ b/include/android/multinetwork.h @@ -102,6 +102,28 @@ int android_setprocnetwork(net_handle_t network) __INTRODUCED_IN(23); */ int android_getprocnetwork(net_handle_t *network) __INTRODUCED_IN(31); +/** + * Binds domain name resolutions performed by this process to |network|. + * android_setprocnetwork takes precedence over this setting. + * + * To clear a previous process binding, invoke with NETWORK_UNSPECIFIED. + * On success 0 is returned. On error -1 is returned, and errno is set. + * + * Available since API level 31. + */ +int android_setprocdns(net_handle_t network) __INTRODUCED_IN(31); + +/** + * Gets the |network| to which domain name resolutions are bound on the + * current process. + * + * Returns 0 on success, or -1 setting errno to EINVAL if a null pointer is + * passed in. + * + * Available since API level 31. + */ +int android_getprocdns(net_handle_t *network) __INTRODUCED_IN(31); + /** * Perform hostname resolution via the DNS servers associated with |network|. diff --git a/include/android/surface_control.h b/include/android/surface_control.h index c17f8224c2..91726cd737 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -286,6 +286,9 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible * for closing it. * + * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE + * as the surface control might be composited using the GPU. + * * Available since API level 29. */ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, @@ -325,6 +328,38 @@ void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction, const ARect& destination, int32_t transform) __INTRODUCED_IN(29); +/** + * \param source The sub-rect within the buffer's content to be rendered inside the surface's area + * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width + * and height must be > 0. + * + * Available since API level 31. + */ +void ASurfaceTransaction_setSourceRect(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, const ARect& source) + __INTRODUCED_IN(31); + +/** + * \param destination Specifies the rect in the parent's space where this surface will be drawn. The + * post source rect bounds are scaled to fit the destination rect. The surface's destination rect is + * clipped by the bounds of its parent. The destination rect's width and height must be > 0. + * + * Available since API level 31. + */ +void ASurfaceTransaction_setPosition(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, const ARect& destination) + __INTRODUCED_IN(31); + +/** + * \param transform The transform applied after the source rect is applied to the buffer. This + * parameter should be set to 0 for no transform. To specify a transfrom use the + * NATIVE_WINDOW_TRANSFORM_* enum. + * + * Available since API level 31. + */ +void ASurfaceTransaction_setTransform(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, int32_t transform) + __INTRODUCED_IN(31); /** * Parameter for ASurfaceTransaction_setBufferTransparency(). diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index a17e482d79..144dbd3585 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -73,6 +73,7 @@ libbinder_device_interface_sources = [ "PermissionController.cpp", "ProcessInfoService.cpp", "IpPrefix.cpp", + ":activity_manager_procstate_aidl", ] cc_library { @@ -121,13 +122,17 @@ cc_library { "ParcelFileDescriptor.cpp", "PersistableBundle.cpp", "ProcessState.cpp", + "RpcAddress.cpp", + "RpcConnection.cpp", + "RpcServer.cpp", + "RpcState.cpp", "Static.cpp", "Stability.cpp", "Status.cpp", "TextOutput.cpp", "Utils.cpp", + ":packagemanager_aidl", ":libbinder_aidl", - ":activity_manager_procstate_aidl", ], target: { @@ -228,9 +233,6 @@ cc_library { filegroup { name: "libbinder_aidl", srcs: [ - "aidl/android/content/pm/IPackageChangeObserver.aidl", - "aidl/android/content/pm/IPackageManagerNative.aidl", - "aidl/android/content/pm/PackageChangeEvent.aidl", "aidl/android/os/IClientCallback.aidl", "aidl/android/os/IServiceCallback.aidl", "aidl/android/os/IServiceManager.aidl", @@ -239,6 +241,16 @@ filegroup { path: "aidl", } +filegroup { + name: "packagemanager_aidl", + srcs: [ + "aidl/android/content/pm/IPackageChangeObserver.aidl", + "aidl/android/content/pm/IPackageManagerNative.aidl", + "aidl/android/content/pm/PackageChangeEvent.aidl", + ], + path: "aidl", +} + aidl_interface { name: "libbinder_aidl_test_stub", unstable: true, diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index ddda024a40..825a821cc4 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -21,6 +21,7 @@ #include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> +#include <binder/RpcConnection.h> #include <binder/Stability.h> #include <cutils/compiler.h> #include <utils/Log.h> @@ -133,25 +134,56 @@ BpBinder* BpBinder::create(int32_t handle) { } sTrackingMap[trackedUid]++; } - return new BpBinder(handle, trackedUid); + return new BpBinder(BinderHandle{handle}, trackedUid); } -BpBinder::BpBinder(int32_t handle, int32_t trackedUid) - : mStability(0) - , mHandle(handle) - , mAlive(1) - , mObitsSent(0) - , mObituaries(nullptr) - , mTrackedUid(trackedUid) -{ - ALOGV("Creating BpBinder %p handle %d\n", this, mHandle); +BpBinder* BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) { + LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection"); + + // These are not currently tracked, since there is no UID or other + // identifier to track them with. However, if similar functionality is + // needed, connection objects keep track of all BpBinder objects on a + // per-connection basis. + + return new BpBinder(SocketHandle{connection, address}); +} +BpBinder::BpBinder(Handle&& handle) + : mStability(0), + mHandle(handle), + mAlive(true), + mObitsSent(false), + mObituaries(nullptr), + mTrackedUid(-1) { extendObjectLifetime(OBJECT_LIFETIME_WEAK); - IPCThreadState::self()->incWeakHandle(handle, this); } -int32_t BpBinder::handle() const { - return mHandle; +BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) { + mTrackedUid = trackedUid; + + ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle()); + + IPCThreadState::self()->incWeakHandle(this->binderHandle(), this); +} + +BpBinder::BpBinder(SocketHandle&& handle) : BpBinder(Handle(handle)) { + LOG_ALWAYS_FATAL_IF(rpcConnection() == nullptr, "BpBinder created w/o connection object"); +} + +bool BpBinder::isRpcBinder() const { + return std::holds_alternative<SocketHandle>(mHandle); +} + +const RpcAddress& BpBinder::rpcAddress() const { + return std::get<SocketHandle>(mHandle).address; +} + +const sp<RpcConnection>& BpBinder::rpcConnection() const { + return std::get<SocketHandle>(mHandle).connection; +} + +int32_t BpBinder::binderHandle() const { + return std::get<BinderHandle>(mHandle).handle; } bool BpBinder::isDescriptorCached() const { @@ -190,9 +222,10 @@ bool BpBinder::isBinderAlive() const status_t BpBinder::pingBinder() { - Parcel send; + Parcel data; + data.markForBinder(this); Parcel reply; - return transact(PING_TRANSACTION, send, &reply); + return transact(PING_TRANSACTION, data, &reply); } status_t BpBinder::dump(int fd, const Vector<String16>& args) @@ -236,8 +269,13 @@ status_t BpBinder::transact( } } - status_t status = IPCThreadState::self()->transact( - mHandle, code, data, reply, flags); + status_t status; + if (CC_UNLIKELY(isRpcBinder())) { + status = rpcConnection()->transact(rpcAddress(), code, data, reply, flags); + } else { + status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); + } + if (status == DEAD_OBJECT) mAlive = 0; return status; @@ -250,6 +288,8 @@ status_t BpBinder::transact( status_t BpBinder::linkToDeath( const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags) { + if (isRpcBinder()) return UNKNOWN_TRANSACTION; + Obituary ob; ob.recipient = recipient; ob.cookie = cookie; @@ -267,10 +307,10 @@ status_t BpBinder::linkToDeath( if (!mObituaries) { return NO_MEMORY; } - ALOGV("Requesting death notification: %p handle %d\n", this, mHandle); + ALOGV("Requesting death notification: %p handle %d\n", this, binderHandle()); getWeakRefs()->incWeak(this); IPCThreadState* self = IPCThreadState::self(); - self->requestDeathNotification(mHandle, this); + self->requestDeathNotification(binderHandle(), this); self->flushCommands(); } ssize_t res = mObituaries->add(ob); @@ -286,6 +326,8 @@ status_t BpBinder::unlinkToDeath( const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, wp<DeathRecipient>* outRecipient) { + if (isRpcBinder()) return UNKNOWN_TRANSACTION; + AutoMutex _l(mLock); if (mObitsSent) { @@ -303,9 +345,9 @@ status_t BpBinder::unlinkToDeath( } mObituaries->removeAt(i); if (mObituaries->size() == 0) { - ALOGV("Clearing death notification: %p handle %d\n", this, mHandle); + ALOGV("Clearing death notification: %p handle %d\n", this, binderHandle()); IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(mHandle, this); + self->clearDeathNotification(binderHandle(), this); self->flushCommands(); delete mObituaries; mObituaries = nullptr; @@ -319,8 +361,10 @@ status_t BpBinder::unlinkToDeath( void BpBinder::sendObituary() { - ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", - this, mHandle, mObitsSent ? "true" : "false"); + LOG_ALWAYS_FATAL_IF(isRpcBinder(), "Cannot send obituary for remote binder."); + + ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, binderHandle(), + mObitsSent ? "true" : "false"); mAlive = 0; if (mObitsSent) return; @@ -328,9 +372,9 @@ void BpBinder::sendObituary() mLock.lock(); Vector<Obituary>* obits = mObituaries; if(obits != nullptr) { - ALOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); + ALOGV("Clearing sent death notification: %p handle %d\n", this, binderHandle()); IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(mHandle, this); + self->clearDeathNotification(binderHandle(), this); self->flushCommands(); mObituaries = nullptr; } @@ -388,7 +432,9 @@ BpBinder* BpBinder::remoteBinder() BpBinder::~BpBinder() { - ALOGV("Destroying BpBinder %p handle %d\n", this, mHandle); + ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle()); + + if (CC_UNLIKELY(isRpcBinder())) return; IPCThreadState* ipc = IPCThreadState::self(); @@ -396,7 +442,8 @@ BpBinder::~BpBinder() AutoMutex _l(sTrackingLock); uint32_t trackedValue = sTrackingMap[mTrackedUid]; if (CC_UNLIKELY((trackedValue & COUNTING_VALUE_MASK) == 0)) { - ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this, mHandle); + ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this, + binderHandle()); } else { if (CC_UNLIKELY( (trackedValue & LIMIT_REACHED_MASK) && @@ -413,26 +460,31 @@ BpBinder::~BpBinder() } if (ipc) { - ipc->expungeHandle(mHandle, this); - ipc->decWeakHandle(mHandle); + ipc->expungeHandle(binderHandle(), this); + ipc->decWeakHandle(binderHandle()); } } void BpBinder::onFirstRef() { - ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); + ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle()); + if (CC_UNLIKELY(isRpcBinder())) return; IPCThreadState* ipc = IPCThreadState::self(); - if (ipc) ipc->incStrongHandle(mHandle, this); + if (ipc) ipc->incStrongHandle(binderHandle(), this); } void BpBinder::onLastStrongRef(const void* /*id*/) { - ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle); + ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle()); + if (CC_UNLIKELY(isRpcBinder())) { + (void)rpcConnection()->sendDecStrong(rpcAddress()); + return; + } IF_ALOGV() { printRefs(); } IPCThreadState* ipc = IPCThreadState::self(); - if (ipc) ipc->decStrongHandle(mHandle); + if (ipc) ipc->decStrongHandle(binderHandle()); mLock.lock(); Vector<Obituary>* obits = mObituaries; @@ -442,7 +494,7 @@ void BpBinder::onLastStrongRef(const void* /*id*/) mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>"); } - if (ipc) ipc->clearDeathNotification(mHandle, this); + if (ipc) ipc->clearDeathNotification(binderHandle(), this); mObituaries = nullptr; } mLock.unlock(); @@ -457,9 +509,12 @@ void BpBinder::onLastStrongRef(const void* /*id*/) bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) { - ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle); + // RPC binder doesn't currently support inc from weak binders + if (CC_UNLIKELY(isRpcBinder())) return false; + + ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle()); IPCThreadState* ipc = IPCThreadState::self(); - return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false; + return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false; } uint32_t BpBinder::getBinderProxyCount(uint32_t uid) diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index e4ac4b49a4..86769551d7 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -26,6 +26,22 @@ namespace android { +std::string hexString(const void* bytes, size_t len) { + if (bytes == nullptr) return "<null>"; + + const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes); + const char chars[] = "0123456789abcdef"; + std::string result; + result.resize(len * 2); + + for (size_t i = 0; i < len; i++) { + result[2 * i] = chars[bytes8[i] >> 4]; + result[2 * i + 1] = chars[bytes8[i] & 0xf]; + } + + return result; +} + // --------------------------------------------------------------------- static const char indentStr[] = diff --git a/libs/binder/Debug.h b/libs/binder/Debug.h index ac71e003c4..7ca087e7d2 100644 --- a/libs/binder/Debug.h +++ b/libs/binder/Debug.h @@ -17,13 +17,13 @@ #pragma once #include <stdint.h> -#include <sys/cdefs.h> #include <sys/types.h> +#include <string> namespace android { // --------------------------------------------------------------------------- -__BEGIN_DECLS +std::string hexString(const void* data, size_t size); const char* stringForIndent(int32_t indentLevel); @@ -37,10 +37,7 @@ void printHexData(int32_t indent, const void *buf, size_t length, size_t alignment=0, bool cArrayStyle=false, debugPrintFunc func = nullptr, void* cookie = nullptr); - -ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf); - -__END_DECLS +extern "C" ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf); // --------------------------------------------------------------------------- } // namespace android diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 79a11d22bd..406bd54e6f 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -689,6 +689,8 @@ status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + LOG_ALWAYS_FATAL_IF(data.isForRpc(), "Parcel constructed for RPC, but being used with binder."); + status_t err; flags |= TF_ACCEPT_FDS; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 3773760f38..7fedba2666 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -41,13 +41,15 @@ #include <binder/TextOutput.h> #include <cutils/ashmem.h> +#include <cutils/compiler.h> #include <utils/Flattenable.h> #include <utils/Log.h> -#include <utils/misc.h> -#include <utils/String8.h> #include <utils/String16.h> +#include <utils/String8.h> +#include <utils/misc.h> #include <private/binder/binder_module.h> +#include "RpcState.h" #include "Static.h" #include "Utils.h" @@ -191,6 +193,22 @@ static constexpr inline int schedPolicyMask(int policy, int priority) { status_t Parcel::flattenBinder(const sp<IBinder>& binder) { + if (isForRpc()) { + if (binder) { + status_t status = writeInt32(1); // non-null + if (status != OK) return status; + RpcAddress address = RpcAddress::zero(); + status = mConnection->state()->onBinderLeaving(mConnection, binder, &address); + if (status != OK) return status; + status = address.writeToParcel(this); + if (status != OK) return status; + } else { + status_t status = writeInt32(0); // null + if (status != OK) return status; + } + return finishFlattenBinder(binder); + } + flat_binder_object obj; obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; @@ -205,8 +223,13 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) BpBinder *proxy = binder->remoteBinder(); if (proxy == nullptr) { ALOGE("null proxy"); + } else { + if (proxy->isRpcBinder()) { + ALOGE("Sending a socket binder over RPC is prohibited"); + return INVALID_OPERATION; + } } - const int32_t handle = proxy ? proxy->getPrivateAccessorForHandle().handle() : 0; + const int32_t handle = proxy ? proxy->getPrivateAccessorForId().binderHandle() : 0; obj.hdr.type = BINDER_TYPE_HANDLE; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; @@ -245,6 +268,26 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) status_t Parcel::unflattenBinder(sp<IBinder>* out) const { + if (isForRpc()) { + LOG_ALWAYS_FATAL_IF(mConnection == nullptr, + "RpcConnection required to read from remote parcel"); + + int32_t isNull; + status_t status = readInt32(&isNull); + if (status != OK) return status; + + sp<IBinder> binder; + + if (isNull & 1) { + auto addr = RpcAddress::zero(); + status_t status = addr.readFromParcel(*this); + if (status != OK) return status; + binder = mConnection->state()->onBinderEntering(mConnection, addr); + } + + return finishUnflattenBinder(binder, out); + } + const flat_binder_object* flat = readObject(false); if (flat) { @@ -511,6 +554,21 @@ void Parcel::markSensitive() const mDeallocZero = true; } +void Parcel::markForBinder(const sp<IBinder>& binder) { + if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) { + markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection()); + } +} + +void Parcel::markForRpc(const sp<RpcConnection>& connection) { + LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection"); + mConnection = connection; +} + +bool Parcel::isForRpc() const { + return mConnection != nullptr; +} + void Parcel::updateWorkSourceRequestHeaderPosition() const { // Only update the request headers once. We only want to point // to the first headers read/written. @@ -533,12 +591,14 @@ status_t Parcel::writeInterfaceToken(const String16& interface) } status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { - const IPCThreadState* threadState = IPCThreadState::self(); - writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); - updateWorkSourceRequestHeaderPosition(); - writeInt32(threadState->shouldPropagateWorkSource() ? - threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); - writeInt32(kHeader); + if (CC_LIKELY(!isForRpc())) { + const IPCThreadState* threadState = IPCThreadState::self(); + writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); + updateWorkSourceRequestHeaderPosition(); + writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid() + : IPCThreadState::kUnsetWorkSource); + writeInt32(kHeader); + } // currently the interface identification token is just its name as a string return writeString16(str, len); @@ -585,31 +645,34 @@ bool Parcel::enforceInterface(const char16_t* interface, size_t len, IPCThreadState* threadState) const { - // StrictModePolicy. - int32_t strictPolicy = readInt32(); - if (threadState == nullptr) { - threadState = IPCThreadState::self(); - } - if ((threadState->getLastTransactionBinderFlags() & - IBinder::FLAG_ONEWAY) != 0) { - // For one-way calls, the callee is running entirely - // disconnected from the caller, so disable StrictMode entirely. - // Not only does disk/network usage not impact the caller, but - // there's no way to commuicate back any violations anyway. - threadState->setStrictModePolicy(0); - } else { - threadState->setStrictModePolicy(strictPolicy); - } - // WorkSource. - updateWorkSourceRequestHeaderPosition(); - int32_t workSource = readInt32(); - threadState->setCallingWorkSourceUidWithoutPropagation(workSource); - // vendor header - int32_t header = readInt32(); - if (header != kHeader) { - ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header); - return false; + if (CC_LIKELY(!isForRpc())) { + // StrictModePolicy. + int32_t strictPolicy = readInt32(); + if (threadState == nullptr) { + threadState = IPCThreadState::self(); + } + if ((threadState->getLastTransactionBinderFlags() & IBinder::FLAG_ONEWAY) != 0) { + // For one-way calls, the callee is running entirely + // disconnected from the caller, so disable StrictMode entirely. + // Not only does disk/network usage not impact the caller, but + // there's no way to communicate back violations anyway. + threadState->setStrictModePolicy(0); + } else { + threadState->setStrictModePolicy(strictPolicy); + } + // WorkSource. + updateWorkSourceRequestHeaderPosition(); + int32_t workSource = readInt32(); + threadState->setCallingWorkSourceUidWithoutPropagation(workSource); + // vendor header + int32_t header = readInt32(); + if (header != kHeader) { + ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, + header); + return false; + } } + // Interface descriptor. size_t parcel_interface_len; const char16_t* parcel_interface = readString16Inplace(&parcel_interface_len); @@ -1070,6 +1133,11 @@ status_t Parcel::writeNativeHandle(const native_handle* handle) status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) { + if (isForRpc()) { + ALOGE("Cannot write file descriptor to remote binder."); + return BAD_TYPE; + } + flat_binder_object obj; obj.hdr.type = BINDER_TYPE_FD; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; @@ -2413,6 +2481,7 @@ void Parcel::initState() mDataPos = 0; ALOGV("initState Setting data size of %p to %zu", this, mDataSize); ALOGV("initState Setting data pos of %p to %zu", this, mDataPos); + mConnection = nullptr; mObjects = nullptr; mObjectsSize = 0; mObjectsCapacity = 0; diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index edadcd5c58..abb792eda0 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -205,10 +205,12 @@ ssize_t ProcessState::getKernelReferences(size_t buf_count, uintptr_t* buf) // // Returns -1 in case of failure, otherwise the strong reference count. ssize_t ProcessState::getStrongRefCountForNode(const sp<BpBinder>& binder) { + if (binder->isRpcBinder()) return -1; + binder_node_info_for_ref info; memset(&info, 0, sizeof(binder_node_info_for_ref)); - info.handle = binder->getPrivateAccessorForHandle().handle(); + info.handle = binder->getPrivateAccessorForId().binderHandle(); status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info); diff --git a/libs/binder/RpcAddress.cpp b/libs/binder/RpcAddress.cpp new file mode 100644 index 0000000000..5c3232045e --- /dev/null +++ b/libs/binder/RpcAddress.cpp @@ -0,0 +1,90 @@ +/* + * 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 <binder/RpcAddress.h> + +#include <binder/Parcel.h> + +#include "Debug.h" +#include "RpcState.h" +#include "RpcWireFormat.h" + +namespace android { + +RpcAddress RpcAddress::zero() { + return RpcAddress(); +} + +bool RpcAddress::isZero() const { + RpcWireAddress ZERO{0}; + return memcmp(mRawAddr.get(), &ZERO, sizeof(RpcWireAddress)) == 0; +} + +static void ReadRandomBytes(uint8_t* buf, size_t len) { + int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (fd == -1) { + ALOGE("%s: cannot read /dev/urandom", __func__); + return; + } + + size_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd, buf, len))) > 0) { + len -= n; + buf += n; + } + if (len > 0) { + ALOGW("%s: there are %d bytes skipped", __func__, (int)len); + } + close(fd); +} + +RpcAddress RpcAddress::unique() { + RpcAddress ret; + ReadRandomBytes((uint8_t*)ret.mRawAddr.get(), sizeof(RpcWireAddress)); + LOG_RPC_DETAIL("Creating new address: %s", ret.toString().c_str()); + return ret; +} + +RpcAddress RpcAddress::fromRawEmbedded(const RpcWireAddress* raw) { + RpcAddress addr; + memcpy(addr.mRawAddr.get(), raw, sizeof(RpcWireAddress)); + return addr; +} + +const RpcWireAddress& RpcAddress::viewRawEmbedded() const { + return *mRawAddr.get(); +} + +bool RpcAddress::operator<(const RpcAddress& rhs) const { + return std::memcmp(mRawAddr.get(), rhs.mRawAddr.get(), sizeof(RpcWireAddress)) < 0; +} + +std::string RpcAddress::toString() const { + return hexString(mRawAddr.get(), sizeof(RpcWireAddress)); +} + +status_t RpcAddress::writeToParcel(Parcel* parcel) const { + return parcel->write(mRawAddr.get(), sizeof(RpcWireAddress)); +} + +status_t RpcAddress::readFromParcel(const Parcel& parcel) { + return parcel.read(mRawAddr.get(), sizeof(RpcWireAddress)); +} + +RpcAddress::~RpcAddress() {} +RpcAddress::RpcAddress() : mRawAddr(std::make_shared<RpcWireAddress>()) {} + +} // namespace android diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp new file mode 100644 index 0000000000..83a1618e9f --- /dev/null +++ b/libs/binder/RpcConnection.cpp @@ -0,0 +1,299 @@ +/* + * 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 "RpcConnection" + +#include <binder/RpcConnection.h> + +#include <binder/Parcel.h> +#include <binder/Stability.h> + +#include "RpcState.h" +#include "RpcWireFormat.h" + +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#if defined(__GLIBC__) +extern "C" pid_t gettid(); +#endif + +namespace android { + +using base::unique_fd; + +RpcConnection::RpcConnection() { + LOG_RPC_DETAIL("RpcConnection created %p", this); + + mState = std::make_unique<RpcState>(); +} +RpcConnection::~RpcConnection() { + LOG_RPC_DETAIL("RpcConnection destroyed %p", this); +} + +sp<RpcConnection> RpcConnection::make() { + return new RpcConnection; +} + +bool RpcConnection::setupUnixDomainServer(const char* path) { + LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Only supports one server now"); + + unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0))); + if (serverFd == -1) { + ALOGE("Could not create socket at %s: %s", path, strerror(errno)); + return false; + } + + struct sockaddr_un addr = { + .sun_family = AF_UNIX, + }; + + unsigned int pathLen = strlen(path) + 1; + LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen); + memcpy(addr.sun_path, path, pathLen); + + if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) { + ALOGE("Could not bind socket at %s: %s", path, strerror(errno)); + return false; + } + + if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) { + ALOGE("Could not listen socket at %s: %s", path, strerror(errno)); + return false; + } + + mServer = std::move(serverFd); + return true; +} + +bool RpcConnection::addUnixDomainClient(const char* path) { + LOG_RPC_DETAIL("Connecting on path: %s", path); + + unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0))); + if (serverFd == -1) { + ALOGE("Could not create socket at %s: %s", path, strerror(errno)); + return false; + } + + struct sockaddr_un addr = { + .sun_family = AF_UNIX, + }; + + unsigned int pathLen = strlen(path) + 1; + LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen); + memcpy(addr.sun_path, path, pathLen); + + if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) { + ALOGE("Could not connect socket at %s: %s", path, strerror(errno)); + return false; + } + + LOG_RPC_DETAIL("Unix domain client with fd %d", serverFd.get()); + + addClient(std::move(serverFd)); + return true; +} + +sp<IBinder> RpcConnection::getRootObject() { + ExclusiveSocket socket(this, SocketUse::CLIENT); + return state()->getRootObject(socket.fd(), this); +} + +status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + ExclusiveSocket socket(this, + (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC + : SocketUse::CLIENT); + return state()->transact(socket.fd(), address, code, data, this, reply, flags); +} + +status_t RpcConnection::sendDecStrong(const RpcAddress& address) { + ExclusiveSocket socket(this, SocketUse::CLIENT_REFCOUNT); + return state()->sendDecStrong(socket.fd(), address); +} + +void RpcConnection::join() { + // establish a connection + { + struct sockaddr_un clientSa; + socklen_t clientSaLen = sizeof(clientSa); + + unique_fd clientFd(TEMP_FAILURE_RETRY( + accept4(mServer.get(), (struct sockaddr*)&clientSa, &clientSaLen, SOCK_CLOEXEC))); + if (clientFd < 0) { + // If this log becomes confusing, should save more state from setupUnixDomainServer + // in order to output here. + ALOGE("Could not accept4 socket: %s", strerror(errno)); + return; + } + + LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get()); + + addServer(std::move(clientFd)); + } + + // We may not use the connection we just established (two threads might + // establish connections for each other), but for now, just use one + // server/socket connection. + ExclusiveSocket socket(this, SocketUse::SERVER); + + while (true) { + status_t error = state()->getAndExecuteCommand(socket.fd(), this); + + if (error != OK) { + ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str()); + return; + } + } +} + +void RpcConnection::setForServer(const wp<RpcServer>& server) { + mForServer = server; +} + +wp<RpcServer> RpcConnection::server() { + return mForServer; +} + +void RpcConnection::addClient(base::unique_fd&& fd) { + std::lock_guard<std::mutex> _l(mSocketMutex); + sp<ConnectionSocket> connection = new ConnectionSocket(); + connection->fd = std::move(fd); + mClients.push_back(connection); +} + +void RpcConnection::addServer(base::unique_fd&& fd) { + std::lock_guard<std::mutex> _l(mSocketMutex); + sp<ConnectionSocket> connection = new ConnectionSocket(); + connection->fd = std::move(fd); + mServers.push_back(connection); +} + +RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use) + : mConnection(connection) { + pid_t tid = gettid(); + std::unique_lock<std::mutex> _l(mConnection->mSocketMutex); + + mConnection->mWaitingThreads++; + while (true) { + sp<ConnectionSocket> exclusive; + sp<ConnectionSocket> available; + + // CHECK FOR DEDICATED CLIENT SOCKET + // + // A server/looper should always use a dedicated connection. + if (use != SocketUse::SERVER) { + findSocket(tid, &exclusive, &available, mConnection->mClients, + mConnection->mClientsOffset); + + // WARNING: this assumes a server cannot request its client to send + // a transaction, as mServers is excluded below. + // + // Imagine we have more than one thread in play, and a single thread + // sends a synchronous, then an asynchronous command. Imagine the + // asynchronous command is sent on the first client socket. Then, if + // we naively send a synchronous command to that same socket, the + // thread on the far side might be busy processing the asynchronous + // command. So, we move to considering the second available thread + // for subsequent calls. + if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) { + mConnection->mClientsOffset = + (mConnection->mClientsOffset + 1) % mConnection->mClients.size(); + } + } + + // USE SERVING SOCKET (to start serving or for nested transaction) + // + // asynchronous calls cannot be nested + if (use != SocketUse::CLIENT_ASYNC) { + // servers should start serving on an available thread only + // otherwise, this should only be a nested call + bool useAvailable = use == SocketUse::SERVER; + + findSocket(tid, &exclusive, (useAvailable ? &available : nullptr), + mConnection->mServers, 0 /* index hint */); + } + + // if our thread is already using a connection, prioritize using that + if (exclusive != nullptr) { + mSocket = exclusive; + mReentrant = true; + break; + } else if (available != nullptr) { + mSocket = available; + mSocket->exclusiveTid = tid; + break; + } + + LOG_ALWAYS_FATAL_IF(use == SocketUse::SERVER, "Must create connection to join one."); + + // in regular binder, this would usually be a deadlock :) + LOG_ALWAYS_FATAL_IF(mConnection->mClients.size() == 0, + "Not a client of any connection. You must create a connection to an " + "RPC server to make any non-nested (e.g. oneway or on another thread) " + "calls."); + + LOG_RPC_DETAIL("No available connection (have %zu clients and %zu servers). Waiting...", + mConnection->mClients.size(), mConnection->mServers.size()); + mConnection->mSocketCv.wait(_l); + } + mConnection->mWaitingThreads--; +} + +void RpcConnection::ExclusiveSocket::findSocket(pid_t tid, sp<ConnectionSocket>* exclusive, + sp<ConnectionSocket>* available, + std::vector<sp<ConnectionSocket>>& sockets, + size_t socketsIndexHint) { + LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(), + "Bad index %zu >= %zu", socketsIndexHint, sockets.size()); + + if (*exclusive != nullptr) return; // consistent with break below + + for (size_t i = 0; i < sockets.size(); i++) { + sp<ConnectionSocket>& socket = sockets[(i + socketsIndexHint) % sockets.size()]; + + // take first available connection (intuition = caching) + if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) { + *available = socket; + continue; + } + + // though, prefer to take connection which is already inuse by this thread + // (nested transactions) + if (exclusive && socket->exclusiveTid == tid) { + *exclusive = socket; + break; // consistent with return above + } + } +} + +RpcConnection::ExclusiveSocket::~ExclusiveSocket() { + // reentrant use of a connection means something less deep in the call stack + // is using this fd, and it retains the right to it. So, we don't give up + // exclusive ownership, and no thread is freed. + if (!mReentrant) { + std::unique_lock<std::mutex> _l(mConnection->mSocketMutex); + mSocket->exclusiveTid = std::nullopt; + if (mConnection->mWaitingThreads > 0) { + _l.unlock(); + mConnection->mSocketCv.notify_one(); + } + } +} + +} // namespace android diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp new file mode 100644 index 0000000000..df07916f7a --- /dev/null +++ b/libs/binder/RpcServer.cpp @@ -0,0 +1,64 @@ +/* + * 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 "RpcServer" + +#include <sys/socket.h> +#include <sys/un.h> + +#include <vector> + +#include <binder/Parcel.h> +#include <binder/RpcServer.h> +#include <log/log.h> +#include "RpcState.h" + +#include "RpcWireFormat.h" + +namespace android { + +using base::unique_fd; + +RpcServer::RpcServer() {} +RpcServer::~RpcServer() {} + +sp<RpcServer> RpcServer::make() { + return new RpcServer; +} + +void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() { + mAgreedExperimental = true; +} + +sp<RpcConnection> RpcServer::addClientConnection() { + LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); + + auto connection = RpcConnection::make(); + connection->setForServer(this); + mConnections.push_back(connection); + return connection; +} + +void RpcServer::setRootObject(const sp<IBinder>& binder) { + LOG_ALWAYS_FATAL_IF(mRootObject != nullptr, "There can only be one root object"); + mRootObject = binder; +} + +sp<IBinder> RpcServer::getRootObject() { + return mRootObject; +} + +} // namespace android diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp new file mode 100644 index 0000000000..64e842e927 --- /dev/null +++ b/libs/binder/RpcState.cpp @@ -0,0 +1,663 @@ +/* + * 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 "RpcState" + +#include "RpcState.h" + +#include <binder/BpBinder.h> +#include <binder/RpcServer.h> + +#include "Debug.h" +#include "RpcWireFormat.h" + +#include <inttypes.h> + +namespace android { + +RpcState::RpcState() {} +RpcState::~RpcState() {} + +status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp<IBinder>& binder, + RpcAddress* outAddress) { + bool isRemote = binder->remoteBinder(); + bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder(); + + if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcConnection() != connection) { + // We need to be able to send instructions over the socket for how to + // connect to a different server, and we also need to let the host + // process know that this is happening. + ALOGE("Canot send binder from unrelated binder RPC connection."); + return INVALID_OPERATION; + } + + if (isRemote && !isRpc) { + // Without additional work, this would have the effect of using this + // process to proxy calls from the socket over to the other process, and + // it would make those calls look like they come from us (not over the + // sockets). In order to make this work transparently like binder, we + // would instead need to send instructions over the socket for how to + // connect to the host process, and we also need to let the host process + // know this was happening. + ALOGE("Cannot send binder proxy %p over sockets", binder.get()); + return INVALID_OPERATION; + } + + std::lock_guard<std::mutex> _l(mNodeMutex); + + // TODO(b/182939933): maybe move address out of BpBinder, and keep binder->address map + // in RpcState + for (auto& [addr, node] : mNodeForAddress) { + if (binder == node.binder) { + if (isRpc) { + const RpcAddress& actualAddr = + binder->remoteBinder()->getPrivateAccessorForId().rpcAddress(); + // TODO(b/182939933): this is only checking integrity of data structure + // a different data structure doesn't need this + LOG_ALWAYS_FATAL_IF(addr < actualAddr, "Address mismatch"); + LOG_ALWAYS_FATAL_IF(actualAddr < addr, "Address mismatch"); + } + node.timesSent++; + node.sentRef = binder; // might already be set + *outAddress = addr; + return OK; + } + } + LOG_ALWAYS_FATAL_IF(isRpc, "RPC binder must have known address at this point"); + + auto&& [it, inserted] = mNodeForAddress.insert({RpcAddress::unique(), + BinderNode{ + .binder = binder, + .timesSent = 1, + .sentRef = binder, + }}); + // TODO(b/182939933): better organization could avoid needing this log + LOG_ALWAYS_FATAL_IF(!inserted); + + *outAddress = it->first; + return OK; +} + +sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection, + const RpcAddress& address) { + std::unique_lock<std::mutex> _l(mNodeMutex); + + if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) { + sp<IBinder> binder = it->second.binder.promote(); + + // implicitly have strong RPC refcount, since we received this binder + it->second.timesRecd++; + + _l.unlock(); + + // We have timesRecd RPC refcounts, but we only need to hold on to one + // when we keep the object. All additional dec strongs are sent + // immediately, we wait to send the last one in BpBinder::onLastDecStrong. + (void)connection->sendDecStrong(address); + + return binder; + } + + auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}}); + LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy"); + + // Currently, all binders are assumed to be part of the same connection (no + // device global binders in the RPC world). + sp<IBinder> binder = BpBinder::create(connection, it->first); + it->second.binder = binder; + it->second.timesRecd = 1; + return binder; +} + +size_t RpcState::countBinders() { + std::lock_guard<std::mutex> _l(mNodeMutex); + return mNodeForAddress.size(); +} + +void RpcState::dump() { + std::lock_guard<std::mutex> _l(mNodeMutex); + ALOGE("DUMP OF RpcState %p", this); + ALOGE("DUMP OF RpcState (%zu nodes)", mNodeForAddress.size()); + for (const auto& [address, node] : mNodeForAddress) { + sp<IBinder> binder = node.binder.promote(); + + const char* desc; + if (binder) { + if (binder->remoteBinder()) { + if (binder->remoteBinder()->isRpcBinder()) { + desc = "(rpc binder proxy)"; + } else { + desc = "(binder proxy)"; + } + } else { + desc = "(local binder)"; + } + } else { + desc = "(null)"; + } + + ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a:%s type:%s", + node.binder.unsafe_get(), node.timesSent, node.timesRecd, address.toString().c_str(), + desc); + } + ALOGE("END DUMP OF RpcState"); +} + +void RpcState::terminate() { + if (SHOULD_LOG_RPC_DETAIL) { + ALOGE("RpcState::terminate()"); + dump(); + } + + // if the destructor of a binder object makes another RPC call, then calling + // decStrong could deadlock. So, we must hold onto these binders until + // mNodeMutex is no longer taken. + std::vector<sp<IBinder>> tempHoldBinder; + + { + std::lock_guard<std::mutex> _l(mNodeMutex); + mTerminated = true; + for (auto& [address, node] : mNodeForAddress) { + sp<IBinder> binder = node.binder.promote(); + LOG_ALWAYS_FATAL_IF(binder == nullptr, "Binder %p expected to be owned.", binder.get()); + + if (node.sentRef != nullptr) { + tempHoldBinder.push_back(node.sentRef); + } + } + + mNodeForAddress.clear(); + } +} + +bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) { + LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str()); + + if (size > std::numeric_limits<ssize_t>::max()) { + ALOGE("Cannot send %s at size %zu (too big)", what, size); + terminate(); + return false; + } + + ssize_t sent = TEMP_FAILURE_RETRY(send(fd.get(), data, size, 0)); + + if (sent < 0 || sent != static_cast<ssize_t>(size)) { + ALOGE("Failed to send %s (sent %zd of %zu bytes) on fd %d, error: %s", what, sent, size, + fd.get(), strerror(errno)); + + terminate(); + return false; + } + + return true; +} + +bool RpcState::rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size) { + if (size > std::numeric_limits<ssize_t>::max()) { + ALOGE("Cannot rec %s at size %zu (too big)", what, size); + terminate(); + return false; + } + + ssize_t recd = TEMP_FAILURE_RETRY(recv(fd.get(), data, size, MSG_WAITALL)); + + if (recd < 0 || recd != static_cast<ssize_t>(size)) { + terminate(); + + if (recd == 0 && errno == 0) { + LOG_RPC_DETAIL("No more data when trying to read %s on fd %d", what, fd.get()); + return false; + } + + ALOGE("Failed to read %s (received %zd of %zu bytes) on fd %d, error: %s", what, recd, size, + fd.get(), strerror(errno)); + return false; + } else { + LOG_RPC_DETAIL("Received %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str()); + } + + return true; +} + +sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, + const sp<RpcConnection>& connection) { + Parcel data; + data.markForRpc(connection); + Parcel reply; + + status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data, + connection, &reply, 0); + if (status != OK) { + ALOGE("Error getting root object: %s", statusToString(status).c_str()); + return nullptr; + } + + return reply.readStrongBinder(); +} + +status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code, + const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply, + uint32_t flags) { + uint64_t asyncNumber = 0; + + if (!address.isZero()) { + std::lock_guard<std::mutex> _l(mNodeMutex); + if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races + auto it = mNodeForAddress.find(address); + LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending transact on unknown address %s", + address.toString().c_str()); + + if (flags & IBinder::FLAG_ONEWAY) { + asyncNumber = it->second.asyncNumber++; + } + } + + if (!data.isForRpc()) { + ALOGE("Refusing to send RPC with parcel not crafted for RPC"); + return BAD_TYPE; + } + + if (data.objectsCount() != 0) { + ALOGE("Parcel at %p has attached objects but is being used in an RPC call", &data); + return BAD_TYPE; + } + + RpcWireTransaction transaction{ + .address = address.viewRawEmbedded(), + .code = code, + .flags = flags, + .asyncNumber = asyncNumber, + }; + + std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize()); + memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction)); + memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize()); + + if (transactionData.size() > std::numeric_limits<uint32_t>::max()) { + ALOGE("Transaction size too big %zu", transactionData.size()); + return BAD_VALUE; + } + + RpcWireHeader command{ + .command = RPC_COMMAND_TRANSACT, + .bodySize = static_cast<uint32_t>(transactionData.size()), + }; + + if (!rpcSend(fd, "transact header", &command, sizeof(command))) { + return DEAD_OBJECT; + } + if (!rpcSend(fd, "command body", transactionData.data(), transactionData.size())) { + return DEAD_OBJECT; + } + + if (flags & IBinder::FLAG_ONEWAY) { + return OK; // do not wait for result + } + + LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction."); + + return waitForReply(fd, connection, reply); +} + +static void cleanup_data(Parcel* p, const uint8_t* data, size_t dataSize, + const binder_size_t* objects, size_t objectsCount) { + (void)p; + delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data)); + (void)dataSize; + LOG_ALWAYS_FATAL_IF(objects != nullptr); + LOG_ALWAYS_FATAL_IF(objectsCount, 0); +} + +status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnection>& connection, + Parcel* reply) { + RpcWireHeader command; + while (true) { + if (!rpcRec(fd, "command header", &command, sizeof(command))) { + return DEAD_OBJECT; + } + + if (command.command == RPC_COMMAND_REPLY) break; + + status_t status = processServerCommand(fd, connection, command); + if (status != OK) return status; + } + + uint8_t* data = new uint8_t[command.bodySize]; + + if (!rpcRec(fd, "reply body", data, command.bodySize)) { + return DEAD_OBJECT; + } + + if (command.bodySize < sizeof(RpcWireReply)) { + ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!", + sizeof(RpcWireReply), command.bodySize); + terminate(); + return BAD_VALUE; + } + RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data); + if (rpcReply->status != OK) return rpcReply->status; + + reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data), + nullptr, 0, cleanup_data); + + reply->markForRpc(connection); + + return OK; +} + +status_t RpcState::sendDecStrong(const base::unique_fd& fd, const RpcAddress& addr) { + { + std::lock_guard<std::mutex> _l(mNodeMutex); + if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races + auto it = mNodeForAddress.find(addr); + LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending dec strong on unknown address %s", + addr.toString().c_str()); + LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %s", + addr.toString().c_str()); + + it->second.timesRecd--; + if (it->second.timesRecd == 0 && it->second.timesSent == 0) { + mNodeForAddress.erase(it); + } + } + + RpcWireHeader cmd = { + .command = RPC_COMMAND_DEC_STRONG, + .bodySize = sizeof(RpcWireAddress), + }; + if (!rpcSend(fd, "dec ref header", &cmd, sizeof(cmd))) return DEAD_OBJECT; + if (!rpcSend(fd, "dec ref body", &addr.viewRawEmbedded(), sizeof(RpcWireAddress))) + return DEAD_OBJECT; + return OK; +} + +status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd, + const sp<RpcConnection>& connection) { + LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get()); + + RpcWireHeader command; + if (!rpcRec(fd, "command header", &command, sizeof(command))) { + return DEAD_OBJECT; + } + + return processServerCommand(fd, connection, command); +} + +status_t RpcState::processServerCommand(const base::unique_fd& fd, + const sp<RpcConnection>& connection, + const RpcWireHeader& command) { + switch (command.command) { + case RPC_COMMAND_TRANSACT: + return processTransact(fd, connection, command); + case RPC_COMMAND_DEC_STRONG: + return processDecStrong(fd, command); + } + + // We should always know the version of the opposing side, and since the + // RPC-binder-level wire protocol is not self synchronizing, we have no way + // to understand where the current command ends and the next one begins. We + // also can't consider it a fatal error because this would allow any client + // to kill us, so ending the connection for misbehaving client. + ALOGE("Unknown RPC command %d - terminating connection", command.command); + terminate(); + return DEAD_OBJECT; +} +status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnection>& connection, + const RpcWireHeader& command) { + LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command); + + std::vector<uint8_t> transactionData(command.bodySize); + if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) { + return DEAD_OBJECT; + } + + return processTransactInternal(fd, connection, std::move(transactionData)); +} + +status_t RpcState::processTransactInternal(const base::unique_fd& fd, + const sp<RpcConnection>& connection, + std::vector<uint8_t>&& transactionData) { + if (transactionData.size() < sizeof(RpcWireTransaction)) { + ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!", + sizeof(RpcWireTransaction), transactionData.size()); + terminate(); + return BAD_VALUE; + } + RpcWireTransaction* transaction = reinterpret_cast<RpcWireTransaction*>(transactionData.data()); + + // TODO(b/182939933): heap allocation just for lookup in mNodeForAddress, + // maybe add an RpcAddress 'view' if the type remains 'heavy' + auto addr = RpcAddress::fromRawEmbedded(&transaction->address); + + status_t replyStatus = OK; + sp<IBinder> target; + if (!addr.isZero()) { + std::lock_guard<std::mutex> _l(mNodeMutex); + + auto it = mNodeForAddress.find(addr); + if (it == mNodeForAddress.end()) { + ALOGE("Unknown binder address %s.", addr.toString().c_str()); + dump(); + replyStatus = BAD_VALUE; + } else { + target = it->second.binder.promote(); + if (target == nullptr) { + // This can happen if the binder is remote in this process, and + // another thread has called the last decStrong on this binder. + // However, for local binders, it indicates a misbehaving client + // (any binder which is being transacted on should be holding a + // strong ref count), so in either case, terminating the + // connection. + ALOGE("While transacting, binder has been deleted at address %s. Terminating!", + addr.toString().c_str()); + terminate(); + replyStatus = BAD_VALUE; + } else if (target->localBinder() == nullptr) { + ALOGE("Transactions can only go to local binders, not address %s. Terminating!", + addr.toString().c_str()); + terminate(); + replyStatus = BAD_VALUE; + } else if (transaction->flags & IBinder::FLAG_ONEWAY) { + if (transaction->asyncNumber != it->second.asyncNumber) { + // we need to process some other asynchronous transaction + // first + // TODO(b/183140903): limit enqueues/detect overfill for bad client + // TODO(b/183140903): detect when an object is deleted when it still has + // pending async transactions + it->second.asyncTodo.push(BinderNode::AsyncTodo{ + .data = std::move(transactionData), + .asyncNumber = transaction->asyncNumber, + }); + LOG_RPC_DETAIL("Enqueuing %" PRId64 " on %s", transaction->asyncNumber, + addr.toString().c_str()); + return OK; + } + } + } + } + + Parcel data; + data.setData(transaction->data, transactionData.size() - offsetof(RpcWireTransaction, data)); + data.markForRpc(connection); + + Parcel reply; + reply.markForRpc(connection); + + if (replyStatus == OK) { + if (target) { + replyStatus = target->transact(transaction->code, data, &reply, transaction->flags); + } else { + LOG_RPC_DETAIL("Got special transaction %u", transaction->code); + // special case for 'zero' address (special server commands) + switch (transaction->code) { + case RPC_SPECIAL_TRANSACT_GET_ROOT: { + sp<IBinder> root; + sp<RpcServer> server = connection->server().promote(); + if (server) { + root = server->getRootObject(); + } else { + ALOGE("Root object requested, but no server attached."); + } + + replyStatus = reply.writeStrongBinder(root); + break; + } + default: { + replyStatus = UNKNOWN_TRANSACTION; + } + } + } + } + + if (transaction->flags & IBinder::FLAG_ONEWAY) { + if (replyStatus != OK) { + ALOGW("Oneway call failed with error: %d", replyStatus); + } + + LOG_RPC_DETAIL("Processed async transaction %" PRId64 " on %s", transaction->asyncNumber, + addr.toString().c_str()); + + // Check to see if there is another asynchronous transaction to process. + // This behavior differs from binder behavior, since in the binder + // driver, asynchronous transactions will be processed after existing + // pending binder transactions on the queue. The downside of this is + // that asynchronous transactions can be drowned out by synchronous + // transactions. However, we have no easy way to queue these + // transactions after the synchronous transactions we may want to read + // from the wire. So, in socket binder here, we have the opposite + // downside: asynchronous transactions may drown out synchronous + // transactions. + { + std::unique_lock<std::mutex> _l(mNodeMutex); + auto it = mNodeForAddress.find(addr); + // last refcount dropped after this transaction happened + if (it == mNodeForAddress.end()) return OK; + + // note - only updated now, instead of later, so that other threads + // will queue any later transactions + + // TODO(b/183140903): support > 2**64 async transactions + // (we can do this by allowing asyncNumber to wrap, since we + // don't expect more than 2**64 simultaneous transactions) + it->second.asyncNumber++; + + if (it->second.asyncTodo.size() == 0) return OK; + if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) { + LOG_RPC_DETAIL("Found next async transaction %" PRId64 " on %s", + it->second.asyncNumber, addr.toString().c_str()); + + // justification for const_cast (consider avoiding priority_queue): + // - AsyncTodo operator< doesn't depend on 'data' object + // - gotta go fast + std::vector<uint8_t> data = std::move( + const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data); + it->second.asyncTodo.pop(); + _l.unlock(); + return processTransactInternal(fd, connection, std::move(data)); + } + } + return OK; + } + + RpcWireReply rpcReply{ + .status = replyStatus, + }; + + std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize()); + memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply)); + memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize()); + + if (replyData.size() > std::numeric_limits<uint32_t>::max()) { + ALOGE("Reply size too big %zu", transactionData.size()); + terminate(); + return BAD_VALUE; + } + + RpcWireHeader cmdReply{ + .command = RPC_COMMAND_REPLY, + .bodySize = static_cast<uint32_t>(replyData.size()), + }; + + if (!rpcSend(fd, "reply header", &cmdReply, sizeof(RpcWireHeader))) { + return DEAD_OBJECT; + } + if (!rpcSend(fd, "reply body", replyData.data(), replyData.size())) { + return DEAD_OBJECT; + } + return OK; +} + +status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) { + LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command); + + std::vector<uint8_t> commandData(command.bodySize); + if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) { + return DEAD_OBJECT; + } + + if (command.bodySize < sizeof(RpcWireAddress)) { + ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireAddress. Terminating!", + sizeof(RpcWireAddress), command.bodySize); + terminate(); + return BAD_VALUE; + } + RpcWireAddress* address = reinterpret_cast<RpcWireAddress*>(commandData.data()); + + // TODO(b/182939933): heap allocation just for lookup + auto addr = RpcAddress::fromRawEmbedded(address); + std::unique_lock<std::mutex> _l(mNodeMutex); + auto it = mNodeForAddress.find(addr); + if (it == mNodeForAddress.end()) { + ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str()); + dump(); + return OK; + } + + sp<IBinder> target = it->second.binder.promote(); + if (target == nullptr) { + ALOGE("While requesting dec strong, binder has been deleted at address %s. Terminating!", + addr.toString().c_str()); + terminate(); + return BAD_VALUE; + } + + if (it->second.timesSent == 0) { + ALOGE("No record of sending binder, but requested decStrong: %s", addr.toString().c_str()); + return OK; + } + + LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %s", + addr.toString().c_str()); + + sp<IBinder> tempHold; + + it->second.timesSent--; + if (it->second.timesSent == 0) { + tempHold = it->second.sentRef; + it->second.sentRef = nullptr; + + if (it->second.timesRecd == 0) { + mNodeForAddress.erase(it); + } + } + + _l.unlock(); + tempHold = nullptr; // destructor may make binder calls on this connection + + return OK; +} + +} // namespace android diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h new file mode 100644 index 0000000000..f4f5151c73 --- /dev/null +++ b/libs/binder/RpcState.h @@ -0,0 +1,170 @@ +/* + * 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. + */ +#pragma once + +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <binder/Parcel.h> +#include <binder/RpcConnection.h> + +#include <map> +#include <queue> + +namespace android { + +struct RpcWireHeader; + +/** + * Log a lot more information about RPC calls, when debugging issues. Usually, + * you would want to enable this in only one process. If repeated issues require + * a specific subset of logs to debug, this could be broken up like + * IPCThreadState's. + */ +#define SHOULD_LOG_RPC_DETAIL false + +#if SHOULD_LOG_RPC_DETAIL +#define LOG_RPC_DETAIL(...) ALOGI(__VA_ARGS__) +#else +#define LOG_RPC_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking +#endif + +/** + * Abstracts away management of ref counts and the wire format from + * RpcConnection + */ +class RpcState { +public: + RpcState(); + ~RpcState(); + + sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection); + + [[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address, + uint32_t code, const Parcel& data, + const sp<RpcConnection>& connection, Parcel* reply, + uint32_t flags); + [[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address); + [[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd, + const sp<RpcConnection>& connection); + + /** + * Called by Parcel for outgoing binders. This implies one refcount of + * ownership to the outgoing binder. + */ + [[nodiscard]] status_t onBinderLeaving(const sp<RpcConnection>& connection, + const sp<IBinder>& binder, RpcAddress* outAddress); + + /** + * Called by Parcel for incoming binders. This either returns the refcount + * to the process, if this process already has one, or it takes ownership of + * that refcount + */ + sp<IBinder> onBinderEntering(const sp<RpcConnection>& connection, const RpcAddress& address); + + size_t countBinders(); + void dump(); + +private: + /** + * Called when reading or writing data to a connection fails to clean up + * data associated with the connection in order to cleanup binders. + * Specifically, we have a strong dependency cycle, since BpBinder is + * OBJECT_LIFETIME_WEAK (so that onAttemptIncStrong may return true). + * + * BpBinder -> RpcConnection -> RpcState + * ^-----------------------------/ + * + * In the success case, eventually all refcounts should be propagated over + * the connection, though this could also be called to eagerly cleanup + * the connection. + * + * WARNING: RpcState is responsible for calling this when the connection is + * no longer recoverable. + */ + void terminate(); + + [[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data, + size_t size); + [[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size); + + [[nodiscard]] status_t waitForReply(const base::unique_fd& fd, + const sp<RpcConnection>& connection, Parcel* reply); + [[nodiscard]] status_t processServerCommand(const base::unique_fd& fd, + const sp<RpcConnection>& connection, + const RpcWireHeader& command); + [[nodiscard]] status_t processTransact(const base::unique_fd& fd, + const sp<RpcConnection>& connection, + const RpcWireHeader& command); + [[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd, + const sp<RpcConnection>& connection, + std::vector<uint8_t>&& transactionData); + [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd, + const RpcWireHeader& command); + + struct BinderNode { + // Two cases: + // A - local binder we are serving + // B - remote binder, we are sending transactions to + wp<IBinder> binder; + + // if timesSent > 0, this will be equal to binder.promote() + sp<IBinder> sentRef; + + // Number of times we've sent this binder out of process, which + // translates to an implicit strong count. A client must send RPC binder + // socket's dec ref for each time it is sent out of process in order to + // deallocate it. Note, a proxy binder we are holding onto might be + // sent (this is important when the only remaining refcount of this + // binder is the one associated with a transaction sending it back to + // its server) + size_t timesSent = 0; + + // Number of times we've received this binder, each time corresponds to + // a reference we hold over the wire (not a local incStrong/decStrong) + size_t timesRecd = 0; + + // transaction ID, for async transactions + uint64_t asyncNumber = 0; + + // + // CASE A - local binder we are serving + // + + // async transaction queue, _only_ for local binder + struct AsyncTodo { + std::vector<uint8_t> data; // most convenient format, to move it here + uint64_t asyncNumber = 0; + + bool operator<(const AsyncTodo& o) const { + return asyncNumber > /* !!! */ o.asyncNumber; + } + }; + std::priority_queue<AsyncTodo> asyncTodo; + + // + // CASE B - remote binder, we are sending transactions to + // + + // (no additional data specific to remote binders) + }; + + std::mutex mNodeMutex; + bool mTerminated = false; + // binders known by both sides of a connection + std::map<RpcAddress, BinderNode> mNodeForAddress; +}; + +} // namespace android diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h new file mode 100644 index 0000000000..60ec6c91bf --- /dev/null +++ b/libs/binder/RpcWireFormat.h @@ -0,0 +1,85 @@ +/* + * 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. + */ +#pragma once + +namespace android { + +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wpadded" + +enum : uint32_t { + /** + * follows is RpcWireTransaction, if flags != oneway, reply w/ RPC_COMMAND_REPLY expected + */ + RPC_COMMAND_TRANSACT = 0, + /** + * follows is RpcWireReply + */ + RPC_COMMAND_REPLY, + /** + * follows is RpcWireAddress + * + * note - this in the protocol directly instead of as a 'special + * transaction' in order to keep it as lightweight as possible (we don't + * want to create a 'Parcel' object for every decref) + */ + RPC_COMMAND_DEC_STRONG, +}; + +/** + * These commands are used when the address in an RpcWireTransaction is zero'd + * out (no address). This allows the transact/reply flow to be used for + * additional server commands, without making the protocol for + * transactions/replies more complicated. + */ +enum : uint32_t { + RPC_SPECIAL_TRANSACT_GET_ROOT = 0, +}; + +// serialization is like: +// |RpcWireHeader|struct desginated by 'command'| (over and over again) + +struct RpcWireHeader { + uint32_t command; // RPC_COMMAND_* + uint32_t bodySize; + + uint32_t reserved[2]; +}; + +struct RpcWireAddress { + uint8_t address[32]; +}; + +struct RpcWireTransaction { + RpcWireAddress address; + uint32_t code; + uint32_t flags; + + uint64_t asyncNumber; + + uint32_t reserved[4]; + + uint8_t data[0]; +}; + +struct RpcWireReply { + int32_t status; // transact return + uint8_t data[0]; +}; + +#pragma clang diagnostic pop + +} // namespace android diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp index b56e09fa6e..c3f1ba7b4d 100644 --- a/libs/binder/Stability.cpp +++ b/libs/binder/Stability.cpp @@ -38,6 +38,18 @@ Stability::Category Stability::Category::currentFromLevel(Level level) { }; } +void Stability::forceDowngradeCompilationUnit(const sp<IBinder>& binder) { + // Downgrading a remote binder would require also copying the version from + // the binder sent here. In practice though, we don't need to downgrade the + // stability of a remote binder, since this would as an effect only restrict + // what we can do to it. + LOG_ALWAYS_FATAL_IF(!binder || !binder->localBinder(), "Can only downgrade local binder"); + + auto stability = Category::currentFromLevel(getLocalLevel()); + status_t result = setRepr(binder.get(), stability.repr(), REPR_LOG | REPR_ALLOW_DOWNGRADE); + LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); +} + std::string Stability::Category::debugString() { return levelString(level) + " wire protocol version " + std::to_string(version); @@ -45,13 +57,13 @@ std::string Stability::Category::debugString() { void Stability::markCompilationUnit(IBinder* binder) { auto stability = Category::currentFromLevel(getLocalLevel()); - status_t result = setRepr(binder, stability.repr(), true /*log*/); + status_t result = setRepr(binder, stability.repr(), REPR_LOG); LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); } void Stability::markVintf(IBinder* binder) { auto stability = Category::currentFromLevel(Level::VINTF); - status_t result = setRepr(binder, stability.repr(), true /*log*/); + status_t result = setRepr(binder, stability.repr(), REPR_LOG); LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); } @@ -62,7 +74,7 @@ void Stability::debugLogStability(const std::string& tag, const sp<IBinder>& bin void Stability::markVndk(IBinder* binder) { auto stability = Category::currentFromLevel(Level::VENDOR); - status_t result = setRepr(binder, stability.repr(), true /*log*/); + status_t result = setRepr(binder, stability.repr(), REPR_LOG); LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); } @@ -72,7 +84,7 @@ bool Stability::requiresVintfDeclaration(const sp<IBinder>& binder) { void Stability::tryMarkCompilationUnit(IBinder* binder) { auto stability = Category::currentFromLevel(getLocalLevel()); - (void) setRepr(binder, stability.repr(), false /*log*/); + (void) setRepr(binder, stability.repr(), REPR_NONE); } Stability::Level Stability::getLocalLevel() { @@ -88,7 +100,10 @@ Stability::Level Stability::getLocalLevel() { #endif } -status_t Stability::setRepr(IBinder* binder, int32_t representation, bool log) { +status_t Stability::setRepr(IBinder* binder, int32_t representation, uint32_t flags) { + bool log = flags & REPR_LOG; + bool allowDowngrade = flags & REPR_ALLOW_DOWNGRADE; + auto current = getCategory(binder); auto setting = Category::fromRepr(representation); @@ -123,7 +138,11 @@ status_t Stability::setRepr(IBinder* binder, int32_t representation, bool log) { return BAD_TYPE; } - if (current.repr() != 0 && current != setting) { + if (current == setting) return OK; + + bool hasAlreadyBeenSet = current.repr() != 0; + bool isAllowedDowngrade = allowDowngrade && check(current, setting.level); + if (hasAlreadyBeenSet && !isAllowedDowngrade) { if (log) { ALOGE("Interface being set with %s but it is already marked as %s", setting.debugString().c_str(), @@ -132,7 +151,11 @@ status_t Stability::setRepr(IBinder* binder, int32_t representation, bool log) { return BAD_TYPE; } - if (current == setting) return OK; + if (isAllowedDowngrade) { + ALOGI("Interface set with %s downgraded to %s stability", + current.debugString().c_str(), + setting.debugString().c_str()); + } BBinder* local = binder->localBinder(); if (local != nullptr) { diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 1fbaa13887..b58d919d33 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -25,6 +25,9 @@ "name": "binderLibTest" }, { + "name": "binderRpcTest" + }, + { "name": "binderStabilityTest" }, { @@ -40,6 +43,9 @@ "name": "aidl_integration_test" }, { + "name": "memunreachable_binder_test" + }, + { "name": "libbinderthreadstateutils_test" }, { diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 22300acd86..8ab789318d 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -17,15 +17,19 @@ #pragma once #include <binder/IBinder.h> +#include <binder/RpcAddress.h> #include <utils/KeyedVector.h> #include <utils/Mutex.h> #include <utils/threads.h> #include <unordered_map> +#include <variant> // --------------------------------------------------------------------------- namespace android { +class RpcConnection; +class RpcState; namespace internal { class Stability; } @@ -37,6 +41,14 @@ class BpBinder : public IBinder { public: static BpBinder* create(int32_t handle); + static BpBinder* create(const sp<RpcConnection>& connection, const RpcAddress& address); + + /** + * Return value: + * true - this is associated with a socket RpcConnection + * false - (usual) binder over e.g. /dev/binder + */ + bool isRpcBinder() const; virtual const String16& getInterfaceDescriptor() const; virtual bool isBinderAlive() const; @@ -108,33 +120,56 @@ public: KeyedVector<const void*, entry_t> mObjects; }; - class PrivateAccessorForHandle { + class PrivateAccessorForId { private: - friend BpBinder; - friend ::android::Parcel; - friend ::android::ProcessState; - explicit PrivateAccessorForHandle(const BpBinder* binder) : mBinder(binder) {} - int32_t handle() const { return mBinder->handle(); } + friend class BpBinder; + friend class ::android::Parcel; + friend class ::android::ProcessState; + friend class ::android::RpcState; + explicit PrivateAccessorForId(const BpBinder* binder) : mBinder(binder) {} + + // valid if !isRpcBinder + int32_t binderHandle() const { return mBinder->binderHandle(); } + + // valid if isRpcBinder + const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); } + const sp<RpcConnection>& rpcConnection() const { return mBinder->rpcConnection(); } + const BpBinder* mBinder; }; - const PrivateAccessorForHandle getPrivateAccessorForHandle() const { - return PrivateAccessorForHandle(this); + const PrivateAccessorForId getPrivateAccessorForId() const { + return PrivateAccessorForId(this); } private: - friend PrivateAccessorForHandle; + friend PrivateAccessorForId; + + struct BinderHandle { + int32_t handle; + }; + struct SocketHandle { + sp<RpcConnection> connection; + RpcAddress address; + }; + using Handle = std::variant<BinderHandle, SocketHandle>; + + int32_t binderHandle() const; + const RpcAddress& rpcAddress() const; + const sp<RpcConnection>& rpcConnection() const; + + explicit BpBinder(Handle&& handle); + BpBinder(BinderHandle&& handle, int32_t trackedUid); + explicit BpBinder(SocketHandle&& handle); - int32_t handle() const; - BpBinder(int32_t handle,int32_t trackedUid); virtual ~BpBinder(); virtual void onFirstRef(); virtual void onLastStrongRef(const void* id); virtual bool onIncStrongAttempted(uint32_t flags, const void* id); friend ::android::internal::Stability; - int32_t mStability; - const int32_t mHandle; + int32_t mStability; + Handle mHandle; struct Obituary { wp<DeathRecipient> recipient; diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 7b298f5b39..957837233b 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -50,11 +50,14 @@ template <typename T> class LightFlattenable; class IBinder; class IPCThreadState; class ProcessState; +class RpcConnection; class String8; class TextOutput; class Parcel { friend class IPCThreadState; + friend class RpcState; + public: class ReadableBlob; class WritableBlob; @@ -92,7 +95,21 @@ public: // In order to verify this, heap dumps should be used. void markSensitive() const; - // Writes the RPC header. + // For a 'data' Parcel, this should mark the Parcel as being prepared for a + // transaction on this specific binder object. Based on this, the format of + // the wire binder protocol may change (data is written differently when it + // is for an RPC transaction). + void markForBinder(const sp<IBinder>& binder); + + // Whenever possible, markForBinder should be preferred. This method is + // called automatically on reply Parcels for RPC transactions. + void markForRpc(const sp<RpcConnection>& connection); + + // Whether this Parcel is written for RPC transactions (after calls to + // markForBinder or markForRpc). + bool isForRpc() const; + + // Writes the IPC/RPC header. status_t writeInterfaceToken(const String16& interface); status_t writeInterfaceToken(const char16_t* str, size_t len); @@ -1106,6 +1123,7 @@ private: mutable bool mObjectsSorted; mutable bool mRequestHeaderPresent; + mutable size_t mWorkSourceRequestHeaderPosition; mutable bool mFdsKnown; @@ -1118,8 +1136,7 @@ private: release_func mOwner; - // TODO(167966510): reserved for binder/version/stability - void* mReserved = reinterpret_cast<void*>(0xAAAAAAAA); + sp<RpcConnection> mConnection; class Blob { public: diff --git a/libs/binder/include/binder/RpcAddress.h b/libs/binder/include/binder/RpcAddress.h new file mode 100644 index 0000000000..5a3f3a6afa --- /dev/null +++ b/libs/binder/include/binder/RpcAddress.h @@ -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. + */ +#pragma once + +#include <memory> + +#include <utils/Errors.h> + +// WARNING: This is a feature which is still in development, and it is subject +// to radical change. Any production use of this may subject your code to any +// number of problems. + +namespace android { + +class Parcel; +struct RpcWireAddress; + +/** + * This class represents an identifier of a binder object. + * + * The purpose of this class it to hide the ABI of an RpcWireAddress, and + * potentially allow us to change the size of it in the future (RpcWireAddress + * is PIMPL, essentially - although the type that is used here is not exposed). + */ +class RpcAddress { +public: + /** + * The zero address is used for special RPC transactions, but it might also + * be used in conjunction with readFromParcel. + */ + static RpcAddress zero(); + + bool isZero() const; + + /** + * Create a new address which is unique + */ + static RpcAddress unique(); + + /** + * Creates a new address as a copy of an embedded object. + */ + static RpcAddress fromRawEmbedded(const RpcWireAddress* raw); + const RpcWireAddress& viewRawEmbedded() const; + + bool operator<(const RpcAddress& rhs) const; + std::string toString() const; + + status_t writeToParcel(Parcel* parcel) const; + status_t readFromParcel(const Parcel& parcel); + + ~RpcAddress(); + +private: + RpcAddress(); + + std::shared_ptr<RpcWireAddress> mRawAddr; +}; + +} // namespace android diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h new file mode 100644 index 0000000000..65c5232d46 --- /dev/null +++ b/libs/binder/include/binder/RpcConnection.h @@ -0,0 +1,158 @@ +/* + * 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. + */ +#pragma once + +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <binder/RpcAddress.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <optional> +#include <vector> + +// WARNING: This is a feature which is still in development, and it is subject +// to radical change. Any production use of this may subject your code to any +// number of problems. + +namespace android { + +class Parcel; +class RpcServer; +class RpcState; + +/** + * This represents a multi-threaded/multi-socket connection between a client + * and a server. + */ +class RpcConnection final : public virtual RefBase { +public: + static sp<RpcConnection> make(); + + /** + * This represents a connection for responses, e.g.: + * + * process A serves binder a + * process B opens a connection to process A + * process B makes binder b and sends it to A + * A uses this 'back connection' to send things back to B + * + * This should be called once, and then a call should be made to join per + * connection thread. + */ + [[nodiscard]] bool setupUnixDomainServer(const char* path); + + /** + * This should be called once per thread, matching 'join' in the remote + * process. + */ + [[nodiscard]] bool addUnixDomainClient(const char* path); + + /** + * Query the other side of the connection for the root object hosted by that + * process's RpcServer (if one exists) + */ + sp<IBinder> getRootObject(); + + [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + [[nodiscard]] status_t sendDecStrong(const RpcAddress& address); + + /** + * Adds a server thread accepting connections. Must be called after + * setup*Server. + */ + void join(); + + ~RpcConnection(); + + void setForServer(const wp<RpcServer>& server); + wp<RpcServer> server(); + + // internal only + const std::unique_ptr<RpcState>& state() { return mState; } + +private: + RpcConnection(); + + void addServer(base::unique_fd&& fd); + void addClient(base::unique_fd&& fd); + + struct ConnectionSocket : public RefBase { + base::unique_fd fd; + + // whether this or another thread is currently using this fd to make + // or receive transactions. + std::optional<pid_t> exclusiveTid; + }; + + enum class SocketUse { + CLIENT, + CLIENT_ASYNC, + CLIENT_REFCOUNT, + SERVER, + }; + + // RAII object for connection socket + class ExclusiveSocket { + public: + explicit ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use); + ~ExclusiveSocket(); + const base::unique_fd& fd() { return mSocket->fd; } + + private: + static void findSocket(pid_t tid, sp<ConnectionSocket>* exclusive, + sp<ConnectionSocket>* available, + std::vector<sp<ConnectionSocket>>& sockets, size_t socketsIndexHint); + + sp<RpcConnection> mConnection; // avoid deallocation + sp<ConnectionSocket> mSocket; + + // whether this is being used for a nested transaction (being on the same + // thread guarantees we won't write in the middle of a message, the way + // the wire protocol is constructed guarantees this is safe). + bool mReentrant = false; + }; + + // On the other side of a connection, for each of mClients here, there should + // be one of mServers on the other side (and vice versa). + // + // For the simplest connection, a single server with one client, you would + // have: + // - the server has a single 'mServers' and a thread listening on this + // - the client has a single 'mClients' and makes calls to this + // - here, when the client makes a call, the server can call back into it + // (nested calls), but outside of this, the client will only ever read + // calls from the server when it makes a call itself. + // + // For a more complicated case, the client might itself open up a thread to + // serve calls to the server at all times (e.g. if it hosts a callback) + + wp<RpcServer> mForServer; // maybe null, for client connections + + std::unique_ptr<RpcState> mState; + + base::unique_fd mServer; // socket we are accepting connections on + + std::mutex mSocketMutex; // for all below + std::condition_variable mSocketCv; // for mWaitingThreads + size_t mWaitingThreads = 0; + size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction + std::vector<sp<ConnectionSocket>> mClients; + std::vector<sp<ConnectionSocket>> mServers; +}; + +} // namespace android diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h new file mode 100644 index 0000000000..a2c2aee7b8 --- /dev/null +++ b/libs/binder/include/binder/RpcServer.h @@ -0,0 +1,84 @@ +/* + * 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. + */ +#pragma once + +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <binder/RpcConnection.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +// WARNING: This is a feature which is still in development, and it is subject +// to radical change. Any production use of this may subject your code to any +// number of problems. + +namespace android { + +/** + * This represents a server of an interface, which may be connected to by any + * number of clients over sockets. + * + * This object is not (currently) thread safe. All calls to it are expected to + * happen at process startup. + */ +class RpcServer final : public virtual RefBase { +public: + static sp<RpcServer> make(); + + void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); + + /** + * Setup a static connection, when the number of clients are known. + * + * Each call to this function corresponds to a different client, and clients + * each have their own threadpools. + * + * TODO(b/167966510): support dynamic creation of connections/threads + */ + sp<RpcConnection> addClientConnection(); + + /** + * Allowing a server to explicitly drop clients would be easy to add here, + * but it is not currently implemented, since users of this functionality + * could not use similar functionality if they are running under real + * binder. + */ + // void drop(const sp<RpcConnection>& connection); + + /** + * The root object can be retrieved by any client, without any + * authentication. + */ + void setRootObject(const sp<IBinder>& binder); + + /** + * Root object set with setRootObject + */ + sp<IBinder> getRootObject(); + + ~RpcServer(); + +private: + RpcServer(); + + bool mAgreedExperimental = false; + + sp<IBinder> mRootObject; + + std::vector<sp<RpcConnection>> mConnections; // per-client +}; + +} // namespace android diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h index 12272ba652..a09e587f04 100644 --- a/libs/binder/include/binder/Stability.h +++ b/libs/binder/include/binder/Stability.h @@ -49,10 +49,17 @@ namespace internal { // that it knows how to process. The summary of stability of a binder is // represented by a Stability::Category object. -// WARNING: These APIs are only ever expected to be called by auto-generated code. -// Instead of calling them, you should set the stability of a .aidl interface class Stability final { public: + // Given a binder interface at a certain stability, there may be some + // requirements associated with that higher stability level. For instance, a + // VINTF stability binder is required to be in the VINTF manifest. This API + // can be called to use that same interface within a partition. + static void forceDowngradeCompilationUnit(const sp<IBinder>& binder); + + // WARNING: Below APIs are only ever expected to be called by auto-generated code. + // Instead of calling them, you should set the stability of a .aidl interface + // WARNING: This is only ever expected to be called by auto-generated code. You likely want to // change or modify the stability class of the interface you are using. // This must be called as soon as the binder in question is constructed. No thread safety @@ -139,9 +146,14 @@ private: // returns the stability according to how this was built static Level getLocalLevel(); + enum { + REPR_NONE = 0, + REPR_LOG = 1, + REPR_ALLOW_DOWNGRADE = 2, + }; // applies stability to binder if stability level is known __attribute__((warn_unused_result)) - static status_t setRepr(IBinder* binder, int32_t representation, bool log); + static status_t setRepr(IBinder* binder, int32_t representation, uint32_t flags); // get stability information as encoded on the wire static Category getCategory(IBinder* binder); diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index a44c261b3b..8941e4996c 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -36,11 +36,6 @@ __BEGIN_DECLS -#ifndef __ANDROID_API__ -#error Android builds must be compiled against a specific API. If this is an \ - android platform host build, you must use libbinder_ndk_host_user. -#endif - typedef uint32_t binder_flags_t; enum { /** diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h index 05b25e7c40..b4dc08a49b 100644 --- a/libs/binder/ndk/include_ndk/android/binder_status.h +++ b/libs/binder/ndk/include_ndk/android/binder_status.h @@ -32,6 +32,16 @@ __BEGIN_DECLS +#ifndef __ANDROID_API__ +#error Android builds must be compiled against a specific API. If this is an \ + android platform host build, you must use libbinder_ndk_host_user. +#endif + +/** + * Low-level status types for use in binder. This is the least preferable way to + * return an error for binder services (where binder_exception_t should be used, + * particularly EX_SERVICE_SPECIFIC). + */ enum { STATUS_OK = 0, @@ -62,6 +72,10 @@ enum { */ typedef int32_t binder_status_t; +/** + * Top level exceptions types for Android binder errors, mapping to Java + * exceptions. Also see Parcel.java. + */ enum { EX_NONE = 0, EX_SECURITY = -1, @@ -170,7 +184,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificErrorWit /** * New status with binder_status_t. This is typically for low level failures when a binder_status_t * is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning - * an AStatus instance. + * an AStatus instance. This is the least preferable way to return errors. + * Prefer exceptions (particularly service-specific errors) when possible. * * Available since API level 29. * diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 5df0012bd3..55169140df 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -26,6 +26,9 @@ __BEGIN_DECLS * This registers the service with the default service manager under this instance name. This does * not take ownership of binder. * + * WARNING: when using this API across an APEX boundary, do not use with unstable + * AIDL services. TODO(b/139325195) + * * \param binder object to register globally with the service manager. * \param instance identifier of the service. This will be used to lookup the service. * @@ -39,6 +42,9 @@ __attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServic * service is not available This also implicitly calls AIBinder_incStrong (so the caller of this * function is responsible for calling AIBinder_decStrong). * + * WARNING: when using this API across an APEX boundary, do not use with unstable + * AIDL services. TODO(b/139325195) + * * \param instance identifier of the service used to lookup the service. */ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance); @@ -48,6 +54,9 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible * for calling AIBinder_decStrong). * + * WARNING: when using this API across an APEX boundary, do not use with unstable + * AIDL services. TODO(b/139325195) + * * \param instance identifier of the service used to lookup the service. */ __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance); @@ -78,6 +87,9 @@ binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible * for calling AIBinder_decStrong). * + * WARNING: when using this API across an APEX boundary, do not use with unstable + * AIDL services. TODO(b/139325195) + * * \param instance identifier of the service used to lookup the service. * * \return service if registered, null if not. diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h index d54c1a18ba..6372449716 100644 --- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h +++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h @@ -20,9 +20,7 @@ __BEGIN_DECLS -#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__) -#error this is only for platform code -#endif +#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__) /** * Gets whether or not FDs are allowed by this AParcel @@ -33,6 +31,9 @@ __BEGIN_DECLS */ bool AParcel_getAllowFds(const AParcel*); +#endif + +#if !defined(__ANDROID_APEX__) /** * Data written to the parcel will be zero'd before being deleted or realloced. * @@ -43,5 +44,6 @@ bool AParcel_getAllowFds(const AParcel*); * \param parcel The parcel to clear associated data from. */ void AParcel_markSensitive(const AParcel* parcel); +#endif __END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index 44ed48f839..ce7255e174 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -30,9 +30,7 @@ enum { FLAG_PRIVATE_VENDOR = 0x10000000, }; -// TODO(b/180646847): __ANDROID_APEX__ here is what allows product partition to -// talk to system. -#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) +#if defined(__ANDROID_VENDOR__) enum { FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR, @@ -47,7 +45,7 @@ static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { AIBinder_markVendorStability(binder); } -#else // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) +#else // defined(__ANDROID_VENDOR__) enum { FLAG_PRIVATE_LOCAL = 0, @@ -64,7 +62,7 @@ static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { AIBinder_markSystemStability(binder); } -#endif // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) +#endif // defined(__ANDROID_VENDOR__) /** * This interface has system<->vendor stability diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 8d08275eec..f1db653e07 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -117,6 +117,7 @@ LIBBINDER_NDK31 { # introduced=31 ABinderProcess_setupPolling; # apex AIBinder_getCallingSid; # apex AIBinder_setRequestingSid; # apex + AParcel_markSensitive; # llndk AServiceManager_isDeclared; # apex llndk AServiceManager_forEachDeclaredInstance; # apex llndk AServiceManager_registerLazyService; # llndk @@ -139,7 +140,6 @@ LIBBINDER_NDK31 { # introduced=31 LIBBINDER_NDK_PLATFORM { global: AParcel_getAllowFds; - AParcel_markSensitive; extern "C++" { AIBinder_fromPlatformBinder*; AIBinder_toPlatformBinder*; diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 3899d47bd6..321b422185 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -56,6 +56,26 @@ pub trait Interface: Send { } } +/// Interface stability promise +/// +/// An interface can promise to be a stable vendor interface ([`Vintf`]), or +/// makes no stability guarantees ([`Local`]). [`Local`] is +/// currently the default stability. +pub enum Stability { + /// Default stability, visible to other modules in the same compilation + /// context (e.g. modules on system.img) + Local, + + /// A Vendor Interface Object, which promises to be stable + Vintf, +} + +impl Default for Stability { + fn default() -> Self { + Stability::Local + } +} + /// A local service that can be remotable via Binder. /// /// An object that implement this interface made be made into a Binder service @@ -94,6 +114,8 @@ pub const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION; pub const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY; /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made. pub const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF; +/// Set to the vendor flag if we are building for the VNDK, 0 otherwise +pub const FLAG_PRIVATE_LOCAL: TransactionFlags = sys::FLAG_PRIVATE_LOCAL; /// Internal interface of binder local or remote objects for making /// transactions. @@ -602,6 +624,23 @@ macro_rules! declare_binder_interface { $interface[$descriptor] { native: $native($on_transact), proxy: $proxy {}, + stability: $crate::Stability::default(), + } + } + }; + + { + $interface:path[$descriptor:expr] { + native: $native:ident($on_transact:path), + proxy: $proxy:ident, + stability: $stability:expr, + } + } => { + $crate::declare_binder_interface! { + $interface[$descriptor] { + native: $native($on_transact), + proxy: $proxy {}, + stability: $stability, } } }; @@ -616,12 +655,33 @@ macro_rules! declare_binder_interface { } => { $crate::declare_binder_interface! { $interface[$descriptor] { + native: $native($on_transact), + proxy: $proxy { + $($fname: $fty = $finit),* + }, + stability: $crate::Stability::default(), + } + } + }; + + { + $interface:path[$descriptor:expr] { + native: $native:ident($on_transact:path), + proxy: $proxy:ident { + $($fname:ident: $fty:ty = $finit:expr),* + }, + stability: $stability:expr, + } + } => { + $crate::declare_binder_interface! { + $interface[$descriptor] { @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")] native: $native($on_transact), @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")] proxy: $proxy { $($fname: $fty = $finit),* }, + stability: $stability, } } }; @@ -635,6 +695,8 @@ macro_rules! declare_binder_interface { proxy: $proxy:ident { $($fname:ident: $fty:ty = $finit:expr),* }, + + stability: $stability:expr, } } => { #[doc = $proxy_doc] @@ -669,7 +731,7 @@ macro_rules! declare_binder_interface { impl $native { /// Create a new binder service. pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> { - let binder = $crate::Binder::new($native(Box::new(inner))); + let binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability); $crate::Strong::new(Box::new(binder)) } } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 5bbd2a3870..30928a5cff 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -107,8 +107,9 @@ use binder_ndk_sys as sys; pub mod parcel; pub use crate::binder::{ - FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable, Strong, - TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, + FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable, + Stability, Strong, TransactionCode, TransactionFlags, Weak, + FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION, }; pub use error::{status_t, ExceptionCode, Result, Status, StatusCode}; diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index 185645ef4a..3b3fd08cdc 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode}; +use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode}; use crate::error::{status_result, status_t, Result, StatusCode}; use crate::parcel::{Parcel, Serialize}; use crate::proxy::SpIBinder; @@ -49,11 +49,19 @@ pub struct Binder<T: Remotable> { unsafe impl<T: Remotable> Send for Binder<T> {} impl<T: Remotable> Binder<T> { - /// Create a new Binder remotable object. + /// Create a new Binder remotable object with default stability /// /// This moves the `rust_object` into an owned [`Box`] and Binder will /// manage its lifetime. pub fn new(rust_object: T) -> Binder<T> { + Self::new_with_stability(rust_object, Stability::default()) + } + + /// Create a new Binder remotable object with the given stability + /// + /// This moves the `rust_object` into an owned [`Box`] and Binder will + /// manage its lifetime. + pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> { let class = T::get_class(); let rust_object = Box::into_raw(Box::new(rust_object)); let ibinder = unsafe { @@ -65,10 +73,12 @@ impl<T: Remotable> Binder<T> { // ends. sys::AIBinder_new(class.into(), rust_object as *mut c_void) }; - Binder { + let mut binder = Binder { ibinder, rust_object, - } + }; + binder.mark_stability(stability); + binder } /// Set the extension of a binder interface. This allows a downstream @@ -161,6 +171,42 @@ impl<T: Remotable> Binder<T> { pub fn get_descriptor() -> &'static str { T::get_descriptor() } + + /// Mark this binder object with the given stability guarantee + fn mark_stability(&mut self, stability: Stability) { + match stability { + Stability::Local => self.mark_local_stability(), + Stability::Vintf => { + unsafe { + // Safety: Self always contains a valid `AIBinder` pointer, so + // we can always call this C API safely. + sys::AIBinder_markVintfStability(self.as_native_mut()); + } + } + } + } + + /// Mark this binder object with local stability, which is vendor if we are + /// building for the VNDK and system otherwise. + #[cfg(vendor_ndk)] + fn mark_local_stability(&mut self) { + unsafe { + // Safety: Self always contains a valid `AIBinder` pointer, so + // we can always call this C API safely. + sys::AIBinder_markVendorStability(self.as_native_mut()); + } + } + + /// Mark this binder object with local stability, which is vendor if we are + /// building for the VNDK and system otherwise. + #[cfg(not(vendor_ndk))] + fn mark_local_stability(&mut self) { + unsafe { + // Safety: Self always contains a valid `AIBinder` pointer, so + // we can always call this C API safely. + sys::AIBinder_markSystemStability(self.as_native_mut()); + } + } } impl<T: Remotable> Interface for Binder<T> { diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp index ef142b5cf8..65fa2ca905 100644 --- a/libs/binder/rust/sys/BinderBindings.hpp +++ b/libs/binder/rust/sys/BinderBindings.hpp @@ -21,6 +21,7 @@ #include <android/binder_parcel_platform.h> #include <android/binder_process.h> #include <android/binder_shell.h> +#include <android/binder_stability.h> #include <android/binder_status.h> namespace android { @@ -80,6 +81,7 @@ enum { enum { FLAG_ONEWAY = FLAG_ONEWAY, FLAG_CLEAR_BUF = FLAG_CLEAR_BUF, + FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_LOCAL, }; } // namespace consts diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 3bbb0b52bb..a44cddf761 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -105,6 +105,26 @@ cc_test { } cc_test { + name: "binderRpcTest", + defaults: ["binder_test_defaults"], + + srcs: [ + "IBinderRpcSession.aidl", + "IBinderRpcTest.aidl", + "binderRpcTest.cpp", + ], + shared_libs: [ + "libbinder", + "libbase", + "libutils", + "libcutils", + "liblog", + ], + test_suites: ["general-tests"], + require_root: true, +} + +cc_test { name: "binderThroughputTest", defaults: ["binder_test_defaults"], srcs: ["binderThroughputTest.cpp"], diff --git a/libs/binder/tests/IBinderRpcSession.aidl b/libs/binder/tests/IBinderRpcSession.aidl new file mode 100644 index 0000000000..cf5f318867 --- /dev/null +++ b/libs/binder/tests/IBinderRpcSession.aidl @@ -0,0 +1,19 @@ +/* + * 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. + */ + +interface IBinderRpcSession { + @utf8InCpp String getName(); +} diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl new file mode 100644 index 0000000000..2bdb26483f --- /dev/null +++ b/libs/binder/tests/IBinderRpcTest.aidl @@ -0,0 +1,58 @@ +/* + * 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. + */ + +interface IBinderRpcTest { + oneway void sendString(@utf8InCpp String str); + @utf8InCpp String doubleString(@utf8InCpp String str); + + // number of known RPC binders to process, RpcState::countBinders + int countBinders(); + + // Caller sends server, callee pings caller's server and returns error code. + int pingMe(IBinder binder); + @nullable IBinder repeatBinder(@nullable IBinder binder); + + void holdBinder(@nullable IBinder binder); + @nullable IBinder getHeldBinder(); + + // Idea is client creates its own instance of IBinderRpcTest and calls this, + // and the server calls 'binder' with (calls - 1) passing itself as 'binder', + // going back and forth until calls = 0 + void nestMe(IBinderRpcTest binder, int calls); + + // should always return the same binder + IBinder alwaysGiveMeTheSameBinder(); + + // Idea is that the server will not hold onto the session, the remote connection + // object must. This is to test lifetimes of binder objects, and consequently, also + // identity (since by assigning sessions names, we can make sure a section always + // references the session it was originally opened with). + IBinderRpcSession openSession(@utf8InCpp String name); + + // Decremented in ~IBinderRpcSession + int getNumOpenSessions(); + + // primitives to test threading behavior + void lock(); + oneway void unlockInMsAsync(int ms); + void lockUnlock(); // locks and unlocks a mutex + + // take up binder thread for some time + void sleepMs(int ms); + oneway void sleepMsAsync(int ms); + + void die(boolean cleanup); +} diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp new file mode 100644 index 0000000000..6fa53330ef --- /dev/null +++ b/libs/binder/tests/binderRpcTest.cpp @@ -0,0 +1,762 @@ +/* + * 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 <sys/prctl.h> +#include <unistd.h> + +#include <chrono> +#include <cstdlib> +#include <iostream> +#include <thread> + +#include <BnBinderRpcSession.h> +#include <BnBinderRpcTest.h> +#include <android-base/logging.h> +#include <binder/Binder.h> +#include <binder/BpBinder.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <binder/RpcConnection.h> +#include <binder/RpcServer.h> +#include <gtest/gtest.h> + +#include "../RpcState.h" // for debugging + +namespace android { + +using android::binder::Status; + +#define EXPECT_OK(status) \ + do { \ + Status stat = (status); \ + EXPECT_TRUE(stat.isOk()) << stat; \ + } while (false) + +class MyBinderRpcSession : public BnBinderRpcSession { +public: + static std::atomic<int32_t> gNum; + + MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; } + Status getName(std::string* name) override { + *name = mName; + return Status::ok(); + } + ~MyBinderRpcSession() { gNum--; } + +private: + std::string mName; +}; +std::atomic<int32_t> MyBinderRpcSession::gNum; + +class MyBinderRpcTest : public BnBinderRpcTest { +public: + sp<RpcConnection> connection; + + Status sendString(const std::string& str) override { + std::cout << "Child received string: " << str << std::endl; + return Status::ok(); + } + Status doubleString(const std::string& str, std::string* strstr) override { + std::cout << "Child received string to double: " << str << std::endl; + *strstr = str + str; + return Status::ok(); + } + Status countBinders(int32_t* out) override { + if (connection == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + *out = connection->state()->countBinders(); + if (*out != 1) { + connection->state()->dump(); + } + return Status::ok(); + } + Status pingMe(const sp<IBinder>& binder, int32_t* out) override { + if (binder == nullptr) { + std::cout << "Received null binder!" << std::endl; + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + *out = binder->pingBinder(); + return Status::ok(); + } + Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override { + *out = binder; + return Status::ok(); + } + static sp<IBinder> mHeldBinder; + Status holdBinder(const sp<IBinder>& binder) override { + mHeldBinder = binder; + return Status::ok(); + } + Status getHeldBinder(sp<IBinder>* held) override { + *held = mHeldBinder; + return Status::ok(); + } + Status nestMe(const sp<IBinderRpcTest>& binder, int count) override { + if (count <= 0) return Status::ok(); + return binder->nestMe(this, count - 1); + } + Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override { + static sp<IBinder> binder = new BBinder; + *out = binder; + return Status::ok(); + } + Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override { + *out = new MyBinderRpcSession(name); + return Status::ok(); + } + Status getNumOpenSessions(int32_t* out) override { + *out = MyBinderRpcSession::gNum; + return Status::ok(); + } + + std::mutex blockMutex; + Status lock() override { + blockMutex.lock(); + return Status::ok(); + } + Status unlockInMsAsync(int32_t ms) override { + usleep(ms * 1000); + blockMutex.unlock(); + return Status::ok(); + } + Status lockUnlock() override { + std::lock_guard<std::mutex> _l(blockMutex); + return Status::ok(); + } + + Status sleepMs(int32_t ms) override { + usleep(ms * 1000); + return Status::ok(); + } + + Status sleepMsAsync(int32_t ms) override { + // In-process binder calls are asynchronous, but the call to this method + // is synchronous wrt its client. This in/out-process threading model + // diffentiation is a classic binder leaky abstraction (for better or + // worse) and is preserved here the way binder sockets plugs itself + // into BpBinder, as nothing is changed at the higher levels + // (IInterface) which result in this behavior. + return sleepMs(ms); + } + + Status die(bool cleanup) override { + if (cleanup) { + exit(1); + } else { + _exit(1); + } + } +}; +sp<IBinder> MyBinderRpcTest::mHeldBinder; + +class Process { +public: + Process(const std::function<void()>& f) { + if (0 == (mPid = fork())) { + // racey: assume parent doesn't crash before this is set + prctl(PR_SET_PDEATHSIG, SIGHUP); + + f(); + } + } + ~Process() { + if (mPid != 0) { + kill(mPid, SIGKILL); + } + } + +private: + pid_t mPid = 0; +}; + +static std::string allocateSocketAddress() { + static size_t id = 0; + + return "/dev/binderRpcTest_" + std::to_string(id++); +}; + +struct ProcessConnection { + // reference to process hosting a socket server + Process host; + + // client connection object associated with other process + sp<RpcConnection> connection; + + // pre-fetched root object + sp<IBinder> rootBinder; + + // whether connection should be invalidated by end of run + bool expectInvalid = false; + + ~ProcessConnection() { + rootBinder = nullptr; + EXPECT_NE(nullptr, connection); + EXPECT_NE(nullptr, connection->state()); + EXPECT_EQ(0, connection->state()->countBinders()) << (connection->state()->dump(), "dump:"); + + wp<RpcConnection> weakConnection = connection; + connection = nullptr; + EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection"; + } +}; + +// This creates a new process serving an interface on a certain number of +// threads. +ProcessConnection createRpcTestSocketServerProcess( + size_t numThreads, + const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) { + CHECK_GT(numThreads, 0); + + std::string addr = allocateSocketAddress(); + unlink(addr.c_str()); + + auto ret = ProcessConnection{ + .host = Process([&] { + sp<RpcServer> server = RpcServer::make(); + + server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); + + // server supporting one client on one socket + sp<RpcConnection> connection = server->addClientConnection(); + CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr; + + configure(server, connection); + + // accept 'numThreads' connections + std::vector<std::thread> pool; + for (size_t i = 0; i + 1 < numThreads; i++) { + pool.push_back(std::thread([=] { connection->join(); })); + } + connection->join(); + for (auto& t : pool) t.join(); + }), + .connection = RpcConnection::make(), + }; + + // wait up to 1s for sockets to be created + constexpr useconds_t kMaxWaitUs = 1000000; + constexpr useconds_t kWaitDivision = 100; + for (size_t i = 0; i < kWaitDivision && 0 != access(addr.c_str(), F_OK); i++) { + usleep(kMaxWaitUs / kWaitDivision); + } + + // create remainder of connections + for (size_t i = 0; i < numThreads; i++) { + // Connection refused sometimes after file created but before listening. + CHECK(ret.connection->addUnixDomainClient(addr.c_str()) || + (usleep(10000), ret.connection->addUnixDomainClient(addr.c_str()))) + << i; + } + + ret.rootBinder = ret.connection->getRootObject(); + return ret; +} + +// Process connection where the process hosts IBinderRpcTest, the server used +// for most testing here +struct BinderRpcTestProcessConnection { + ProcessConnection proc; + + // pre-fetched root object + sp<IBinder> rootBinder; + + // pre-casted root object + sp<IBinderRpcTest> rootIface; + + ~BinderRpcTestProcessConnection() { + if (!proc.expectInvalid) { + int32_t remoteBinders = 0; + EXPECT_OK(rootIface->countBinders(&remoteBinders)); + // should only be the root binder object, iface + EXPECT_EQ(remoteBinders, 1); + } + + rootIface = nullptr; + rootBinder = nullptr; + } +}; + +BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) { + BinderRpcTestProcessConnection ret{ + .proc = createRpcTestSocketServerProcess(numThreads, + [&](const sp<RpcServer>& server, + const sp<RpcConnection>& connection) { + sp<MyBinderRpcTest> service = + new MyBinderRpcTest; + server->setRootObject(service); + service->connection = + connection; // for testing only + }), + }; + + ret.rootBinder = ret.proc.rootBinder; + ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); + + return ret; +} + +TEST(BinderRpc, RootObjectIsNull) { + auto proc = createRpcTestSocketServerProcess(1, + [](const sp<RpcServer>& server, + const sp<RpcConnection>&) { + // this is the default, but to be explicit + server->setRootObject(nullptr); + }); + + // retrieved by getRootObject when process is created above + EXPECT_EQ(nullptr, proc.rootBinder); + + // make sure we can retrieve it again (process doesn't crash) + EXPECT_EQ(nullptr, proc.connection->getRootObject()); +} + +TEST(BinderRpc, Ping) { + auto proc = createRpcTestSocketServerProcess(1); + ASSERT_NE(proc.rootBinder, nullptr); + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); +} + +TEST(BinderRpc, TransactionsMustBeMarkedRpc) { + auto proc = createRpcTestSocketServerProcess(1); + Parcel data; + Parcel reply; + EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0)); +} + +TEST(BinderRpc, UnknownTransaction) { + auto proc = createRpcTestSocketServerProcess(1); + Parcel data; + data.markForBinder(proc.rootBinder); + Parcel reply; + EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0)); +} + +TEST(BinderRpc, SendSomethingOneway) { + auto proc = createRpcTestSocketServerProcess(1); + EXPECT_OK(proc.rootIface->sendString("asdf")); +} + +TEST(BinderRpc, SendAndGetResultBack) { + auto proc = createRpcTestSocketServerProcess(1); + std::string doubled; + EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled)); + EXPECT_EQ("cool cool ", doubled); +} + +TEST(BinderRpc, SendAndGetResultBackBig) { + auto proc = createRpcTestSocketServerProcess(1); + std::string single = std::string(1024, 'a'); + std::string doubled; + EXPECT_OK(proc.rootIface->doubleString(single, &doubled)); + EXPECT_EQ(single + single, doubled); +} + +TEST(BinderRpc, CallMeBack) { + auto proc = createRpcTestSocketServerProcess(1); + + int32_t pingResult; + EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult)); + EXPECT_EQ(OK, pingResult); + + EXPECT_EQ(0, MyBinderRpcSession::gNum); +} + +TEST(BinderRpc, RepeatBinder) { + auto proc = createRpcTestSocketServerProcess(1); + + sp<IBinder> inBinder = new MyBinderRpcSession("foo"); + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); + EXPECT_EQ(inBinder, outBinder); + + wp<IBinder> weak = inBinder; + inBinder = nullptr; + outBinder = nullptr; + + // Force reading a reply, to process any pending dec refs from the other + // process (the other process will process dec refs there before processing + // the ping here). + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + EXPECT_EQ(nullptr, weak.promote()); + + EXPECT_EQ(0, MyBinderRpcSession::gNum); +} + +TEST(BinderRpc, RepeatTheirBinder) { + auto proc = createRpcTestSocketServerProcess(1); + + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); + + sp<IBinder> inBinder = IInterface::asBinder(session); + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder)); + EXPECT_EQ(inBinder, outBinder); + + wp<IBinder> weak = inBinder; + session = nullptr; + inBinder = nullptr; + outBinder = nullptr; + + // Force reading a reply, to process any pending dec refs from the other + // process (the other process will process dec refs there before processing + // the ping here). + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + EXPECT_EQ(nullptr, weak.promote()); +} + +TEST(BinderRpc, RepeatBinderNull) { + auto proc = createRpcTestSocketServerProcess(1); + + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder)); + EXPECT_EQ(nullptr, outBinder); +} + +TEST(BinderRpc, HoldBinder) { + auto proc = createRpcTestSocketServerProcess(1); + + IBinder* ptr = nullptr; + { + sp<IBinder> binder = new BBinder(); + ptr = binder.get(); + EXPECT_OK(proc.rootIface->holdBinder(binder)); + } + + sp<IBinder> held; + EXPECT_OK(proc.rootIface->getHeldBinder(&held)); + + EXPECT_EQ(held.get(), ptr); + + // stop holding binder, because we test to make sure references are cleaned + // up + EXPECT_OK(proc.rootIface->holdBinder(nullptr)); + // and flush ref counts + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); +} + +// START TESTS FOR LIMITATIONS OF SOCKET BINDER +// These are behavioral differences form regular binder, where certain usecases +// aren't supported. + +TEST(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) { + auto proc1 = createRpcTestSocketServerProcess(1); + auto proc2 = createRpcTestSocketServerProcess(1); + + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError()); +} + +TEST(BinderRpc, CannotSendRegularBinderOverSocketBinder) { + auto proc = createRpcTestSocketServerProcess(1); + + sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager()); + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError()); +} + +TEST(BinderRpc, CannotSendSocketBinderOverRegularBinder) { + auto proc = createRpcTestSocketServerProcess(1); + + // for historical reasons, IServiceManager interface only returns the + // exception code + EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED, + defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder)); +} + +// END TESTS FOR LIMITATIONS OF SOCKET BINDER + +TEST(BinderRpc, RepeatRootObject) { + auto proc = createRpcTestSocketServerProcess(1); + + sp<IBinder> outBinder; + EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder)); + EXPECT_EQ(proc.rootBinder, outBinder); +} + +TEST(BinderRpc, NestedTransactions) { + auto proc = createRpcTestSocketServerProcess(1); + + auto nastyNester = sp<MyBinderRpcTest>::make(); + EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10)); + + wp<IBinder> weak = nastyNester; + nastyNester = nullptr; + EXPECT_EQ(nullptr, weak.promote()); +} + +TEST(BinderRpc, SameBinderEquality) { + auto proc = createRpcTestSocketServerProcess(1); + + sp<IBinder> a; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); + + sp<IBinder> b; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); + + EXPECT_EQ(a, b); +} + +TEST(BinderRpc, SameBinderEqualityWeak) { + auto proc = createRpcTestSocketServerProcess(1); + + sp<IBinder> a; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); + wp<IBinder> weak = a; + a = nullptr; + + sp<IBinder> b; + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b)); + + // this is the wrong behavior, since BpBinder + // doesn't implement onIncStrongAttempted + // but make sure there is no crash + EXPECT_EQ(nullptr, weak.promote()); + + GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder."; + + // In order to fix this: + // - need to have incStrongAttempted reflected across IPC boundary (wait for + // response to promote - round trip...) + // - sendOnLastWeakRef, to delete entries out of RpcState table + EXPECT_EQ(b, weak.promote()); +} + +#define expectSessions(expected, iface) \ + do { \ + int session; \ + EXPECT_OK((iface)->getNumOpenSessions(&session)); \ + EXPECT_EQ(expected, session); \ + } while (false) + +TEST(BinderRpc, SingleSession) { + auto proc = createRpcTestSocketServerProcess(1); + + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession("aoeu", &session)); + std::string out; + EXPECT_OK(session->getName(&out)); + EXPECT_EQ("aoeu", out); + + expectSessions(1, proc.rootIface); + session = nullptr; + expectSessions(0, proc.rootIface); +} + +TEST(BinderRpc, ManySessions) { + auto proc = createRpcTestSocketServerProcess(1); + + std::vector<sp<IBinderRpcSession>> sessions; + + for (size_t i = 0; i < 15; i++) { + expectSessions(i, proc.rootIface); + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session)); + sessions.push_back(session); + } + expectSessions(sessions.size(), proc.rootIface); + for (size_t i = 0; i < sessions.size(); i++) { + std::string out; + EXPECT_OK(sessions.at(i)->getName(&out)); + EXPECT_EQ(std::to_string(i), out); + } + expectSessions(sessions.size(), proc.rootIface); + + while (!sessions.empty()) { + sessions.pop_back(); + expectSessions(sessions.size(), proc.rootIface); + } + expectSessions(0, proc.rootIface); +} + +size_t epochMillis() { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::seconds; + using std::chrono::system_clock; + return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); +} + +TEST(BinderRpc, ThreadPoolGreaterThanEqualRequested) { + constexpr size_t kNumThreads = 10; + + auto proc = createRpcTestSocketServerProcess(kNumThreads); + + EXPECT_OK(proc.rootIface->lock()); + + // block all but one thread taking locks + std::vector<std::thread> ts; + for (size_t i = 0; i < kNumThreads - 1; i++) { + ts.push_back(std::thread([&] { proc.rootIface->lockUnlock(); })); + } + + usleep(100000); // give chance for calls on other threads + + // other calls still work + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + constexpr size_t blockTimeMs = 500; + size_t epochMsBefore = epochMillis(); + // after this, we should never see a response within this time + EXPECT_OK(proc.rootIface->unlockInMsAsync(blockTimeMs)); + + // this call should be blocked for blockTimeMs + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + size_t epochMsAfter = epochMillis(); + EXPECT_GE(epochMsAfter, epochMsBefore + blockTimeMs) << epochMsBefore; + + for (auto& t : ts) t.join(); +} + +TEST(BinderRpc, ThreadPoolOverSaturated) { + constexpr size_t kNumThreads = 10; + constexpr size_t kNumCalls = kNumThreads + 3; + constexpr size_t kSleepMs = 500; + + auto proc = createRpcTestSocketServerProcess(kNumThreads); + + size_t epochMsBefore = epochMillis(); + + std::vector<std::thread> ts; + for (size_t i = 0; i < kNumCalls; i++) { + ts.push_back(std::thread([&] { proc.rootIface->sleepMs(kSleepMs); })); + } + + for (auto& t : ts) t.join(); + + size_t epochMsAfter = epochMillis(); + + EXPECT_GE(epochMsAfter, epochMsBefore + 2 * kSleepMs); + + // Potential flake, but make sure calls are handled in parallel. + EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs); +} + +TEST(BinderRpc, ThreadingStressTest) { + constexpr size_t kNumClientThreads = 10; + constexpr size_t kNumServerThreads = 10; + constexpr size_t kNumCalls = 100; + + auto proc = createRpcTestSocketServerProcess(kNumServerThreads); + + std::vector<std::thread> threads; + for (size_t i = 0; i < kNumClientThreads; i++) { + threads.push_back(std::thread([&] { + for (size_t j = 0; j < kNumCalls; j++) { + sp<IBinder> out; + proc.rootIface->repeatBinder(proc.rootBinder, &out); + EXPECT_EQ(proc.rootBinder, out); + } + })); + } + + for (auto& t : threads) t.join(); +} + +TEST(BinderRpc, OnewayCallDoesNotWait) { + constexpr size_t kReallyLongTimeMs = 100; + constexpr size_t kSleepMs = kReallyLongTimeMs * 5; + + // more than one thread, just so this doesn't deadlock + auto proc = createRpcTestSocketServerProcess(2); + + size_t epochMsBefore = epochMillis(); + + EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs)); + + size_t epochMsAfter = epochMillis(); + EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs); +} + +TEST(BinderRpc, OnewayCallQueueing) { + constexpr size_t kNumSleeps = 10; + constexpr size_t kNumExtraServerThreads = 4; + constexpr size_t kSleepMs = 50; + + // make sure calls to the same object happen on the same thread + auto proc = createRpcTestSocketServerProcess(1 + kNumExtraServerThreads); + + EXPECT_OK(proc.rootIface->lock()); + + for (size_t i = 0; i < kNumSleeps; i++) { + // these should be processed serially + proc.rootIface->sleepMsAsync(kSleepMs); + } + // should also be processesed serially + EXPECT_OK(proc.rootIface->unlockInMsAsync(kSleepMs)); + + size_t epochMsBefore = epochMillis(); + EXPECT_OK(proc.rootIface->lockUnlock()); + size_t epochMsAfter = epochMillis(); + + EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps); +} + +TEST(BinderRpc, Die) { + // TODO(b/183141167): handle this in library + signal(SIGPIPE, SIG_IGN); + + for (bool doDeathCleanup : {true, false}) { + auto proc = createRpcTestSocketServerProcess(1); + + // make sure there is some state during crash + // 1. we hold their binder + sp<IBinderRpcSession> session; + EXPECT_OK(proc.rootIface->openSession("happy", &session)); + // 2. they hold our binder + sp<IBinder> binder = new BBinder(); + EXPECT_OK(proc.rootIface->holdBinder(binder)); + + EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError()) + << "Do death cleanup: " << doDeathCleanup; + + proc.proc.expectInvalid = true; + } +} + +ssize_t countFds() { + DIR* dir = opendir("/proc/self/fd/"); + if (dir == nullptr) return -1; + ssize_t ret = 0; + dirent* ent; + while ((ent = readdir(dir)) != nullptr) ret++; + closedir(dir); + return ret; +} + +TEST(BinderRpc, Fds) { + ssize_t beforeFds = countFds(); + ASSERT_GE(beforeFds, 0); + { + auto proc = createRpcTestSocketServerProcess(10); + ASSERT_EQ(OK, proc.rootBinder->pingBinder()); + } + ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?"); +} + +extern "C" int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter); + return RUN_ALL_TESTS(); +} + +} // namespace android diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp index 1f2779abf0..42705401da 100644 --- a/libs/binder/tests/binderStabilityTest.cpp +++ b/libs/binder/tests/binderStabilityTest.cpp @@ -131,6 +131,17 @@ TEST(BinderStability, OnlyVintfStabilityBinderNeedsVintfDeclaration) { EXPECT_TRUE(Stability::requiresVintfDeclaration(BadStableBinder::vintf())); } +TEST(BinderStability, ForceDowngradeStability) { + sp<IBinder> someBinder = BadStableBinder::vintf(); + + EXPECT_TRUE(Stability::requiresVintfDeclaration(someBinder)); + + // silly to do this after already using the binder, but it's for the test + Stability::forceDowngradeCompilationUnit(someBinder); + + EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder)); +} + TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) { sp<IBinder> vintfServer = BadStableBinder::vintf(); diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 8da97bbbf9..1976b493b2 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -606,7 +606,9 @@ void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transacti // Apply the transaction since we have already acquired the desired frame. t->apply(); } else { - mPendingTransactions.emplace_back(frameNumber, std::move(*t)); + mPendingTransactions.emplace_back(frameNumber, *t); + // Clear the transaction so it can't be applied elsewhere. + t->clear(); } } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index ceab6ec542..97f8f4702c 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -477,6 +477,26 @@ public: return reply.readInt32(); } + virtual status_t overrideHdrTypes(const sp<IBinder>& display, + const std::vector<ui::Hdr>& hdrTypes) { + Parcel data, reply; + SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(data.writeStrongBinder, display); + + std::vector<int32_t> hdrTypesVector; + for (ui::Hdr i : hdrTypes) { + hdrTypesVector.push_back(static_cast<int32_t>(i)); + } + SAFE_PARCEL(data.writeInt32Vector, hdrTypesVector); + + status_t result = remote()->transact(BnSurfaceComposer::OVERRIDE_HDR_TYPES, data, &reply); + if (result != NO_ERROR) { + ALOGE("overrideHdrTypes failed to transact: %d", result); + return result; + } + return result; + } + status_t enableVSyncInjections(bool enable) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -1963,6 +1983,20 @@ status_t BnSurfaceComposer::onTransact( SAFE_PARCEL(reply->writeInt32, extraBuffers); return NO_ERROR; } + case OVERRIDE_HDR_TYPES: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> display = nullptr; + SAFE_PARCEL(data.readStrongBinder, &display); + + std::vector<int32_t> hdrTypes; + SAFE_PARCEL(data.readInt32Vector, &hdrTypes); + + std::vector<ui::Hdr> hdrTypesVector; + for (int i : hdrTypes) { + hdrTypesVector.push_back(static_cast<ui::Hdr>(i)); + } + return overrideHdrTypes(display, hdrTypesVector); + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index d7b2c2eea1..0b01084633 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1985,6 +1985,11 @@ status_t SurfaceComposerClient::getAnimationFrameStats(FrameStats* outStats) { return ComposerService::getComposerService()->getAnimationFrameStats(outStats); } +status_t SurfaceComposerClient::overrideHdrTypes(const sp<IBinder>& display, + const std::vector<ui::Hdr>& hdrTypes) { + return ComposerService::getComposerService()->overrideHdrTypes(display, hdrTypes); +} + status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 35990d171a..9f9ca74d65 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -267,6 +267,13 @@ public: */ virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0; + /* Overrides the supported HDR modes for the given display device. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t overrideHdrTypes(const sp<IBinder>& display, + const std::vector<ui::Hdr>& hdrTypes) = 0; + virtual status_t enableVSyncInjections(bool enable) = 0; virtual status_t injectVSync(nsecs_t when) = 0; @@ -570,6 +577,7 @@ public: GET_DYNAMIC_DISPLAY_INFO, ADD_FPS_LISTENER, REMOVE_FPS_LISTENER, + OVERRIDE_HDR_TYPES, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index a643e9eb7e..c38375cf77 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -572,6 +572,9 @@ public: static status_t clearAnimationFrameStats(); static status_t getAnimationFrameStats(FrameStats* outStats); + static status_t overrideHdrTypes(const sp<IBinder>& display, + const std::vector<ui::Hdr>& hdrTypes); + static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation, const Rect& layerStackRect, const Rect& displayRect); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 8d7f8c97f4..5ac3f19486 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -772,6 +772,10 @@ public: status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { return NO_ERROR; } + status_t overrideHdrTypes(const sp<IBinder>& /*display*/, + const std::vector<ui::Hdr>& /*hdrTypes*/) override { + return NO_ERROR; + } status_t enableVSyncInjections(bool /*enable*/) override { return NO_ERROR; } diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index a375d43a43..79b47a1ebc 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -51,13 +51,13 @@ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]")); status_t err = gbuffer->initCheck(); - if (err != 0 || gbuffer->handle == 0) { + if (err != 0 || gbuffer->handle == nullptr) { if (err == NO_MEMORY) { GraphicBuffer::dumpAllocationsToSystemLog(); } ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p", desc->width, desc->height, desc->layers, strerror(-err), gbuffer->handle); - return err; + return err == 0 ? UNKNOWN_ERROR : err; } *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index c535597aea..ae3d88a9fb 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -28,19 +28,19 @@ namespace android { namespace renderengine { namespace skia { -AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, - bool isRender) { +AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer) { ATRACE_CALL(); AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); - bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); + const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); + const bool isRenderable = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER); GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false); mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height, &mDeleteProc, &mUpdateProc, &mImageCtx, createProtectedImage, backendFormat, - isRender); + isRenderable); mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); } diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h index bb758780e1..a6f73db3dc 100644 --- a/libs/renderengine/skia/AutoBackendTexture.h +++ b/libs/renderengine/skia/AutoBackendTexture.h @@ -69,7 +69,7 @@ public: }; // Creates a GrBackendTexture whose contents come from the provided buffer. - AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isRender); + AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer); void ref() { mUsageCount++; } diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index f8dd7f52e0..69c6a09076 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -28,8 +28,11 @@ namespace android::renderengine::skia { -static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display, - sp<GraphicBuffer> dstBuffer) { +// Warming shader cache, not framebuffer cache. +constexpr bool kUseFrameBufferCache = false; + +static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, + sp<GraphicBuffer> dstBuffer) { // Somewhat arbitrary dimensions, but on screen and slightly shorter, based // on actual use. FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30); @@ -39,6 +42,7 @@ static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySetting .boundaries = rect, .roundedCornersCrop = rect, }, + // drawShadow ignores alpha .shadow = ShadowSettings{ .ambientColor = vec4(0, 0, 0, 0.00935997f), @@ -61,9 +65,8 @@ static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySetting 0.f, 0.f, 1.f, 0.f, 167.355743f, 1852.257812f, 0.f, 1.f) }) { layer.geometry.positionTransform = transform; - renderengine->drawLayers(display, layers, dstBuffer, false /* useFrameBufferCache*/, + renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache, base::unique_fd(), nullptr); - renderengine->assertShadersCompiled(identity ? 1 : 2); identity = false; } } @@ -76,14 +79,6 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting .geometry = Geometry{ .boundaries = rect, - // This matrix is based on actual data seen when opening the dialer. - // What seems to be important in matching the actual use cases are: - // - it is not identity - // - the layer will be drawn (not clipped out etc) - .positionTransform = mat4(.19f, .0f, .0f, .0f, - .0f, .19f, .0f, .0f, - .0f, .0f, 1.f, .0f, - 169.f, 1527.f, .0f, 1.f), .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = @@ -94,27 +89,60 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting }}, }; + // This matrix is based on actual data seen when opening the dialer. + // translate and scale creates new shaders when combined with rounded corners + // clang-format off + auto scale_and_translate = mat4(.19f, .0f, .0f, .0f, + .0f, .19f, .0f, .0f, + .0f, .0f, 1.f, .0f, + 169.f, 1527.f, .0f, 1.f); + // clang-format on + // Test both drawRect and drawRRect auto layers = std::vector<const LayerSettings*>{&layer}; - for (float roundedCornersRadius : {0.0f, 500.f}) { - layer.geometry.roundedCornersRadius = roundedCornersRadius; - // No need to check UNKNOWN, which is treated as SRGB. - for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) { - layer.sourceDataspace = dataspace; - for (bool isOpaque : {true, false}) { - layer.source.buffer.isOpaque = isOpaque; - for (auto alpha : {half(.23999f), half(1.0f)}) { - layer.alpha = alpha; - renderengine->drawLayers(display, layers, dstBuffer, - false /* useFrameBufferCache*/, base::unique_fd(), - nullptr); - renderengine->assertShadersCompiled(1); + for (auto transform : {mat4(), scale_and_translate}) { + layer.geometry.positionTransform = transform; + // fractional corner radius creates a shader that is used during home button swipe + for (float roundedCornersRadius : {0.0f, 0.05f, 500.f}) { + // roundedCornersCrop is always set, but it is this radius that triggers the behavior + layer.geometry.roundedCornersRadius = roundedCornersRadius; + // No need to check UNKNOWN, which is treated as SRGB. + for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) { + layer.sourceDataspace = dataspace; + for (bool isOpaque : {true, false}) { + layer.source.buffer.isOpaque = isOpaque; + for (auto alpha : {half(.23999f), half(1.0f)}) { + layer.alpha = alpha; + renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache, + base::unique_fd(), nullptr); + } } } } } } +static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, + sp<GraphicBuffer> dstBuffer) { + const Rect& displayRect = display.physicalDisplay; + FloatRect rect(0, 0, displayRect.width(), displayRect.height()); + LayerSettings layer{ + .geometry = + Geometry{ + .boundaries = rect, + }, + .alpha = 1, + .source = + PixelSource{ + .solidColor = half3(0.1f, 0.2f, 0.3f), + }, + }; + + auto layers = std::vector<const LayerSettings*>{&layer}; + renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache, base::unique_fd(), + nullptr); +} + void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { const nsecs_t timeBefore = systemTime(); // The dimensions should not matter, so long as we draw inside them. @@ -133,14 +161,13 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { usage, "primeShaderCache_dst"); // This buffer will be the source for the call to drawImageLayers. Draw // something to it as a placeholder for what an app draws. We should draw - // something, but the details are not important. We only need one version of - // the shadow's SkSL, so draw it here, giving us both a placeholder image - // and a chance to compile the shadow's SkSL. + // something, but the details are not important. Make use of the shadow layer drawing step + // to populate it. sp<GraphicBuffer> srcBuffer = new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1, usage, "drawImageLayer_src"); - drawShadowLayer(renderengine, display, srcBuffer); - + drawSolidLayers(renderengine, display, dstBuffer); + drawShadowLayers(renderengine, display, srcBuffer); drawImageLayers(renderengine, display, dstBuffer, srcBuffer); const nsecs_t timeAfter = systemTime(); const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 7d711c19c6..c7001b9851 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -479,18 +479,31 @@ void SkiaGLRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buf } ATRACE_CALL(); + // We need to switch the currently bound context if the buffer is protected but the current + // context is not. The current state must then be restored after the buffer is cached. + const bool protectedContextState = mInProtectedContext; + if (!useProtectedContext(protectedContextState || + (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) { + ALOGE("Attempting to cache a buffer into a different context than what is currently bound"); + return; + } + + auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext; + auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache; + std::lock_guard<std::mutex> lock(mRenderingMutex); - auto iter = mTextureCache.find(buffer->getId()); - if (iter != mTextureCache.end()) { + auto iter = cache.find(buffer->getId()); + if (iter != cache.end()) { ALOGV("Texture already exists in cache."); - return; } else { std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(); imageTextureRef->setTexture( - new AutoBackendTexture(mGrContext.get(), buffer->toAHardwareBuffer(), false)); - mTextureCache.insert({buffer->getId(), imageTextureRef}); + new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer())); + cache.insert({buffer->getId(), imageTextureRef}); } + // restore the original state of the protected context if necessary + useProtectedContext(protectedContextState); } void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { @@ -640,7 +653,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, ATRACE_NAME("Cache miss"); surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(); surfaceTextureRef->setTexture( - new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), true)); + new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer())); if (useFramebufferCache) { ALOGD("Adding to cache"); cache.insert({buffer->getId(), surfaceTextureRef}); @@ -842,15 +855,14 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, validateInputBufferUsage(layer->source.buffer.buffer); const auto& item = layer->source.buffer; std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr; - auto iter = mTextureCache.find(item.buffer->getId()); - if (iter != mTextureCache.end()) { + auto iter = cache.find(item.buffer->getId()); + if (iter != cache.end()) { imageTextureRef = iter->second; } else { imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(); - imageTextureRef->setTexture(new AutoBackendTexture(grContext.get(), - item.buffer->toAHardwareBuffer(), - false)); - mTextureCache.insert({item.buffer->getId(), imageTextureRef}); + imageTextureRef->setTexture( + new AutoBackendTexture(grContext.get(), item.buffer->toAHardwareBuffer())); + cache.insert({item.buffer->getId(), imageTextureRef}); } sk_sp<SkImage> image = diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 393f649746..9750ef9833 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -42,6 +42,7 @@ filegroup { "InputTarget.cpp", "Monitor.cpp", "TouchState.cpp", + "DragState.cpp", ], } diff --git a/services/inputflinger/dispatcher/DragState.cpp b/services/inputflinger/dispatcher/DragState.cpp new file mode 100644 index 0000000000..2e2df43009 --- /dev/null +++ b/services/inputflinger/dispatcher/DragState.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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 "DragState.h" +#include <android-base/stringprintf.h> +#include <input/InputWindow.h> + +using android::InputWindowHandle; +using android::base::StringPrintf; + +namespace android::inputdispatcher { + +void DragState::dump(std::string& dump, const char* prefix) { + dump += prefix + StringPrintf("Drag Window: %s\n", dragWindow->getName().c_str()); + if (dragHoverWindowHandle) { + dump += prefix + + StringPrintf("Drag Hover Window: %s\n", dragHoverWindowHandle->getName().c_str()); + } + dump += prefix + StringPrintf("isStartDrag: %s\n", isStartDrag ? "true" : "false"); + dump += prefix + + StringPrintf("isStylusButtonDownAtStart: %s\n", + isStylusButtonDownAtStart ? "true" : "false"); +} + +} // namespace android::inputdispatcher
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h new file mode 100644 index 0000000000..06453d8eee --- /dev/null +++ b/services/inputflinger/dispatcher/DragState.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 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_DRAGSTATE_H +#define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H + +#include <utils/RefBase.h> +#include <string> + +namespace android { + +class InputWindowHandle; + +namespace inputdispatcher { +struct DragState { + DragState(const sp<android::InputWindowHandle>& windowHandle) : dragWindow(windowHandle) {} + void dump(std::string& dump, const char* prefix = ""); + + // The window being dragged. + const sp<InputWindowHandle> dragWindow; + // The last drag hover window which could receive the drag event. + sp<InputWindowHandle> dragHoverWindowHandle; + // Indicates the if received first event to check for button state. + bool isStartDrag = false; + // Indicate if the stylus button is down at the start of the drag. + bool isStylusButtonDownAtStart = false; +}; + +} // namespace inputdispatcher +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index de7acf9f30..073455a7ff 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -961,7 +961,7 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display // Traverse windows from front to back to find touched window. const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& windowHandle : windowHandles) { - if (ignoreDragWindow && haveSameToken(windowHandle, touchState->dragWindow)) { + if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) { continue; } const InputWindowInfo* windowInfo = windowHandle->getInfo(); @@ -2061,7 +2061,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( goto Failed; } - addDragEventLocked(entry, tempTouchState); + addDragEventLocked(entry); // Check whether touches should slip outside of the current foreground window. if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 && @@ -2318,39 +2318,61 @@ Failed: return injectionResult; } -void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) { - if (entry.pointerCount != 1 || !state.dragWindow) { +void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) { + const sp<InputWindowHandle> dropWindow = + findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, + false /*addOutsideTargets*/, false /*addPortalWindows*/, + true /*ignoreDragWindow*/); + if (dropWindow) { + vec2 local = dropWindow->getInfo()->transform.transform(x, y); + notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y); + } + mDragState.reset(); +} + +void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { + if (entry.pointerCount != 1 || !mDragState) { return; } + if (!mDragState->isStartDrag) { + mDragState->isStartDrag = true; + mDragState->isStylusButtonDownAtStart = + (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0; + } + int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK; int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); if (maskedAction == AMOTION_EVENT_ACTION_MOVE) { + // Handle the special case : stylus button no longer pressed. + bool isStylusButtonDown = (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0; + if (mDragState->isStylusButtonDownAtStart && !isStylusButtonDown) { + finishDragAndDrop(entry.displayId, x, y); + return; + } + const sp<InputWindowHandle> hoverWindowHandle = - findTouchedWindowAtLocked(entry.displayId, x, y, &state, + findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, false /*addOutsideTargets*/, false /*addPortalWindows*/, true /*ignoreDragWindow*/); // enqueue drag exit if needed. - if (hoverWindowHandle != state.dragHoverWindowHandle && - !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) { - if (state.dragHoverWindowHandle != nullptr) { - enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry); + if (hoverWindowHandle != mDragState->dragHoverWindowHandle && + !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) { + if (mDragState->dragHoverWindowHandle != nullptr) { + enqueueDragEventLocked(mDragState->dragHoverWindowHandle, true /*isExiting*/, + entry); } - state.dragHoverWindowHandle = hoverWindowHandle; + mDragState->dragHoverWindowHandle = hoverWindowHandle; } // enqueue drag location if needed. if (hoverWindowHandle != nullptr) { enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry); } - } else if (maskedAction == AMOTION_EVENT_ACTION_UP || - maskedAction == AMOTION_EVENT_ACTION_CANCEL) { - if (state.dragHoverWindowHandle && maskedAction == AMOTION_EVENT_ACTION_UP) { - vec2 local = state.dragHoverWindowHandle->getInfo()->transform.transform(x, y); - notifyDropWindowLocked(state.dragHoverWindowHandle->getToken(), local.x, local.y); - } - state.dragWindow = nullptr; - state.dragHoverWindowHandle = nullptr; + } else if (maskedAction == AMOTION_EVENT_ACTION_UP) { + finishDragAndDrop(entry.displayId, x, y); + } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + mDragState.reset(); } } @@ -4451,13 +4473,12 @@ void InputDispatcher::setInputWindowsLocked( } } - // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we + // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We // could just clear the state here. - if (state.dragWindow && - std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) == + if (mDragState && + std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) == windowHandles.end()) { - state.dragWindow = nullptr; - state.dragHoverWindowHandle = nullptr; + mDragState.reset(); } } @@ -4696,7 +4717,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Store the dragging window. if (isDragDrop) { - state.dragWindow = toWindowHandle; + mDragState = std::make_unique<DragState>(toWindowHandle); } found = true; @@ -4839,6 +4860,11 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "TouchStates: <no displays touched>\n"; } + if (mDragState) { + dump += StringPrintf(INDENT "DragState:\n"); + mDragState->dump(dump, INDENT2); + } + if (!mWindowHandlesByDisplay.empty()) { for (auto& it : mWindowHandlesByDisplay) { const std::vector<sp<InputWindowHandle>> windowHandles = it.second; diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 593ec23e23..5708face4b 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -19,6 +19,7 @@ #include "AnrTracker.h" #include "CancelationOptions.h" +#include "DragState.h" #include "Entry.h" #include "FocusResolver.h" #include "InjectionState.h" @@ -340,6 +341,7 @@ private: REQUIRES(mLock); std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock); + std::unique_ptr<DragState> mDragState GUARDED_BY(mLock); // Focused applications. std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>> @@ -499,7 +501,8 @@ private: const InjectionState* injectionState); // Enqueue a drag event if needed, and update the touch state. // Uses findTouchedWindowTargetsLocked to make the decision - void addDragEventLocked(const MotionEntry& entry, TouchState& state) REQUIRES(mLock); + void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock); + void finishDragAndDrop(int32_t displayId, float x, float y) REQUIRES(mLock); struct TouchOcclusionInfo { bool hasBlockingOcclusion; diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 4165f4934c..81b3cf025b 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -49,8 +49,6 @@ void TouchState::copyFrom(const TouchState& other) { windows = other.windows; portalWindows = other.portalWindows; gestureMonitors = other.gestureMonitors; - dragHoverWindowHandle = other.dragHoverWindowHandle; - dragWindow = other.dragWindow; } void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index d7a561c5ae..623c6a824f 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -41,11 +41,6 @@ struct TouchState { std::vector<TouchedMonitor> gestureMonitors; - // The last drag hover window which could receive the drag event. - sp<InputWindowHandle> dragHoverWindowHandle; - // The window being dragged. - sp<InputWindowHandle> dragWindow; - TouchState(); ~TouchState(); void reset(); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index d1bd38e1b7..b62fce4a1c 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -386,7 +386,9 @@ private: void pokeUserActivity(nsecs_t, int32_t, int32_t) override {} - bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; } + bool checkInjectEventsPermissionNonReentrant(int32_t pid, int32_t uid) override { + return pid == INJECTOR_PID && uid == INJECTOR_UID; + } void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override { std::scoped_lock lock(mLock); @@ -1100,7 +1102,8 @@ static InputEventInjectionResult injectKey( const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount, int32_t displayId = ADISPLAY_ID_NONE, InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT, - std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) { + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, + bool allowKeyRepeat = true) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); @@ -1109,10 +1112,13 @@ static InputEventInjectionResult injectKey( INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, repeatCount, currentTime, currentTime); + int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER; + if (!allowKeyRepeat) { + policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; + } // Inject event until dispatch out. return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode, - injectionTimeout, - POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + injectionTimeout, policyFlags); } static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher, @@ -1120,6 +1126,16 @@ static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispat return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); } +// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without +// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it +// has to be woken up to process the repeating key. +static InputEventInjectionResult injectKeyDownNoRepeat(const sp<InputDispatcher>& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId, + InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT, + /* allowKeyRepeat */ false); +} + static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId); @@ -1175,8 +1191,8 @@ public: return *this; } - MotionEventBuilder& buttonState(int32_t actionButton) { - mActionButton = actionButton; + MotionEventBuilder& buttonState(int32_t buttonState) { + mButtonState = buttonState; return *this; } @@ -1264,7 +1280,7 @@ static InputEventInjectionResult injectMotionEvent( .build(); // Inject event until dispatch out. - return injectMotionEvent(dispatcher, event); + return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode); } static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher, @@ -2774,13 +2790,14 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) { // Test inject a key down with display id specified. - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT); windowInSecondary->assertNoEvents(); // Test inject a key down without display id specified. - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->assertNoEvents(); windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); @@ -2793,7 +2810,7 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) AKEY_EVENT_FLAG_CANCELED); // Test inject a key down, should timeout because of no target window. - ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher)) + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDownNoRepeat(mDispatcher)) << "Inject key event should return InputEventInjectionResult::TIMED_OUT"; windowInPrimary->assertNoEvents(); windowInSecondary->consumeFocusEvent(false); @@ -3015,7 +3032,8 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPo // Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't // have focus. Ensure no window received the onPointerDownOutsideFocus callback. TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) { - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); @@ -3296,7 +3314,7 @@ TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) { // Send a regular key and respond, which should not cause an ANR. TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) { - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher)); mWindow->consumeKeyDown(ADISPLAY_ID_NONE); ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertNotifyAnrWasNotCalled(); @@ -3309,7 +3327,8 @@ TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) { InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, - InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/); + InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/, + false /* allowKeyRepeat */); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); // Key will not go to window because we have no focused window. // The 'no focused window' ANR timer should start instead. @@ -3345,7 +3364,7 @@ TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { // Send a key to the app and have the app not respond right away. TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { // Inject a key, and don't respond - expect that ANR is called. - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher)); std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); @@ -3372,7 +3391,7 @@ TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) { // injection times out (instead of failing). const InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, - InputEventInjectionSync::WAIT_FOR_RESULT, 10ms); + InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, false /* allowKeyRepeat */); ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication); @@ -3391,7 +3410,7 @@ TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) // injection times out (instead of failing). const InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, - InputEventInjectionSync::WAIT_FOR_RESULT, 10ms); + InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, false /* allowKeyRepeat */); ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); const std::chrono::duration appTimeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); @@ -3980,7 +3999,8 @@ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_ // Key will not be sent anywhere because we have no focused window. It will remain pending. InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, - InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/); + InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/, + false /* allowKeyRepeat */); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); // Wait until dispatcher starts the "no focused window" timer. If we don't wait here, @@ -4786,6 +4806,32 @@ protected: mWindow->consumeMotionCancel(); mDragWindow->consumeMotionDown(); } + + // Start performing drag, we will create a drag window and transfer touch to it. + void performStylusDrag() { + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_STYLUS) + .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) + .pointer(PointerBuilder(0, + AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(50) + .y(50)) + .build())); + mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + // The drag window covers the entire display + mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}}); + + // Transfer touch focus to the drag window + mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(), + true /* isDragDrop */); + mWindow->consumeMotionCancel(); + mDragWindow->consumeMotionDown(); + } }; TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { @@ -4858,4 +4904,51 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) { mSecondWindow->assertNoEvents(); } +TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { + performStylusDrag(); + + // Move on window and keep button pressed. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS) + .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(50) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->consumeDragEvent(false, 50, 50); + mSecondWindow->assertNoEvents(); + + // Move to another window and release button, expect to drop item. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS) + .buttonState(0) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(150) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken()); + + // nothing to the window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS) + .buttonState(0) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(150) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); +} + } // namespace android::inputdispatcher diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index b3f97924bd..be9bce0795 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -185,10 +185,14 @@ std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareCli return std::nullopt; } } - bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) || + const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) || (isSecure() && !targetSettings.isSecure); + const bool bufferCanBeUsedAsHwTexture = + mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE; compositionengine::LayerFE::LayerSettings& layer = *result; - if (blackOutLayer) { + if (blackOutLayer || !bufferCanBeUsedAsHwTexture) { + ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable", + mName.c_str()); prepareClearClientComposition(layer, true /* blackout */); return layer; } @@ -325,6 +329,37 @@ bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) { mRefreshPending = false; return hasReadyFrame(); } +namespace { +TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) { + using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility; + using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness; + const auto frameRateCompatibility = [frameRate] { + switch (frameRate.type) { + case Layer::FrameRateCompatibility::Default: + return FrameRateCompatibility::Default; + case Layer::FrameRateCompatibility::ExactOrMultiple: + return FrameRateCompatibility::ExactOrMultiple; + default: + return FrameRateCompatibility::Undefined; + } + }(); + + const auto seamlessness = [frameRate] { + switch (frameRate.seamlessness) { + case scheduler::Seamlessness::OnlySeamless: + return Seamlessness::ShouldBeSeamless; + case scheduler::Seamlessness::SeamedAndSeamless: + return Seamlessness::NotRequired; + default: + return Seamlessness::Undefined; + } + }(); + + return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(), + .frameRateCompatibility = frameRateCompatibility, + .seamlessness = seamlessness}; +} +} // namespace bool BufferLayer::onPostComposition(const DisplayDevice* display, const std::shared_ptr<FenceTime>& glDoneFence, @@ -355,6 +390,13 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, clientCompositionTimestamp, FrameTracer::FrameEvent::FALLBACK_COMPOSITION); + // Update the SurfaceFrames in the drawing state + if (mDrawingState.bufferSurfaceFrameTX) { + mDrawingState.bufferSurfaceFrameTX->setGpuComposition(); + } + for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) { + surfaceFrame->setGpuComposition(); + } } std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime; @@ -370,7 +412,9 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); if (presentFence->isValid()) { mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, - refreshRate, renderRate); + refreshRate, renderRate, + frameRateToSetFrameRateVotePayload( + mDrawingState.frameRate)); mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); @@ -382,7 +426,9 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, // timestamp instead. const nsecs_t actualPresentTime = display->getRefreshTimestamp(); mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, - refreshRate, renderRate); + refreshRate, renderRate, + frameRateToSetFrameRateVotePayload( + mDrawingState.frameRate)); mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, actualPresentTime, FrameTracer::FrameEvent::PRESENT_FENCE); diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 4cd9400769..a974dc4488 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -692,7 +692,6 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse // are processing the next state. addSurfaceFramePresentedForBuffer(bufferSurfaceFrame, mDrawingState.acquireFence->getSignalTime(), latchTime); - bufferSurfaceFrame.reset(); } mCurrentStateModified = false; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index b0e42b7692..a56f28aec0 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -70,7 +70,7 @@ public: size_t getCreationCost() const; size_t getDisplayCost() const; - bool hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const; + bool hasBufferUpdate() const; bool hasReadyBuffer() const; // Decomposes this CachedSet into a vector of its layers as individual CachedSets diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index 5b9a9f0bda..2bd3249880 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -45,8 +45,6 @@ public: // Renders the newest cached sets with the supplied output dataspace void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace); - void reset(); - void dump(std::string& result) const; private: diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index 137697b59d..4d3036a686 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -116,12 +116,11 @@ size_t CachedSet::getDisplayCost() const { return static_cast<size_t>(mBounds.width() * mBounds.height()); } -bool CachedSet::hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const { +bool CachedSet::hasBufferUpdate() const { for (const Layer& layer : mLayers) { if (layer.getFramesSinceBufferUpdate() == 0) { return true; } - ++layers; } return false; } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 30b5761a93..60ebbb2c65 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -60,18 +60,6 @@ void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine, mNewCachedSet->render(renderEngine, outputDataspace); } -void Flattener::reset() { - resetActivities(0, std::chrono::steady_clock::now()); - - mUnflattenedDisplayCost = 0; - mFlattenedDisplayCost = 0; - mInitialLayerCounts.clear(); - mFinalLayerCounts.clear(); - mCachedSetCreationCount = 0; - mCachedSetCreationCost = 0; - mInvalidatedCachedSetAges.clear(); -} - void Flattener::dump(std::string& result) const { const auto now = std::chrono::steady_clock::now(); @@ -208,7 +196,7 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash(LayerStateField::Buffer)) { - if (mNewCachedSet->hasBufferUpdate(incomingLayerIter)) { + if (mNewCachedSet->hasBufferUpdate()) { ALOGV("[%s] Dropping new cached set", __func__); ++mInvalidatedCachedSetAges[0]; mNewCachedSet = std::nullopt; @@ -242,7 +230,7 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers } } - if (!currentLayerIter->hasBufferUpdate(incomingLayerIter)) { + if (!currentLayerIter->hasBufferUpdate()) { currentLayerIter->incrementAge(); merged.emplace_back(*currentLayerIter); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index 377f817ae8..7842efbe00 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -201,13 +201,7 @@ TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) { cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); cachedSet.addLayer(layer3.getState(), kStartTime + 20ms); - std::vector<const LayerState*> incomingLayers = { - layer1.getState(), - layer2.getState(), - layer3.getState(), - }; - - EXPECT_FALSE(cachedSet.hasBufferUpdate(incomingLayers.begin())); + EXPECT_FALSE(cachedSet.hasBufferUpdate()); } TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) { @@ -221,13 +215,7 @@ TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) { mTestLayers[1]->layerState->resetFramesSinceBufferUpdate(); - std::vector<const LayerState*> incomingLayers = { - layer1.getState(), - layer2.getState(), - layer3.getState(), - }; - - EXPECT_TRUE(cachedSet.hasBufferUpdate(incomingLayers.begin())); + EXPECT_TRUE(cachedSet.hasBufferUpdate()); } TEST_F(CachedSetTest, append) { diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index f4a2a3f03a..b7b2cc691b 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -354,8 +354,20 @@ int DisplayDevice::getSupportedPerFrameMetadata() const { return mCompositionDisplay->getDisplayColorProfile()->getSupportedPerFrameMetadata(); } -const HdrCapabilities& DisplayDevice::getHdrCapabilities() const { - return mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities(); +void DisplayDevice::overrideHdrTypes(const std::vector<ui::Hdr>& hdrTypes) { + mOverrideHdrTypes = hdrTypes; +} + +HdrCapabilities DisplayDevice::getHdrCapabilities() const { + const HdrCapabilities& capabilities = + mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities(); + std::vector<ui::Hdr> hdrTypes = capabilities.getSupportedHdrTypes(); + if (!mOverrideHdrTypes.empty()) { + hdrTypes = mOverrideHdrTypes; + } + return HdrCapabilities(hdrTypes, capabilities.getDesiredMaxLuminance(), + capabilities.getDesiredMaxAverageLuminance(), + capabilities.getDesiredMinLuminance()); } std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1); diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 7156613631..68846d35bd 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -126,13 +126,15 @@ public: bool hasHLGSupport() const; bool hasDolbyVisionSupport() const; + void overrideHdrTypes(const std::vector<ui::Hdr>& hdrTypes); + // The returned HdrCapabilities is the combination of HDR capabilities from // hardware composer and RenderEngine. When the DisplayDevice supports wide // color gamut, RenderEngine is able to simulate HDR support in Display P3 // color space for both PQ and HLG HDR contents. The minimum and maximum // luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance // respectively if hardware composer doesn't return meaningful values. - const HdrCapabilities& getHdrCapabilities() const; + HdrCapabilities getHdrCapabilities() const; // Return true if intent is supported by the display. bool hasRenderIntent(ui::RenderIntent intent) const; @@ -217,6 +219,8 @@ private: const bool mIsPrimary; std::optional<DeviceProductInfo> mDeviceProductInfo; + + std::vector<ui::Hdr> mOverrideHdrTypes; }; struct DisplayDeviceState { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index ccfaa76374..b73d0320c3 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -322,8 +322,11 @@ void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) { } bool HWComposer::isConnected(PhysicalDisplayId displayId) const { - RETURN_IF_INVALID_DISPLAY(displayId, false); - return mDisplayData.at(displayId).hwcDisplay->isConnected(); + if (mDisplayData.count(displayId)) { + return mDisplayData.at(displayId).hwcDisplay->isConnected(); + } + + return false; } std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const { @@ -964,8 +967,10 @@ void HWComposer::loadLayerMetadataSupport() { std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey> supportedMetadataKeyInfo; const auto error = mComposer->getLayerGenericMetadataKeys(&supportedMetadataKeyInfo); if (error != hardware::graphics::composer::V2_4::Error::NONE) { - ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys", - toString(error).c_str(), static_cast<int32_t>(error)); + if (error != hardware::graphics::composer::V2_4::Error::UNSUPPORTED) { + ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys", + toString(error).c_str(), static_cast<int32_t>(error)); + } return; } diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index b1dff8d11d..2784861b4b 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -348,6 +348,11 @@ void SurfaceFrame::setRenderRate(Fps renderRate) { mRenderRate = renderRate; } +void SurfaceFrame::setGpuComposition() { + std::scoped_lock lock(mMutex); + mGpuComposition = true; +} + std::optional<int32_t> SurfaceFrame::getJankType() const { std::scoped_lock lock(mMutex); if (mPresentState == PresentState::Dropped) { @@ -828,10 +833,14 @@ void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRa } void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, - const std::shared_ptr<FenceTime>& presentFence) { + const std::shared_ptr<FenceTime>& presentFence, + bool gpuComposition) { ATRACE_CALL(); std::scoped_lock lock(mMutex); mCurrentDisplayFrame->setActualEndTime(sfPresentTime); + if (gpuComposition) { + mCurrentDisplayFrame->setGpuComposition(); + } mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame)); flushPendingPresentFences(); finalizeCurrentDisplayFrame(); @@ -869,6 +878,10 @@ void FrameTimeline::DisplayFrame::setActualEndTime(nsecs_t actualEndTime) { mSurfaceFlingerActuals.endTime = actualEndTime; } +void FrameTimeline::DisplayFrame::setGpuComposition() { + mGpuComposition = true; +} + void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) { if (mPredictionState == PredictionState::Expired || mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) { diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 3cf35f0132..3f04592b6e 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -180,6 +180,7 @@ public: void setDropTime(nsecs_t dropTime); void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0); void setRenderRate(Fps renderRate); + void setGpuComposition(); // When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update // isBuffer. @@ -292,11 +293,11 @@ public: // the token and sets the actualSfWakeTime for the current DisplayFrame. virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) = 0; - // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence - // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in - // that vsync. - virtual void setSfPresent(nsecs_t sfPresentTime, - const std::shared_ptr<FenceTime>& presentFence) = 0; + // Sets the sfPresentTime, gpuComposition and finalizes the current DisplayFrame. Tracks the + // given present fence until it's signaled, and updates the present timestamps of all presented + // SurfaceFrames in that vsync. + virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, + bool gpuComposition) = 0; // Args: // -jank : Dumps only the Display Frames that are either janky themselves @@ -375,6 +376,7 @@ public: void setPredictions(PredictionState predictionState, TimelineItem predictions); void setActualStartTime(nsecs_t actualStartTime); void setActualEndTime(nsecs_t actualEndTime); + void setGpuComposition(); // BaseTime is the smallest timestamp in a DisplayFrame. // Used for dumping all timestamps relative to the oldest, making it easy to read. @@ -444,8 +446,8 @@ public: int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) override; void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override; - void setSfPresent(nsecs_t sfPresentTime, - const std::shared_ptr<FenceTime>& presentFence) override; + void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, + bool gpuComposition = false) override; void parseArgs(const Vector<String16>& args, std::string& result) override; void setMaxDisplayFrames(uint32_t size) override; float computeFps(const std::unordered_set<int32_t>& layerIds) override; diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 91e80432a4..b062acd948 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -215,7 +215,7 @@ RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequir int explicitExactOrMultipleVoteLayers = 0; int explicitExact = 0; float maxExplicitWeight = 0; - int seamedLayers = 0; + int seamedFocusedLayers = 0; for (const auto& layer : layers) { switch (layer.vote) { case LayerVoteType::NoVote: @@ -243,8 +243,8 @@ RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequir break; } - if (layer.seamlessness == Seamlessness::SeamedAndSeamless) { - seamedLayers++; + if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) { + seamedFocusedLayers++; } } @@ -329,12 +329,11 @@ RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequir // mode group otherwise. In second case, if the current mode group is different // from the default, this means a layer with seamlessness=SeamedAndSeamless has just // disappeared. - const bool isInPolicyForDefault = seamedLayers > 0 + const bool isInPolicyForDefault = seamedFocusedLayers > 0 ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup() : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup(); - if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault && - !layer.focused) { + if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) { ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(), scores[i].refreshRate->toString().c_str(), mCurrentRefreshRate->toString().c_str()); @@ -499,11 +498,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr // Now that we scored all the refresh rates we need to pick the one that got the highest // score. const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end()); - - // If the nest refresh rate is the current one, we don't have an override - if (!bestRefreshRate->getFps().equalsWithMargin(displayFrameRate)) { - frameRateOverrides.emplace(uid, bestRefreshRate->getFps()); - } + frameRateOverrides.emplace(uid, bestRefreshRate->getFps()); } return frameRateOverrides; @@ -839,11 +834,6 @@ int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrame return static_cast<int>(numPeriodsRounded); } -int RefreshRateConfigs::getRefreshRateDivider(Fps frameRate) const { - std::lock_guard lock(mLock); - return getFrameRateDivider(mCurrentRefreshRate->getFps(), frameRate); -} - void RefreshRateConfigs::dump(std::string& result) const { std::lock_guard lock(mLock); base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n", diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 2bc22b43f7..ee89149fd9 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -324,8 +324,10 @@ public: bool supportsFrameRateOverride() const { return mSupportsFrameRateOverride; } - // Returns a divider for the current refresh rate - int getRefreshRateDivider(Fps frameRate) const EXCLUDES(mLock); + // Return the display refresh rate divider to match the layer + // frame rate, or 0 if the display refresh rate is not a multiple of the + // layer refresh rate. + static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate); using UidToFrameRateOverride = std::map<uid_t, Fps>; // Returns the frame rate override for each uid. @@ -373,11 +375,6 @@ private: const Policy* getCurrentPolicyLocked() const REQUIRES(mLock); bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock); - // Return the display refresh rate divider to match the layer - // frame rate, or 0 if the display refresh rate is not a multiple of the - // layer refresh rate. - static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate); - // calculates a score for a layer. Used to determine the display refresh rate // and the frame rate override for certains applications. float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&, diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 98132701d1..4edbdd20db 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -235,12 +235,7 @@ bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const { return true; } - const auto divider = mRefreshRateConfigs.getRefreshRateDivider(*frameRate); - if (divider <= 1) { - return true; - } - - return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider); + return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate); } impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const { @@ -354,6 +349,10 @@ void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDis std::lock_guard<std::mutex> lock(mFeatureStateLock); // Cache the last reported modes for primary display. mFeatures.cachedModeChangedParams = {handle, displayId, modeId, vsyncPeriod}; + + // Invalidate content based refresh rate selection so it could be calculated + // again for the new refresh rate. + mFeatures.contentRequirements.clear(); } onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod); } diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 7cca206357..028f7a68c9 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -30,6 +30,7 @@ #include <algorithm> #include <chrono> #include <sstream> +#include "RefreshRateConfigs.h" #undef LOG_TAG #define LOG_TAG "VSyncPredictor" @@ -225,13 +226,14 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { } /* - * Returns whether a given vsync timestamp is in phase with a vsync divider. - * For example, if the vsync timestamps are (16,32,48,64): - * isVSyncInPhase(16, 2) = true - * isVSyncInPhase(32, 2) = false - * isVSyncInPhase(48, 2) = true + * Returns whether a given vsync timestamp is in phase with a frame rate. + * If the frame rate is not a divider of the refresh rate, it is always considered in phase. + * For example, if the vsync timestamps are (16.6,33.3,50.0,66.6): + * isVSyncInPhase(16.6, 30) = true + * isVSyncInPhase(33.3, 30) = false + * isVSyncInPhase(50.0, 30) = true */ -bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const { +bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { struct VsyncError { nsecs_t vsyncTimestamp; float error; @@ -239,11 +241,13 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const { bool operator<(const VsyncError& other) const { return error < other.error; } }; + std::lock_guard lock(mMutex); + const auto divider = + RefreshRateConfigs::getFrameRateDivider(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); if (divider <= 1 || timePoint == 0) { return true; } - std::lock_guard lock(mMutex); const nsecs_t period = mRateMap[mIdealPeriod].slope; const nsecs_t justBeforeTimePoint = timePoint - period / 2; const nsecs_t dividedPeriod = mIdealPeriod / divider; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 381cf81f5c..40e69443be 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -64,7 +64,7 @@ public: VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex); - bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex); + bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); void dump(std::string& result) const final EXCLUDES(mMutex); diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 2cd9b3df18..95750ad5cc 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -17,6 +17,7 @@ #pragma once #include <utils/Timers.h> +#include "Fps.h" #include "VSyncDispatch.h" namespace android::scheduler { @@ -69,12 +70,12 @@ public: virtual bool needsMoreSamples() const = 0; /* - * Checks if a vsync timestamp is in phase for a given divider. + * Checks if a vsync timestamp is in phase for a frame rate * * \param [in] timePoint A vsync timestamp - * \param [in] divider The divider to check for + * \param [in] frameRate The frame rate to check for */ - virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0; + virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; virtual void dump(std::string& result) const = 0; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 31097aa8d1..a387587d7f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1287,6 +1287,21 @@ status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const { return NO_ERROR; } +status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken, + const std::vector<ui::Hdr>& hdrTypes) { + Mutex::Autolock lock(mStateLock); + + auto display = getDisplayDeviceLocked(displayToken); + if (!display) { + ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); + return NAME_NOT_FOUND; + } + + display->overrideHdrTypes(hdrTypes); + dispatchDisplayHotplugEvent(display->getPhysicalId(), true /* connected */); + return NO_ERROR; +} + status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, @@ -2111,7 +2126,8 @@ void SurfaceFlinger::postComposition() { // information from previous' frame classification is already available when sending jank info // to clients, so they get jank classification as early as possible. mFrameTimeline->setSfPresent(systemTime(), - std::make_shared<FenceTime>(mPreviousPresentFences[0])); + std::make_shared<FenceTime>(mPreviousPresentFences[0]), + glCompositionDoneFenceTime != FenceTime::NO_FENCE); nsecs_t dequeueReadyTime = systemTime(); for (const auto& layer : mLayersWithQueuedFrames) { @@ -3434,6 +3450,7 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied( const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged); if (acquireFenceChanged && s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) { + ATRACE_NAME("fence unsignaled"); ready = false; } @@ -5013,6 +5030,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case DESTROY_DISPLAY: case ENABLE_VSYNC_INJECTIONS: case GET_ANIMATION_FRAME_STATS: + case OVERRIDE_HDR_TYPES: case GET_HDR_CAPABILITIES: case SET_DESIRED_DISPLAY_MODE_SPECS: case GET_DESIRED_DISPLAY_MODE_SPECS: @@ -5029,9 +5047,11 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { 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 - // necessary permission dynamically. Don't use the permission cache for this check. - bool usePermissionCache = code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN; + // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests, + // which acquire the necessary permission dynamically. Don't use the permission cache + // for this check. + bool usePermissionCache = + code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES; if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) { IPCThreadState* ipc = IPCThreadState::self(); ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d", @@ -5907,8 +5927,11 @@ status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, const status_t bufferStatus = buffer->initCheck(); LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d", bufferStatus); - return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, - false /* regionSampling */, grayscale, captureListener); + getRenderEngine().cacheExternalTextureBuffer(buffer); + status_t result = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, + false /* regionSampling */, grayscale, captureListener); + getRenderEngine().unbindExternalTextureBuffer(buffer->getId()); + return result; } status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, @@ -5948,15 +5971,6 @@ status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, regionSampling, grayscale, captureResults); }); - // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine - // Only do this when we're not doing region sampling, to allow the region sampling thread to - // manage buffer lifecycle itself. - if (!regionSampling && - getRenderEngine().getRenderEngineType() == - renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) { - getRenderEngine().unbindExternalTextureBuffer(buffer->getId()); - } - captureResults.result = result; captureListener->onScreenCaptureCompleted(captureResults); })); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 8d2f66d076..e4ff6c99b9 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -642,6 +642,8 @@ private: void setPowerMode(const sp<IBinder>& displayToken, int mode) override; status_t clearAnimationFrameStats() override; status_t getAnimationFrameStats(FrameStats* outStats) const override; + status_t overrideHdrTypes(const sp<IBinder>& displayToken, + const std::vector<ui::Hdr>& hdrTypes) override; status_t enableVSyncInjections(bool enable) override; status_t injectVSync(nsecs_t when) override; status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override; diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 974ae8484f..2094972ed0 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -89,12 +89,15 @@ std::string histogramToProtoByteString(const std::unordered_map<int32_t, int32_t return byteString; } -std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility, - int seamlessness) { +std::string frameRateVoteToProtoByteString( + float refreshRate, + TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility, + TimeStats::SetFrameRateVote::Seamlessness seamlessness) { util::ProtoOutputStream proto; proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate); - proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility); - proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness); + proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, + static_cast<int>(frameRateCompatibility)); + proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness)); std::string byteString; proto.serializeToString(&byteString); @@ -229,7 +232,10 @@ AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventLis mStatsDelegate->statsEventWriteInt32( event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket - std::string frameRateVoteBytes = frameRateVoteToProtoByteString(0.0, 0, 0); + std::string frameRateVoteBytes = + frameRateVoteToProtoByteString(layer->setFrameRateVote.frameRate, + layer->setFrameRateVote.frameRateCompatibility, + layer->setFrameRateVote.seamlessness); mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(), frameRateVoteBytes.size()); // set_frame_rate_vote std::string appDeadlineMissedBytes = @@ -468,8 +474,10 @@ static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) { } void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate, - std::optional<Fps> renderRate) { + std::optional<Fps> renderRate, + SetFrameRateVote frameRateVote) { ATRACE_CALL(); + ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId); LayerRecord& layerRecord = mTimeStatsTracker[layerId]; TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord; @@ -501,6 +509,9 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR displayStats.stats[layerKey].uid = uid; displayStats.stats[layerKey].layerName = layerName; } + if (frameRateVote.frameRate > 0.0f) { + displayStats.stats[layerKey].setFrameRateVote = frameRateVote; + } TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey]; timeStatsLayer.totalFrames++; timeStatsLayer.droppedFrames += layerRecord.droppedFrames; @@ -724,7 +735,8 @@ void TimeStats::setAcquireFence(int32_t layerId, uint64_t frameNumber, } void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, - Fps displayRefreshRate, std::optional<Fps> renderRate) { + Fps displayRefreshRate, std::optional<Fps> renderRate, + SetFrameRateVote frameRateVote) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -743,12 +755,13 @@ void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t pr layerRecord.waitData++; } - flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate); + flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote); } void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence, - Fps displayRefreshRate, std::optional<Fps> renderRate) { + Fps displayRefreshRate, std::optional<Fps> renderRate, + SetFrameRateVote frameRateVote) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -768,7 +781,7 @@ void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, layerRecord.waitData++; } - flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate); + flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote); } static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL | diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index fd112b95b0..a87b7cb4a6 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -50,6 +50,8 @@ namespace android { class TimeStats { public: + using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote; + virtual ~TimeStats() = default; // Called once boot has been finished to perform additional capabilities, @@ -110,10 +112,12 @@ public: // SetPresent{Time, Fence} are not expected to be called in the critical // rendering path, as they flush prior fences if those fences have fired. virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, - Fps displayRefreshRate, std::optional<Fps> renderRate) = 0; + Fps displayRefreshRate, std::optional<Fps> renderRate, + SetFrameRateVote frameRateVote) = 0; virtual void setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence, - Fps displayRefreshRate, std::optional<Fps> renderRate) = 0; + Fps displayRefreshRate, std::optional<Fps> renderRate, + SetFrameRateVote frameRateVote) = 0; // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName} // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the @@ -307,10 +311,11 @@ public: void setAcquireFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& acquireFence) override; void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, - Fps displayRefreshRate, std::optional<Fps> renderRate) override; + Fps displayRefreshRate, std::optional<Fps> renderRate, + SetFrameRateVote frameRateVote) override; void setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate, - std::optional<Fps> renderRate) override; + std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override; void incrementJankyFrames(const JankyFramesInfo& info) override; // Clean up the layer record @@ -334,7 +339,8 @@ private: AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data); bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord); void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate, - std::optional<Fps> renderRate); + std::optional<Fps> renderRate, + SetFrameRateVote frameRateVote); void flushPowerTimeLocked(); void flushAvailableGlobalRecordsToStatsLocked(); bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index d116b02d11..a7e7db25d7 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -91,6 +91,37 @@ std::string TimeStatsHelper::JankPayload::toString() const { return result; } +std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) { + switch (compatibility) { + case FrameRateCompatibility::Undefined: + return "Undefined"; + case FrameRateCompatibility::Default: + return "Default"; + case FrameRateCompatibility::ExactOrMultiple: + return "ExactOrMultiple"; + } +} + +std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) { + switch (seamlessness) { + case Seamlessness::Undefined: + return "Undefined"; + case Seamlessness::ShouldBeSeamless: + return "ShouldBeSeamless"; + case Seamlessness::NotRequired: + return "NotRequired"; + } +} + +std::string TimeStatsHelper::SetFrameRateVote::toString() const { + std::string result; + StringAppendF(&result, "frameRate = %.2f\n", frameRate); + StringAppendF(&result, "frameRateCompatibility = %s\n", + toString(frameRateCompatibility).c_str()); + StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str()); + return result; +} + std::string TimeStatsHelper::TimeStatsLayer::toString() const { std::string result = "\n"; StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket); @@ -104,6 +135,8 @@ std::string TimeStatsHelper::TimeStatsLayer::toString() const { StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames); result.append("Jank payload for this layer:\n"); result.append(jankPayload.toString()); + result.append("SetFrateRate vote for this layer:\n"); + result.append(setFrameRateVote.toString()); const auto iter = deltas.find("present2present"); if (iter != deltas.end()) { const float averageTime = iter->second.averageTime(); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 5ee28ce0f2..2b37ffef30 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -55,6 +55,28 @@ public: std::string toString() const; }; + struct SetFrameRateVote { + float frameRate = 0; + + // Needs to be in sync with atoms.proto + enum class FrameRateCompatibility { + Undefined = 0, + Default = 1, + ExactOrMultiple = 2, + } frameRateCompatibility = FrameRateCompatibility::Undefined; + + // Needs to be in sync with atoms.proto + enum class Seamlessness { + Undefined = 0, + ShouldBeSeamless = 1, + NotRequired = 2, + } seamlessness = Seamlessness::Undefined; + + static std::string toString(FrameRateCompatibility); + static std::string toString(Seamlessness); + std::string toString() const; + }; + class TimeStatsLayer { public: uid_t uid; @@ -67,6 +89,7 @@ public: int32_t lateAcquireFrames = 0; int32_t badDesiredPresentFrames = 0; JankPayload jankPayload; + SetFrameRateVote setFrameRateVote; std::unordered_map<std::string, Histogram> deltas; std::string toString() const; diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp index e4a1f6697d..e5c2ec8bfb 100644 --- a/services/surfaceflinger/tests/LayerUpdate_test.cpp +++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp @@ -449,16 +449,16 @@ TEST_F(ChildLayerTest, ChildLayerAlpha) { { mCapture = screenshot(); - // Child and BG blended. - mCapture->checkPixel(0, 0, 127, 127, 0); + // Child and BG blended. See b/175352694 for tolerance. + mCapture->expectColor(Rect(0, 0, 1, 1), Color{127, 127, 0, 255}, 1); } asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); }); { mCapture = screenshot(); - // Child and BG blended. - mCapture->checkPixel(0, 0, 95, 64, 95); + // Child and BG blended. See b/175352694 for tolerance. + mCapture->expectColor(Rect(0, 0, 1, 1), Color{95, 64, 95, 255}, 1); } } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 06f2036615..0b70f27781 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -1182,11 +1182,13 @@ TEST_F(RefreshRateConfigsTest, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})); } -TEST_F(RefreshRateConfigsTest, groupSwitching) { +TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) { auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, /*currentConfigId=*/HWC_CONFIG_ID_60); + // The default policy doesn't allow group switching. Verify that no + // group switches are performed. auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; auto& layer = layers[0]; layer.vote = LayerVoteType::ExplicitDefault; @@ -1198,64 +1200,203 @@ TEST_F(RefreshRateConfigsTest, groupSwitching) { ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getModeId()); +} +TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_60); RefreshRateConfigs::Policy policy; policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; policy.allowGroupSwitching = true; ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; + layer.desiredRefreshRate = Fps(90.0f); + layer.seamlessness = Seamlessness::SeamedAndSeamless; + layer.name = "90Hz ExplicitDefault"; + layer.focused = true; ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getModeId()); +} + +TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_60); + RefreshRateConfigs::Policy policy; + policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); // Verify that we won't change the group if seamless switch is required. + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; + layer.desiredRefreshRate = Fps(90.0f); layer.seamlessness = Seamlessness::OnlySeamless; + layer.name = "90Hz ExplicitDefault"; + layer.focused = true; ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getModeId()); +} + +TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_60); + RefreshRateConfigs::Policy policy; + policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); - // Verify that we won't do a seamless switch if we request the same mode as the default refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + + // Verify that we won't do a seamless switch if we request the same mode as the default + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; layer.desiredRefreshRate = Fps(60.0f); - layer.name = "60Hz ExplicitDefault"; layer.seamlessness = Seamlessness::OnlySeamless; + layer.name = "60Hz ExplicitDefault"; + layer.focused = true; ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getModeId()); +} + +TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_60); + RefreshRateConfigs::Policy policy; + policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + + refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); // Verify that if the current config is in another group and there are no layers with // seamlessness=SeamedAndSeamless we'll go back to the default group. + + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + auto& layer = layers[0]; + layer.vote = LayerVoteType::ExplicitDefault; layer.desiredRefreshRate = Fps(60.0f); - layer.name = "60Hz ExplicitDefault"; layer.seamlessness = Seamlessness::Default; + layer.name = "60Hz ExplicitDefault"; + layer.focused = true; + ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getModeId()); +} + +TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_60); + RefreshRateConfigs::Policy policy; + policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + + refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); // If there's a layer with seamlessness=SeamedAndSeamless, another layer with // seamlessness=OnlySeamless can't change the mode group. - refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); - layer.seamlessness = Seamlessness::OnlySeamless; + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].desiredRefreshRate = Fps(60.0f); + layers[0].seamlessness = Seamlessness::OnlySeamless; + layers[0].name = "60Hz ExplicitDefault"; + layers[0].focused = true; layers.push_back(LayerRequirement{.weight = 0.5f}); - auto& layer2 = layers[layers.size() - 1]; - layer2.vote = LayerVoteType::ExplicitDefault; - layer2.desiredRefreshRate = Fps(90.0f); - layer2.name = "90Hz ExplicitDefault"; - layer2.seamlessness = Seamlessness::SeamedAndSeamless; - layer2.focused = false; + layers[1].vote = LayerVoteType::ExplicitDefault; + layers[1].seamlessness = Seamlessness::SeamedAndSeamless; + layers[1].desiredRefreshRate = Fps(90.0f); + layers[1].name = "90Hz ExplicitDefault"; + layers[1].focused = false; ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getModeId()); +} - // If there's a layer with seamlessness=SeamedAndSeamless, another layer with - // seamlessness=Default can't change the mode group. +TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_60); + RefreshRateConfigs::Policy policy; + policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + + refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + + // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with + // seamlessness=Default can't change the mode group back to the group of the default + // mode. + // For example, this may happen when a video playback requests and gets a seamed switch, + // but another layer (with default seamlessness) starts animating. The animating layer + // should not cause a seamed switch. + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; layers[0].seamlessness = Seamlessness::Default; + layers[0].desiredRefreshRate = Fps(60.0f); + layers[0].focused = true; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].name = "60Hz ExplicitDefault"; + + layers.push_back(LayerRequirement{.weight = 0.1f}); + layers[1].seamlessness = Seamlessness::SeamedAndSeamless; + layers[1].desiredRefreshRate = Fps(90.0f); + layers[1].focused = true; + layers[1].vote = LayerVoteType::ExplicitDefault; + layers[1].name = "90Hz ExplicitDefault"; + ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getModeId()); } +TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_60); + RefreshRateConfigs::Policy policy; + policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + + refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); + + // Layer with seamlessness=Default can change the mode group if there's a not + // focused layer with seamlessness=SeamedAndSeamless. This happens for example, + // when in split screen mode the user switches between the two visible applications. + auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}}; + layers[0].seamlessness = Seamlessness::Default; + layers[0].desiredRefreshRate = Fps(60.0f); + layers[0].focused = true; + layers[0].vote = LayerVoteType::ExplicitDefault; + layers[0].name = "60Hz ExplicitDefault"; + + layers.push_back(LayerRequirement{.weight = 0.7f}); + layers[1].seamlessness = Seamlessness::SeamedAndSeamless; + layers[1].desiredRefreshRate = Fps(90.0f); + layers[1].focused = false; + layers[1].vote = LayerVoteType::ExplicitDefault; + layers[1].name = "90Hz ExplicitDefault"; + + ASSERT_EQ(HWC_CONFIG_ID_60, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getModeId()); +} + TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) { auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m30_60Device, @@ -1621,29 +1762,35 @@ TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) { EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction()); } -TEST_F(RefreshRateConfigsTest, getRefreshRateDivider) { +TEST_F(RefreshRateConfigsTest, getFrameRateDivider) { auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/HWC_CONFIG_ID_30); const auto frameRate = Fps(30.f); - EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDivider(frameRate)); + Fps displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_60); - EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDivider(frameRate)); + displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_72); - EXPECT_EQ(0, refreshRateConfigs->getRefreshRateDivider(frameRate)); + displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); - EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDivider(frameRate)); + displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120); - EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(frameRate)); + displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate)); refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); - EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.5f))); - EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.6f))); + displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); + EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f))); + EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f))); } TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) { diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index a72ab42866..c1f0c4ef03 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -142,15 +142,16 @@ public: std::string inputCommand(InputCommand cmd, bool useProto); - void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts); + void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts, + TimeStats::SetFrameRateVote frameRateVote); int32_t genRandomInt32(int32_t begin, int32_t end); template <size_t N> void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber, - nsecs_t ts) { + nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}) { for (size_t i = 0; i < N; i++, ts += 1000000) { - setTimeStamp(sequence[i], id, frameNumber, ts); + setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote); } } @@ -234,7 +235,8 @@ static std::string genLayerName(int32_t layerId) { return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId); } -void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) { +void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts, + TimeStats::SetFrameRateVote frameRateVote) { switch (type) { case TimeStamp::POST: ASSERT_NO_FATAL_FAILURE( @@ -254,13 +256,13 @@ void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumbe ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts)); break; case TimeStamp::PRESENT: - ASSERT_NO_FATAL_FAILURE( - mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0, kRenderRate0)); + ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0, + kRenderRate0, frameRateVote)); break; case TimeStamp::PRESENT_FENCE: - ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentFence(id, frameNumber, - std::make_shared<FenceTime>(ts), - kRefreshRate0, kRenderRate0)); + ASSERT_NO_FATAL_FAILURE( + mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts), + kRefreshRate0, kRenderRate0, frameRateVote)); break; default: ALOGD("Invalid timestamp type"); @@ -411,6 +413,96 @@ TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) { EXPECT_THAT(result, HasSubstr(expectedResult)); } +TEST_F(TimeStatsTest, canCaptureSetFrameRateVote) { + // this stat is not in the proto so verify by checking the string dump + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); + + const auto frameRate60 = TimeStats::SetFrameRateVote{ + .frameRate = 60.0f, + .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default, + .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless, + }; + const auto frameRate90 = TimeStats::SetFrameRateVote{ + .frameRate = 90.0f, + .frameRateCompatibility = + TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple, + .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired, + }; + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60); + std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); + std::string expectedResult = "frameRate = 60.00"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "frameRateCompatibility = Default"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "seamlessness = ShouldBeSeamless"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRate90); + result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING); + expectedResult = "frameRate = 90.00"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "frameRateCompatibility = ExactOrMultiple"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "seamlessness = NotRequired"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + + insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRate60); + result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING); + expectedResult = "frameRate = 60.00"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "frameRateCompatibility = Default"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "seamlessness = ShouldBeSeamless"; + EXPECT_THAT(result, HasSubstr(expectedResult)); +} + +TEST_F(TimeStatsTest, canCaptureSetFrameRateVoteAfterZeroForLayer) { + // this stat is not in the proto so verify by checking the string dump + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); + + const auto frameRate90 = TimeStats::SetFrameRateVote{ + .frameRate = 90.0f, + .frameRateCompatibility = + TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple, + .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired, + }; + const auto frameRateDefault = TimeStats::SetFrameRateVote{ + .frameRate = 0.0f, + .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default, + .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless, + }; + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate90); + std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); + std::string expectedResult = "frameRate = 90.00"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "frameRateCompatibility = ExactOrMultiple"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "seamlessness = NotRequired"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRateDefault); + result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING); + expectedResult = "frameRate = 90.00"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "frameRateCompatibility = ExactOrMultiple"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "seamlessness = NotRequired"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + + insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRateDefault); + result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING); + expectedResult = "frameRate = 90.00"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "frameRateCompatibility = ExactOrMultiple"; + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "seamlessness = NotRequired"; + EXPECT_THAT(result, HasSubstr(expectedResult)); +} + TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) { // this stat is not in the proto so verify by checking the string dump constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2; @@ -936,12 +1028,15 @@ std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times, return byteString; } -std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility, - int seamlessness) { +std::string frameRateVoteToProtoByteString( + float refreshRate, + TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility, + TimeStats::SetFrameRateVote::Seamlessness seamlessness) { util::ProtoOutputStream proto; proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate); - proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility); - proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness); + proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, + static_cast<int>(frameRateCompatibility)); + proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness)); std::string byteString; proto.serializeToString(&byteString); @@ -1149,7 +1244,13 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) { mTimeStats->incrementBadDesiredPresent(LAYER_ID_0); } - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000); + const auto frameRate60 = TimeStats::SetFrameRateVote{ + .frameRate = 60.0f, + .frameRateCompatibility = + TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple, + .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired, + }; + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3}); @@ -1181,7 +1282,10 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1}); std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1}); std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1}); - std::string expectedFrameRateOverride = frameRateVoteToProtoByteString(0.0, 0, 0); + std::string expectedFrameRateOverride = + frameRateVoteToProtoByteString(frameRate60.frameRate, + frameRate60.frameRateCompatibility, + frameRate60.seamlessness); std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3, 2}, {4, 3}); { InSequence seq; @@ -1455,7 +1559,7 @@ TEST_F(TimeStatsTest, canSurviveMonkey) { TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END)); const int32_t ts = genRandomInt32(1, 1000000000); ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts); - setTimeStamp(type, layerId, frameNumber, ts); + setTimeStamp(type, layerId, frameNumber, ts, {}); } } diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 0af5f30649..42b19933b4 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -52,7 +52,7 @@ public: void setPeriod(nsecs_t) final {} void resetModel() final {} bool needsMoreSamples() const final { return false; } - bool isVSyncInPhase(nsecs_t, int) const final { return false; } + bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } void dump(std::string&) const final {} private: @@ -89,7 +89,7 @@ public: void setPeriod(nsecs_t) final {} void resetModel() final {} bool needsMoreSamples() const final { return false; } - bool isVSyncInPhase(nsecs_t, int) const final { return false; } + bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } void dump(std::string&) const final {} private: diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 00cf574cb2..b64cce9e43 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -49,7 +49,7 @@ public: MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const { diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index a4ddbf46d8..2a658dd8ea 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -469,7 +469,9 @@ TEST_F(VSyncPredictorTest, isVSyncInPhase) { for (int divider = 1; divider < maxDivider; divider++) { for (int i = 0; i < maxPeriods; i++) { const bool expectedInPhase = (i % divider) == 0; - EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider)) + EXPECT_THAT(expectedInPhase, + tracker.isVSyncInPhase(mNow + i * mPeriod - bias, + Fps::fromPeriodNsecs(divider * mPeriod))) << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is " << (expectedInPhase ? "not " : "") << "in phase for divider " << divider; } diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index b9651ea672..5826a9b6cf 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -46,7 +46,7 @@ public: MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h index b9d17946b9..570797861d 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h @@ -32,7 +32,7 @@ public: MOCK_METHOD0(onBootFinished, void()); MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>)); MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps)); - MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&)); + MOCK_METHOD3(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&, bool)); MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h index 3e4a0b8c09..37b74ed3a7 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -48,10 +48,11 @@ public: MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t)); MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t)); MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&)); - MOCK_METHOD5(setPresentTime, void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>)); - MOCK_METHOD5(setPresentFence, - void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, - std::optional<Fps>)); + MOCK_METHOD6(setPresentTime, + void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote)); + MOCK_METHOD6(setPresentFence, + void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>, + SetFrameRateVote)); MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&)); MOCK_METHOD1(onDestroy, void(int32_t)); MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index de98025fd8..5b0c1f38be 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -33,7 +33,7 @@ public: MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp index 70f9876858..c1795f5c32 100644 --- a/services/vibratorservice/VibratorHalController.cpp +++ b/services/vibratorservice/VibratorHalController.cpp @@ -87,45 +87,6 @@ std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> schedu // ------------------------------------------------------------------------------------------------- -static constexpr int MAX_RETRIES = 1; - -template <typename T> -HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) { - if (result.isFailed()) { - ALOGE("%s failed: %s", functionName, result.errorMessage()); - std::lock_guard<std::mutex> lock(mConnectedHalMutex); - mConnectedHal->tryReconnect(); - } - return result; -} - -template <typename T> -HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) { - std::shared_ptr<HalWrapper> hal = nullptr; - { - std::lock_guard<std::mutex> lock(mConnectedHalMutex); - if (mConnectedHal == nullptr) { - // Init was never called, so connect to HAL for the first time during this call. - mConnectedHal = mConnector(mCallbackScheduler); - - if (mConnectedHal == nullptr) { - ALOGV("Skipped %s because Vibrator HAL is not available", functionName); - return HalResult<T>::unsupported(); - } - } - hal = mConnectedHal; - } - - HalResult<T> ret = processHalResult(halFn(hal), functionName); - for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) { - ret = processHalResult(halFn(hal), functionName); - } - - return ret; -} - -// ------------------------------------------------------------------------------------------------- - bool HalController::init() { std::lock_guard<std::mutex> lock(mConnectedHalMutex); if (mConnectedHal == nullptr) { @@ -134,11 +95,6 @@ bool HalController::init() { return mConnectedHal != nullptr; } -HalResult<void> HalController::ping() { - hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); }; - return apply(pingFn, "ping"); -} - void HalController::tryReconnect() { std::lock_guard<std::mutex> lock(mConnectedHalMutex); if (mConnectedHal == nullptr) { @@ -148,96 +104,6 @@ void HalController::tryReconnect() { } } -HalResult<void> HalController::on(milliseconds timeout, - const std::function<void()>& completionCallback) { - hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) { - return hal->on(timeout, completionCallback); - }; - return apply(onFn, "on"); -} - -HalResult<void> HalController::off() { - hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); }; - return apply(offFn, "off"); -} - -HalResult<void> HalController::setAmplitude(float amplitude) { - hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) { - return hal->setAmplitude(amplitude); - }; - return apply(setAmplitudeFn, "setAmplitude"); -} - -HalResult<void> HalController::setExternalControl(bool enabled) { - hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) { - return hal->setExternalControl(enabled); - }; - return apply(setExternalControlFn, "setExternalControl"); -} - -HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) { - hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) { - return hal->alwaysOnEnable(id, effect, strength); - }; - return apply(alwaysOnEnableFn, "alwaysOnEnable"); -} - -HalResult<void> HalController::alwaysOnDisable(int32_t id) { - hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) { - return hal->alwaysOnDisable(id); - }; - return apply(alwaysOnDisableFn, "alwaysOnDisable"); -} - -HalResult<Capabilities> HalController::getCapabilities() { - hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) { - return hal->getCapabilities(); - }; - return apply(getCapabilitiesFn, "getCapabilities"); -} - -HalResult<std::vector<Effect>> HalController::getSupportedEffects() { - hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) { - return hal->getSupportedEffects(); - }; - return apply(getSupportedEffectsFn, "getSupportedEffects"); -} - -HalResult<std::vector<CompositePrimitive>> HalController::getSupportedPrimitives() { - hal_fn<std::vector<CompositePrimitive>> getSupportedPrimitivesFn = - [](std::shared_ptr<HalWrapper> hal) { return hal->getSupportedPrimitives(); }; - return apply(getSupportedPrimitivesFn, "getSupportedPrimitives"); -} - -HalResult<float> HalController::getResonantFrequency() { - hal_fn<float> getResonantFrequencyFn = [](std::shared_ptr<HalWrapper> hal) { - return hal->getResonantFrequency(); - }; - return apply(getResonantFrequencyFn, "getResonantFrequency"); -} - -HalResult<float> HalController::getQFactor() { - hal_fn<float> getQFactorFn = [](std::shared_ptr<HalWrapper> hal) { return hal->getQFactor(); }; - return apply(getQFactorFn, "getQFactor"); -} - -HalResult<milliseconds> HalController::performEffect( - Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { - hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) { - return hal->performEffect(effect, strength, completionCallback); - }; - return apply(performEffectFn, "performEffect"); -} - -HalResult<milliseconds> HalController::performComposedEffect( - const std::vector<CompositeEffect>& primitiveEffects, - const std::function<void()>& completionCallback) { - hal_fn<milliseconds> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) { - return hal->performComposedEffect(primitiveEffects, completionCallback); - }; - return apply(performComposedEffectFn, "performComposedEffect"); -} - }; // namespace vibrator }; // namespace android diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp index baee74fb61..1010aa5195 100644 --- a/services/vibratorservice/VibratorHalWrapper.cpp +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -19,16 +19,19 @@ #include <android/hardware/vibrator/1.3/IVibrator.h> #include <android/hardware/vibrator/IVibrator.h> #include <hardware/vibrator.h> +#include <cmath> #include <utils/Log.h> #include <vibratorservice/VibratorCallbackScheduler.h> #include <vibratorservice/VibratorHalWrapper.h> +using android::hardware::vibrator::Braking; using android::hardware::vibrator::CompositeEffect; using android::hardware::vibrator::CompositePrimitive; using android::hardware::vibrator::Effect; using android::hardware::vibrator::EffectStrength; +using android::hardware::vibrator::PrimitivePwle; using std::chrono::milliseconds; @@ -45,20 +48,6 @@ 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>(); @@ -133,6 +122,117 @@ HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) { // ------------------------------------------------------------------------------------------------- +Info HalWrapper::getInfo() { + getCapabilities(); + getPrimitiveDurations(); + std::lock_guard<std::mutex> lock(mInfoMutex); + if (mInfoCache.mSupportedEffects.isFailed()) { + mInfoCache.mSupportedEffects = getSupportedEffectsInternal(); + } + if (mInfoCache.mSupportedBraking.isFailed()) { + mInfoCache.mSupportedBraking = getSupportedBrakingInternal(); + } + if (mInfoCache.mMinFrequency.isFailed()) { + mInfoCache.mMinFrequency = getMinFrequencyInternal(); + } + if (mInfoCache.mResonantFrequency.isFailed()) { + mInfoCache.mResonantFrequency = getResonantFrequencyInternal(); + } + if (mInfoCache.mFrequencyResolution.isFailed()) { + mInfoCache.mFrequencyResolution = getFrequencyResolutionInternal(); + } + if (mInfoCache.mQFactor.isFailed()) { + mInfoCache.mQFactor = getQFactorInternal(); + } + if (mInfoCache.mMaxAmplitudes.isFailed()) { + mInfoCache.mMaxAmplitudes = getMaxAmplitudesInternal(); + } + return mInfoCache.get(); +} + +HalResult<milliseconds> HalWrapper::performComposedEffect(const std::vector<CompositeEffect>&, + const std::function<void()>&) { + ALOGV("Skipped performComposedEffect because it's not available in Vibrator HAL"); + return HalResult<milliseconds>::unsupported(); +} + +HalResult<void> HalWrapper::performPwleEffect(const std::vector<PrimitivePwle>&, + const std::function<void()>&) { + ALOGV("Skipped performPwleEffect because it's not available in Vibrator HAL"); + return HalResult<void>::unsupported(); +} + +HalResult<Capabilities> HalWrapper::getCapabilities() { + std::lock_guard<std::mutex> lock(mInfoMutex); + if (mInfoCache.mCapabilities.isFailed()) { + mInfoCache.mCapabilities = getCapabilitiesInternal(); + } + return mInfoCache.mCapabilities; +} + +HalResult<std::vector<milliseconds>> HalWrapper::getPrimitiveDurations() { + std::lock_guard<std::mutex> lock(mInfoMutex); + if (mInfoCache.mSupportedPrimitives.isFailed()) { + mInfoCache.mSupportedPrimitives = getSupportedPrimitivesInternal(); + if (mInfoCache.mSupportedPrimitives.isUnsupported()) { + mInfoCache.mPrimitiveDurations = HalResult<std::vector<milliseconds>>::unsupported(); + } + } + if (mInfoCache.mPrimitiveDurations.isFailed() && mInfoCache.mSupportedPrimitives.isOk()) { + mInfoCache.mPrimitiveDurations = + getPrimitiveDurationsInternal(mInfoCache.mSupportedPrimitives.value()); + } + return mInfoCache.mPrimitiveDurations; +} + +HalResult<std::vector<Effect>> HalWrapper::getSupportedEffectsInternal() { + ALOGV("Skipped getSupportedEffects because it's not available in Vibrator HAL"); + return HalResult<std::vector<Effect>>::unsupported(); +} + +HalResult<std::vector<Braking>> HalWrapper::getSupportedBrakingInternal() { + ALOGV("Skipped getSupportedBraking because it's not available in Vibrator HAL"); + return HalResult<std::vector<Braking>>::unsupported(); +} + +HalResult<std::vector<CompositePrimitive>> HalWrapper::getSupportedPrimitivesInternal() { + ALOGV("Skipped getSupportedPrimitives because it's not available in Vibrator HAL"); + return HalResult<std::vector<CompositePrimitive>>::unsupported(); +} + +HalResult<std::vector<milliseconds>> HalWrapper::getPrimitiveDurationsInternal( + const std::vector<CompositePrimitive>&) { + ALOGV("Skipped getPrimitiveDurations because it's not available in Vibrator HAL"); + return HalResult<std::vector<milliseconds>>::unsupported(); +} + +HalResult<float> HalWrapper::getMinFrequencyInternal() { + ALOGV("Skipped getMinFrequency because it's not available in Vibrator HAL"); + return HalResult<float>::unsupported(); +} + +HalResult<float> HalWrapper::getResonantFrequencyInternal() { + ALOGV("Skipped getResonantFrequency because it's not available in Vibrator HAL"); + return HalResult<float>::unsupported(); +} + +HalResult<float> HalWrapper::getFrequencyResolutionInternal() { + ALOGV("Skipped getFrequencyResolution because it's not available in Vibrator HAL"); + return HalResult<float>::unsupported(); +} + +HalResult<float> HalWrapper::getQFactorInternal() { + ALOGV("Skipped getQFactor because it's not available in Vibrator HAL"); + return HalResult<float>::unsupported(); +} + +HalResult<std::vector<float>> HalWrapper::getMaxAmplitudesInternal() { + ALOGV("Skipped getMaxAmplitudes because it's not available in Vibrator HAL"); + return HalResult<std::vector<float>>::unsupported(); +} + +// ------------------------------------------------------------------------------------------------- + HalResult<void> AidlHalWrapper::ping() { return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder()); } @@ -184,37 +284,6 @@ HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) { return HalResult<void>::fromStatus(getHal()->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<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitives() { - std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex); - return loadCached<std::vector< - CompositePrimitive>>(std::bind(&AidlHalWrapper::getSupportedPrimitivesInternal, this), - mSupportedPrimitives); -} - -HalResult<float> AidlHalWrapper::getResonantFrequency() { - std::lock_guard<std::mutex> lock(mResonantFrequencyMutex); - return loadCached<float>(std::bind(&AidlHalWrapper::getResonantFrequencyInternal, this), - mResonantFrequency); -} - -HalResult<float> AidlHalWrapper::getQFactor() { - std::lock_guard<std::mutex> lock(mQFactorMutex); - return loadCached<float>(std::bind(&AidlHalWrapper::getQFactorInternal, this), mQFactor); -} - HalResult<milliseconds> AidlHalWrapper::performEffect( Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { HalResult<Capabilities> capabilities = getCapabilities(); @@ -235,44 +304,32 @@ HalResult<milliseconds> AidlHalWrapper::performEffect( } HalResult<milliseconds> AidlHalWrapper::performComposedEffect( - const std::vector<CompositeEffect>& primitiveEffects, + const std::vector<CompositeEffect>& primitives, const std::function<void()>& completionCallback) { // This method should always support callbacks, so no need to double check. auto cb = new HalCallbackWrapper(completionCallback); + + auto durations = getPrimitiveDurations().valueOr({}); milliseconds duration(0); - for (const auto& effect : primitiveEffects) { - auto durationResult = getPrimitiveDuration(effect.primitive); - if (durationResult.isOk()) { - duration += durationResult.value(); + for (const auto& effect : primitives) { + auto primitiveIdx = static_cast<size_t>(effect.primitive); + if (primitiveIdx < durations.size()) { + duration += durations[primitiveIdx]; + } else { + // Make sure the returned duration is positive to indicate successful vibration. + duration += milliseconds(1); } duration += milliseconds(effect.delayMs); } - return HalResult<milliseconds>::fromStatus(getHal()->compose(primitiveEffects, cb), duration); + + return HalResult<milliseconds>::fromStatus(getHal()->compose(primitives, cb), duration); } -HalResult<milliseconds> AidlHalWrapper::getPrimitiveDuration(CompositePrimitive primitive) { - std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex); - if (mPrimitiveDurations.empty()) { - constexpr auto primitiveRange = enum_range<CompositePrimitive>(); - constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end()); - mPrimitiveDurations.resize(primitiveCount); - } - auto primitiveIdx = static_cast<size_t>(primitive); - if (primitiveIdx >= mPrimitiveDurations.size()) { - // Safety check, should not happen if enum_range is correct. - return HalResult<milliseconds>::unsupported(); - } - auto& cache = mPrimitiveDurations[primitiveIdx]; - if (cache.has_value()) { - return HalResult<milliseconds>::ok(*cache); - } - int32_t duration; - auto result = getHal()->getPrimitiveDuration(primitive, &duration); - if (result.isOk()) { - // Cache copy of returned value. - cache.emplace(duration); - } - return HalResult<milliseconds>::fromStatus(result, milliseconds(duration)); +HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwle>& primitives, + 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(getHal()->composePwle(primitives, cb)); } HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() { @@ -287,24 +344,72 @@ HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() { return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects); } +HalResult<std::vector<Braking>> AidlHalWrapper::getSupportedBrakingInternal() { + std::vector<Braking> supportedBraking; + auto result = getHal()->getSupportedBraking(&supportedBraking); + return HalResult<std::vector<Braking>>::fromStatus(result, supportedBraking); +} + HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() { std::vector<CompositePrimitive> supportedPrimitives; auto result = getHal()->getSupportedPrimitives(&supportedPrimitives); return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives); } +HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsInternal( + const std::vector<CompositePrimitive>& supportedPrimitives) { + std::vector<milliseconds> durations; + constexpr auto primitiveRange = enum_range<CompositePrimitive>(); + constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end()); + durations.resize(primitiveCount); + + for (auto primitive : supportedPrimitives) { + auto primitiveIdx = static_cast<size_t>(primitive); + if (primitiveIdx >= durations.size()) { + // Safety check, should not happen if enum_range is correct. + continue; + } + int32_t duration = 0; + auto status = getHal()->getPrimitiveDuration(primitive, &duration); + if (!status.isOk()) { + return HalResult<std::vector<milliseconds>>::failed(status.toString8().c_str()); + } + durations[primitiveIdx] = milliseconds(duration); + } + + return HalResult<std::vector<milliseconds>>::ok(durations); +} + +HalResult<float> AidlHalWrapper::getMinFrequencyInternal() { + float minFrequency = 0; + auto result = getHal()->getFrequencyMinimum(&minFrequency); + return HalResult<float>::fromStatus(result, minFrequency); +} + HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() { float f0 = 0; auto result = getHal()->getResonantFrequency(&f0); return HalResult<float>::fromStatus(result, f0); } +HalResult<float> AidlHalWrapper::getFrequencyResolutionInternal() { + float frequencyResolution = 0; + auto result = getHal()->getFrequencyResolution(&frequencyResolution); + return HalResult<float>::fromStatus(result, frequencyResolution); +} + HalResult<float> AidlHalWrapper::getQFactorInternal() { float qFactor = 0; auto result = getHal()->getQFactor(&qFactor); return HalResult<float>::fromStatus(result, qFactor); } +HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() { + std::vector<float> amplitudes; + auto result = getHal()->getBandwidthAmplitudeMap(&litudes); + return HalResult<std::vector<float>>::fromStatus(result, amplitudes); +} + sp<Aidl::IVibrator> AidlHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; @@ -370,44 +475,6 @@ HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) { } template <typename I> -HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() { - std::lock_guard<std::mutex> lock(mCapabilitiesMutex); - return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this), - mCapabilities); -} - -template <typename I> -HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() { - ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available"); - return HalResult<std::vector<Effect>>::unsupported(); -} - -template <typename I> -HalResult<std::vector<CompositePrimitive>> HidlHalWrapper<I>::getSupportedPrimitives() { - ALOGV("Skipped getSupportedPrimitives because Vibrator HAL AIDL is not available"); - return HalResult<std::vector<CompositePrimitive>>::unsupported(); -} - -template <typename I> -HalResult<float> HidlHalWrapper<I>::getResonantFrequency() { - ALOGV("Skipped getResonantFrequency because Vibrator HAL AIDL is not available"); - return HalResult<float>::unsupported(); -} - -template <typename I> -HalResult<float> HidlHalWrapper<I>::getQFactor() { - ALOGV("Skipped getQFactor because Vibrator HAL AIDL is not available"); - return HalResult<float>::unsupported(); -} - -template <typename I> -HalResult<std::chrono::milliseconds> HidlHalWrapper<I>::performComposedEffect( - const std::vector<CompositeEffect>&, const std::function<void()>&) { - ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available"); - return HalResult<std::chrono::milliseconds>::unsupported(); -} - -template <typename I> HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() { hardware::Return<bool> result = getHal()->supportsAmplitudeControl(); Capabilities capabilities = diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp index 8a08e5b678..a9d499de1d 100644 --- a/services/vibratorservice/VibratorManagerHalWrapper.cpp +++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp @@ -30,7 +30,8 @@ constexpr int32_t SINGLE_VIBRATOR_ID = 0; const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id="; HalResult<void> LegacyManagerHalWrapper::ping() { - return mController->ping(); + auto pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); }; + return mController->doWithRetry<void>(pingFn, "ping"); } void LegacyManagerHalWrapper::tryReconnect() { diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp index f40980c4eb..53f3daf969 100644 --- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp +++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp @@ -20,6 +20,10 @@ #include <vibratorservice/VibratorHalController.h> using ::android::enum_range; +using ::android::hardware::vibrator::CompositeEffect; +using ::android::hardware::vibrator::CompositePrimitive; +using ::android::hardware::vibrator::Effect; +using ::android::hardware::vibrator::EffectStrength; using ::benchmark::Counter; using ::benchmark::Fixture; using ::benchmark::kMicrosecond; @@ -33,7 +37,7 @@ class VibratorBench : public Fixture { public: void SetUp(State& /*state*/) override { mController.init(); } - void TearDown(State& /*state*/) override { mController.off(); } + void TearDown(State& state) override { turnVibratorOff(state); } static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); } @@ -46,8 +50,8 @@ protected: auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); } - bool hasCapabilities(const vibrator::HalResult<vibrator::Capabilities>& result, - vibrator::Capabilities&& query, State& state) { + bool hasCapabilities(vibrator::Capabilities&& query, State& state) { + auto result = mController.getInfo().capabilities; if (result.isFailed()) { state.SkipWithError(result.errorMessage()); return false; @@ -58,6 +62,10 @@ protected: return (result.value() & query) == query; } + void turnVibratorOff(State& state) { + checkHalResult(halCall<void>(mController, [](auto hal) { return hal->off(); }), state); + } + template <class R> bool checkHalResult(const vibrator::HalResult<R>& result, State& state) { if (result.isFailed()) { @@ -66,6 +74,12 @@ protected: } return true; } + + template <class R> + vibrator::HalResult<R> halCall(vibrator::HalController& controller, + const vibrator::HalFunction<vibrator::HalResult<R>>& halFn) { + return controller.doWithRetry<R>(halFn, "benchmark"); + } }; #define BENCHMARK_WRAPPER(fixt, test, code) \ @@ -93,7 +107,7 @@ BENCHMARK_WRAPPER(VibratorBench, initCached, { BENCHMARK_WRAPPER(VibratorBench, ping, { for (auto _ : state) { state.ResumeTiming(); - auto ret = mController.ping(); + auto ret = halCall<void>(mController, [](auto hal) { return hal->ping(); }); state.PauseTiming(); checkHalResult(ret, state); } @@ -111,10 +125,11 @@ BENCHMARK_WRAPPER(VibratorBench, on, { for (auto _ : state) { state.ResumeTiming(); - auto ret = mController.on(duration, callback); + auto ret = + halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); }); state.PauseTiming(); if (checkHalResult(ret, state)) { - checkHalResult(mController.off(), state); + turnVibratorOff(state); } } }); @@ -125,18 +140,18 @@ BENCHMARK_WRAPPER(VibratorBench, off, { for (auto _ : state) { state.PauseTiming(); - if (!checkHalResult(mController.on(duration, callback), state)) { + auto ret = + halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); }); + if (!checkHalResult(ret, state)) { continue; } state.ResumeTiming(); - checkHalResult(mController.off(), state); + turnVibratorOff(state); } }); BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { - auto result = mController.getCapabilities(); - - if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { + if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { return; } @@ -148,22 +163,23 @@ BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { state.PauseTiming(); vibrator::HalController controller; controller.init(); - if (!checkHalResult(controller.on(duration, callback), state)) { + auto result = + halCall<void>(controller, [&](auto hal) { return hal->on(duration, callback); }); + if (!checkHalResult(result, state)) { continue; } state.ResumeTiming(); - auto ret = controller.setAmplitude(amplitude); + auto ret = + halCall<void>(controller, [&](auto hal) { return hal->setAmplitude(amplitude); }); state.PauseTiming(); if (checkHalResult(ret, state)) { - checkHalResult(controller.off(), state); + turnVibratorOff(state); } } }); BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { - auto result = mController.getCapabilities(); - - if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { + if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { return; } @@ -171,19 +187,19 @@ BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { auto callback = []() {}; auto amplitude = 1.0f; - checkHalResult(mController.on(duration, callback), state); + auto onResult = + halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); }); + checkHalResult(onResult, state); for (auto _ : state) { - checkHalResult(mController.setAmplitude(amplitude), state); + auto ret = + halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); }); + checkHalResult(ret, state); } - - checkHalResult(mController.off(), state); }); BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { - auto result = mController.getCapabilities(); - - if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) { + if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { return; } @@ -192,103 +208,85 @@ BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { vibrator::HalController controller; controller.init(); state.ResumeTiming(); - auto ret = controller.setExternalControl(true); + auto ret = + halCall<void>(controller, [](auto hal) { return hal->setExternalControl(true); }); state.PauseTiming(); if (checkHalResult(ret, state)) { - checkHalResult(controller.setExternalControl(false), state); + auto result = halCall<void>(controller, + [](auto hal) { return hal->setExternalControl(false); }); + checkHalResult(result, state); } } }); BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, { - auto result = mController.getCapabilities(); - - if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) { + if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { return; } for (auto _ : state) { state.ResumeTiming(); - auto ret = mController.setExternalControl(true); + auto result = + halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); }); state.PauseTiming(); - if (checkHalResult(ret, state)) { - checkHalResult(mController.setExternalControl(false), state); + if (checkHalResult(result, state)) { + auto ret = halCall<void>(mController, + [](auto hal) { return hal->setExternalControl(false); }); + checkHalResult(ret, state); } } }); BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, { - auto result = mController.getCapabilities(); - - if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) { + if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) { return; } auto amplitude = 1.0f; - checkHalResult(mController.setExternalControl(true), state); - - for (auto _ : state) { - checkHalResult(mController.setAmplitude(amplitude), state); - } - - checkHalResult(mController.setExternalControl(false), state); -}); + auto onResult = + halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); }); + checkHalResult(onResult, state); -BENCHMARK_WRAPPER(VibratorBench, getCapabilities, { for (auto _ : state) { - state.PauseTiming(); - vibrator::HalController controller; - controller.init(); - state.ResumeTiming(); - checkHalResult(controller.getCapabilities(), state); - } -}); - -BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, { - // First call to cache values. - checkHalResult(mController.getCapabilities(), state); - - for (auto _ : state) { - checkHalResult(mController.getCapabilities(), state); - } -}); - -BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, { - for (auto _ : state) { - state.PauseTiming(); - vibrator::HalController controller; - controller.init(); - state.ResumeTiming(); - checkHalResult(controller.getSupportedEffects(), state); + auto ret = + halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); }); + checkHalResult(ret, state); } -}); - -BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, { - // First call to cache values. - checkHalResult(mController.getSupportedEffects(), state); - for (auto _ : state) { - checkHalResult(mController.getSupportedEffects(), state); - } + auto offResult = + halCall<void>(mController, [](auto hal) { return hal->setExternalControl(false); }); + checkHalResult(offResult, state); }); -BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, { +BENCHMARK_WRAPPER(VibratorBench, getInfo, { for (auto _ : state) { state.PauseTiming(); vibrator::HalController controller; controller.init(); state.ResumeTiming(); - checkHalResult(controller.getSupportedPrimitives(), state); + auto result = controller.getInfo(); + checkHalResult(result.capabilities, state); + checkHalResult(result.supportedEffects, state); + checkHalResult(result.supportedPrimitives, state); + checkHalResult(result.primitiveDurations, state); + checkHalResult(result.resonantFrequency, state); + checkHalResult(result.qFactor, state); } }); -BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, { +BENCHMARK_WRAPPER(VibratorBench, getInfoCached, { // First call to cache values. - checkHalResult(mController.getSupportedPrimitives(), state); + mController.getInfo(); for (auto _ : state) { - checkHalResult(mController.getSupportedPrimitives(), state); + auto result = mController.getInfo(); + checkHalResult(result.capabilities, state); + checkHalResult(result.supportedEffects, state); + checkHalResult(result.supportedPrimitives, state); + checkHalResult(result.primitiveDurations, state); + checkHalResult(result.resonantFrequency, state); + checkHalResult(result.qFactor, state); } }); @@ -296,12 +294,12 @@ class VibratorEffectsBench : public VibratorBench { public: static void DefaultArgs(Benchmark* b) { vibrator::HalController controller; - auto effectsResult = controller.getSupportedEffects(); + auto effectsResult = controller.getInfo().supportedEffects; if (!effectsResult.isOk()) { return; } - std::vector<hardware::vibrator::Effect> supported = effectsResult.value(); + std::vector<Effect> supported = effectsResult.value(); b->ArgNames({"Effect", "Strength"}); if (supported.empty()) { @@ -309,11 +307,11 @@ public: return; } - for (const auto& effect : enum_range<hardware::vibrator::Effect>()) { + for (const auto& effect : enum_range<Effect>()) { if (std::find(supported.begin(), supported.end(), effect) == supported.end()) { continue; } - for (const auto& strength : enum_range<hardware::vibrator::EffectStrength>()) { + for (const auto& strength : enum_range<EffectStrength>()) { b->Args({static_cast<long>(effect), static_cast<long>(strength)}); } } @@ -323,18 +321,16 @@ protected: bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; } auto getEffect(const State& state) const { - return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0)); + return static_cast<Effect>(this->getOtherArg(state, 0)); } auto getStrength(const State& state) const { - return static_cast<hardware::vibrator::EffectStrength>(this->getOtherArg(state, 1)); + return static_cast<EffectStrength>(this->getOtherArg(state, 1)); } }; BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { - auto result = mController.getCapabilities(); - - if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { + if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { return; } if (!hasArgs(state)) { @@ -347,18 +343,20 @@ BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { for (auto _ : state) { state.ResumeTiming(); - auto ret = mController.alwaysOnEnable(id, effect, strength); + auto ret = halCall<void>(mController, [&](auto hal) { + return hal->alwaysOnEnable(id, effect, strength); + }); state.PauseTiming(); if (checkHalResult(ret, state)) { - checkHalResult(mController.alwaysOnDisable(id), state); + auto disableResult = + halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); }); + checkHalResult(disableResult, state); } } }); BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { - auto result = mController.getCapabilities(); - - if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { + if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { return; } if (!hasArgs(state)) { @@ -371,11 +369,16 @@ BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { for (auto _ : state) { state.PauseTiming(); - if (!checkHalResult(mController.alwaysOnEnable(id, effect, strength), state)) { + auto enableResult = halCall<void>(mController, [&](auto hal) { + return hal->alwaysOnEnable(id, effect, strength); + }); + if (!checkHalResult(enableResult, state)) { continue; } state.ResumeTiming(); - checkHalResult(mController.alwaysOnDisable(id), state); + auto disableResult = + halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); }); + checkHalResult(disableResult, state); } }); @@ -390,10 +393,12 @@ BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, { for (auto _ : state) { state.ResumeTiming(); - auto ret = mController.performEffect(effect, strength, callback); + auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) { + return hal->performEffect(effect, strength, callback); + }); state.PauseTiming(); if (checkHalResult(ret, state)) { - checkHalResult(mController.off(), state); + turnVibratorOff(state); } } }); @@ -402,12 +407,12 @@ class VibratorPrimitivesBench : public VibratorBench { public: static void DefaultArgs(Benchmark* b) { vibrator::HalController controller; - auto primitivesResult = controller.getSupportedPrimitives(); + auto primitivesResult = controller.getInfo().supportedPrimitives; if (!primitivesResult.isOk()) { return; } - std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value(); + std::vector<CompositePrimitive> supported = primitivesResult.value(); b->ArgNames({"Primitive"}); if (supported.empty()) { @@ -415,11 +420,11 @@ public: return; } - for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) { + for (const auto& primitive : enum_range<CompositePrimitive>()) { if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) { continue; } - if (primitive == hardware::vibrator::CompositePrimitive::NOOP) { + if (primitive == CompositePrimitive::NOOP) { continue; } b->Args({static_cast<long>(primitive)}); @@ -430,35 +435,35 @@ protected: bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; } auto getPrimitive(const State& state) const { - return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0)); + return static_cast<CompositePrimitive>(this->getOtherArg(state, 0)); } }; BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, { - auto result = mController.getCapabilities(); - - if (!hasCapabilities(result, vibrator::Capabilities::COMPOSE_EFFECTS, state)) { + if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) { return; } if (!hasArgs(state)) { return; } - hardware::vibrator::CompositeEffect effect; + CompositeEffect effect; effect.primitive = getPrimitive(state); effect.scale = 1.0f; effect.delayMs = static_cast<int32_t>(0); - std::vector<hardware::vibrator::CompositeEffect> effects; + std::vector<CompositeEffect> effects; effects.push_back(effect); auto callback = []() {}; for (auto _ : state) { state.ResumeTiming(); - auto ret = mController.performComposedEffect(effects, callback); + auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) { + return hal->performComposedEffect(effects, callback); + }); state.PauseTiming(); if (checkHalResult(ret, state)) { - checkHalResult(mController.off(), state); + turnVibratorOff(state); } } }); diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h index f884ff0889..354e56ca68 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h @@ -29,19 +29,22 @@ namespace vibrator { std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler); +template <typename T> +using HalFunction = std::function<T(std::shared_ptr<HalWrapper>)>; + // Controller for Vibrator HAL handle. // This relies on a given Connector to connect to the underlying Vibrator HAL service and reconnects // after each failed api call. This also ensures connecting to the service is thread-safe. -class HalController : public HalWrapper { +class HalController { public: using Connector = std::function<std::shared_ptr<HalWrapper>(std::shared_ptr<CallbackScheduler>)>; HalController() : HalController(std::make_shared<CallbackScheduler>(), &connectHal) {} HalController(std::shared_ptr<CallbackScheduler> callbackScheduler, Connector connector) - : HalWrapper(std::move(callbackScheduler)), - mConnector(connector), - mConnectedHal(nullptr) {} + : mConnector(connector), + mConnectedHal(nullptr), + mCallbackScheduler(std::move(callbackScheduler)) {} virtual ~HalController() = default; /* Connects to the newest HAL version available, possibly waiting for the registered service to @@ -51,53 +54,65 @@ public: */ virtual bool init(); - /* reloads HAL service instance without waiting. This relies on the HAL version found by init() + /* Reloads HAL service instance without waiting. This relies on the HAL version found by init() * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called. */ - virtual void tryReconnect() override; - - virtual HalResult<void> ping() override; - HalResult<void> on(std::chrono::milliseconds timeout, - const std::function<void()>& completionCallback) final override; - HalResult<void> off() final override; - - HalResult<void> setAmplitude(float amplitude) final override; - HalResult<void> setExternalControl(bool enabled) final override; - - HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, - hardware::vibrator::EffectStrength strength) final override; - HalResult<void> alwaysOnDisable(int32_t id) final override; + virtual void tryReconnect(); - HalResult<Capabilities> getCapabilities() final override; - HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override; - HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives() - final override; - - HalResult<float> getResonantFrequency() final override; - HalResult<float> getQFactor() final override; - - HalResult<std::chrono::milliseconds> performEffect( - hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, - const std::function<void()>& completionCallback) final override; - - HalResult<std::chrono::milliseconds> performComposedEffect( - const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, - const std::function<void()>& completionCallback) final override; + /* Returns info loaded from the connected HAL. This allows partial results to be returned if any + * of the Info fields has failed, but also retried on any failure. + */ + Info getInfo() { + static Info sDefaultInfo = InfoCache().get(); + return apply<Info>([](std::shared_ptr<HalWrapper> hal) { return hal->getInfo(); }, + sDefaultInfo, "getInfo"); + } + + /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the + * result has failed. Parameter functionName is for logging purposes. + */ + template <typename T> + HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, const char* functionName) { + return apply(halFn, HalResult<T>::unsupported(), functionName); + } private: + static constexpr int MAX_RETRIES = 1; + Connector mConnector; std::mutex mConnectedHalMutex; // Shared pointer to allow local copies to be used by different threads. std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex); + // Shared pointer to allow copies to be passed to possible recreated mConnectedHal instances. + std::shared_ptr<CallbackScheduler> mCallbackScheduler; + /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the + * result has failed. Given default value is returned when no HAL is available, and given + * function name is for logging purposes. + */ template <typename T> - HalResult<T> processHalResult(HalResult<T> result, const char* functionName); - - template <typename T> - using hal_fn = std::function<HalResult<T>(std::shared_ptr<HalWrapper>)>; - - template <typename T> - HalResult<T> apply(hal_fn<T>& halFn, const char* functionName); + T apply(const HalFunction<T>& halFn, T defaultValue, const char* functionName) { + if (!init()) { + ALOGV("Skipped %s because Vibrator HAL is not available", functionName); + return defaultValue; + } + std::shared_ptr<HalWrapper> hal; + { + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + hal = mConnectedHal; + } + + for (int i = 0; i < MAX_RETRIES; i++) { + T result = halFn(hal); + if (result.checkAndLogFailure(functionName)) { + tryReconnect(); + } else { + return result; + } + } + + return halFn(hal); + } }; }; // namespace vibrator diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h index 667702d7e0..8720d9da27 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -48,7 +48,7 @@ public: if (status.isOk()) { return HalResult<T>::ok(data); } - return HalResult<T>::failed(std::string(status.toString8().c_str())); + return HalResult<T>::failed(status.toString8().c_str()); } static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data); @@ -61,10 +61,18 @@ public: // This will throw std::bad_optional_access if this result is not ok. const T& value() const { return mValue.value(); } + const T valueOr(T&& defaultValue) const { return mValue.value_or(defaultValue); } bool isOk() const { return !mUnsupported && mValue.has_value(); } bool isFailed() const { return !mUnsupported && !mValue.has_value(); } bool isUnsupported() const { return mUnsupported; } const char* errorMessage() const { return mErrorMessage.c_str(); } + bool checkAndLogFailure(const char* functionName) const { + if (isFailed()) { + ALOGE("%s failed: %s", functionName, errorMessage()); + return true; + } + return false; + } private: std::optional<T> mValue; @@ -96,6 +104,13 @@ public: bool isFailed() const { return !mUnsupported && mFailed; } bool isUnsupported() const { return mUnsupported; } const char* errorMessage() const { return mErrorMessage.c_str(); } + bool checkAndLogFailure(const char* functionName) const { + if (isFailed()) { + ALOGE("%s failed: %s", functionName, errorMessage()); + return true; + } + return false; + } private: std::string mErrorMessage; @@ -133,7 +148,8 @@ enum class Capabilities : int32_t { 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 + COMPOSE_PWLE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_PWLE_EFFECTS, + ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL, }; inline Capabilities operator|(Capabilities lhs, Capabilities rhs) { @@ -156,6 +172,62 @@ inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) { // ------------------------------------------------------------------------------------------------- +class Info { +public: + const HalResult<Capabilities> capabilities; + const HalResult<std::vector<hardware::vibrator::Effect>> supportedEffects; + const HalResult<std::vector<hardware::vibrator::Braking>> supportedBraking; + const HalResult<std::vector<hardware::vibrator::CompositePrimitive>> supportedPrimitives; + const HalResult<std::vector<std::chrono::milliseconds>> primitiveDurations; + const HalResult<float> minFrequency; + const HalResult<float> resonantFrequency; + const HalResult<float> frequencyResolution; + const HalResult<float> qFactor; + const HalResult<std::vector<float>> maxAmplitudes; + + bool checkAndLogFailure(const char*) const { + return capabilities.checkAndLogFailure("getCapabilities") || + supportedEffects.checkAndLogFailure("getSupportedEffects") || + supportedBraking.checkAndLogFailure("getSupportedBraking") || + supportedPrimitives.checkAndLogFailure("getSupportedPrimitives") || + primitiveDurations.checkAndLogFailure("getPrimitiveDuration") || + minFrequency.checkAndLogFailure("getMinFrequency") || + resonantFrequency.checkAndLogFailure("getResonantFrequency") || + frequencyResolution.checkAndLogFailure("getFrequencyResolution") || + qFactor.checkAndLogFailure("getQFactor") || + maxAmplitudes.checkAndLogFailure("getMaxAmplitudes"); + } +}; + +class InfoCache { +public: + Info get() { + return {mCapabilities, mSupportedEffects, mSupportedBraking, + mSupportedPrimitives, mPrimitiveDurations, mMinFrequency, + mResonantFrequency, mFrequencyResolution, mQFactor, + mMaxAmplitudes}; + } + +private: + static const constexpr char* MSG = "never loaded"; + HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::failed(MSG); + HalResult<std::vector<hardware::vibrator::Effect>> mSupportedEffects = + HalResult<std::vector<hardware::vibrator::Effect>>::failed(MSG); + HalResult<std::vector<hardware::vibrator::Braking>> mSupportedBraking = + HalResult<std::vector<hardware::vibrator::Braking>>::failed(MSG); + HalResult<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives = + HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::failed(MSG); + HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations = + HalResult<std::vector<std::chrono::milliseconds>>::failed(MSG); + HalResult<float> mMinFrequency = HalResult<float>::failed(MSG); + HalResult<float> mResonantFrequency = HalResult<float>::failed(MSG); + HalResult<float> mFrequencyResolution = HalResult<float>::failed(MSG); + HalResult<float> mQFactor = HalResult<float>::failed(MSG); + HalResult<std::vector<float>> mMaxAmplitudes = HalResult<std::vector<float>>::failed(MSG); + + friend class HalWrapper; +}; + // Wrapper for Vibrator HAL handlers. class HalWrapper { public: @@ -168,6 +240,8 @@ public: */ virtual void tryReconnect() = 0; + Info getInfo(); + virtual HalResult<void> ping() = 0; virtual HalResult<void> on(std::chrono::milliseconds timeout, const std::function<void()>& completionCallback) = 0; @@ -180,25 +254,43 @@ public: 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::vector<hardware::vibrator::CompositePrimitive>> - getSupportedPrimitives() = 0; - - virtual HalResult<float> getResonantFrequency() = 0; - virtual HalResult<float> getQFactor() = 0; - virtual HalResult<std::chrono::milliseconds> performEffect( hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, const std::function<void()>& completionCallback) = 0; virtual HalResult<std::chrono::milliseconds> performComposedEffect( - const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, - const std::function<void()>& completionCallback) = 0; + const std::vector<hardware::vibrator::CompositeEffect>& primitives, + const std::function<void()>& completionCallback); + + virtual HalResult<void> performPwleEffect( + const std::vector<hardware::vibrator::PrimitivePwle>& primitives, + const std::function<void()>& completionCallback); protected: // Shared pointer to allow CallbackScheduler to outlive this wrapper. const std::shared_ptr<CallbackScheduler> mCallbackScheduler; + + // Load and cache vibrator info, returning cached result is present. + HalResult<Capabilities> getCapabilities(); + HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurations(); + + // Request vibrator info to HAL skipping cache. + virtual HalResult<Capabilities> getCapabilitiesInternal() = 0; + virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal(); + virtual HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal(); + virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>> + getSupportedPrimitivesInternal(); + virtual HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal( + const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives); + virtual HalResult<float> getMinFrequencyInternal(); + virtual HalResult<float> getResonantFrequencyInternal(); + virtual HalResult<float> getFrequencyResolutionInternal(); + virtual HalResult<float> getQFactorInternal(); + virtual HalResult<std::vector<float>> getMaxAmplitudesInternal(); + +private: + std::mutex mInfoMutex; + InfoCache mInfoCache GUARDED_BY(mInfoMutex); }; // Wrapper for the AIDL Vibrator HAL. @@ -230,52 +322,38 @@ public: hardware::vibrator::EffectStrength strength) override final; HalResult<void> alwaysOnDisable(int32_t id) override final; - HalResult<Capabilities> getCapabilities() override final; - HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final; - HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives() - override final; - - HalResult<float> getResonantFrequency() override final; - HalResult<float> getQFactor() override final; - HalResult<std::chrono::milliseconds> performEffect( hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, const std::function<void()>& completionCallback) override final; HalResult<std::chrono::milliseconds> performComposedEffect( - const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, + const std::vector<hardware::vibrator::CompositeEffect>& primitives, const std::function<void()>& completionCallback) override final; + HalResult<void> performPwleEffect( + const std::vector<hardware::vibrator::PrimitivePwle>& primitives, + const std::function<void()>& completionCallback) override final; + +protected: + HalResult<Capabilities> getCapabilitiesInternal() override final; + HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal() override final; + HalResult<std::vector<hardware::vibrator::Braking>> getSupportedBrakingInternal() + override final; + HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal() + override final; + HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal( + const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives) + override final; + HalResult<float> getMinFrequencyInternal() override final; + HalResult<float> getResonantFrequencyInternal() override final; + HalResult<float> getFrequencyResolutionInternal() override final; + HalResult<float> getQFactorInternal() override final; + HalResult<std::vector<float>> getMaxAmplitudesInternal() override final; + private: const std::function<HalResult<sp<hardware::vibrator::IVibrator>>()> mReconnectFn; std::mutex mHandleMutex; - std::mutex mCapabilitiesMutex; - std::mutex mSupportedEffectsMutex; - std::mutex mSupportedPrimitivesMutex; - std::mutex mResonantFrequencyMutex; - std::mutex mQFactorMutex; sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex); - std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex); - std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects - GUARDED_BY(mSupportedEffectsMutex); - std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives - GUARDED_BY(mSupportedPrimitivesMutex); - std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations - GUARDED_BY(mSupportedPrimitivesMutex); - std::optional<float> mResonantFrequency GUARDED_BY(mResonantFrequencyMutex); - std::optional<float> mQFactor GUARDED_BY(mQFactorMutex); - - // Loads and caches from IVibrator. - HalResult<std::chrono::milliseconds> getPrimitiveDuration( - hardware::vibrator::CompositePrimitive primitive); - - // Loads directly from IVibrator handle, skipping caches. - HalResult<Capabilities> getCapabilitiesInternal(); - HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal(); - HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal(); - - HalResult<float> getResonantFrequencyInternal(); - HalResult<float> getQFactorInternal(); sp<hardware::vibrator::IVibrator> getHal(); }; @@ -302,26 +380,11 @@ public: hardware::vibrator::EffectStrength strength) override final; HalResult<void> alwaysOnDisable(int32_t id) override final; - HalResult<Capabilities> getCapabilities() override final; - HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final; - HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives() - override final; - - HalResult<float> getResonantFrequency() override final; - HalResult<float> getQFactor() override final; - - HalResult<std::chrono::milliseconds> performComposedEffect( - const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, - const std::function<void()>& completionCallback) override final; - protected: std::mutex mHandleMutex; - std::mutex mCapabilitiesMutex; sp<I> mHandle GUARDED_BY(mHandleMutex); - std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex); - // Loads directly from IVibrator handle, skipping the mCapabilities cache. - virtual HalResult<Capabilities> getCapabilitiesInternal(); + virtual HalResult<Capabilities> getCapabilitiesInternal() override; template <class T> using perform_fn = diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp index e438d78a63..279496a8b8 100644 --- a/services/vibratorservice/test/VibratorHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp @@ -31,8 +31,6 @@ #include "test_utils.h" -using android::hardware::vibrator::CompositeEffect; -using android::hardware::vibrator::CompositePrimitive; using android::hardware::vibrator::Effect; using android::hardware::vibrator::EffectStrength; @@ -42,7 +40,11 @@ using namespace android; using namespace std::chrono_literals; using namespace testing; -static constexpr int MAX_ATTEMPTS = 2; +static const auto ON_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { + return hal->on(10ms, []() {}); +}; +static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); }; +static const auto PING_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->ping(); }; // ------------------------------------------------------------------------------------------------- @@ -63,21 +65,11 @@ public: MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable, (int32_t id, Effect effect, EffectStrength strength), (override)); MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override)); - MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override)); - MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override)); - MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (), - (override)); - - MOCK_METHOD(vibrator::HalResult<float>, getResonantFrequency, (), (override)); - MOCK_METHOD(vibrator::HalResult<float>, getQFactor, (), (override)); - MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect, (Effect effect, EffectStrength strength, const std::function<void()>& completionCallback), (override)); - MOCK_METHOD(vibrator::HalResult<milliseconds>, performComposedEffect, - (const std::vector<CompositeEffect>& primitiveEffects, - const std::function<void()>& completionCallback), + MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilitiesInternal, (), (override)); vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); } @@ -104,64 +96,6 @@ protected: int32_t mConnectCounter; std::shared_ptr<MockHalWrapper> mMockHal; std::unique_ptr<vibrator::HalController> mController; - - void setHalExpectations(int32_t cardinality, std::vector<CompositeEffect> compositeEffects, - vibrator::HalResult<void> voidResult, - vibrator::HalResult<vibrator::Capabilities> capabilitiesResult, - vibrator::HalResult<std::vector<Effect>> effectsResult, - vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult, - vibrator::HalResult<float> resonantFrequencyResult, - vibrator::HalResult<float> qFactorResult, - vibrator::HalResult<milliseconds> durationResult) { - EXPECT_CALL(*mMockHal.get(), ping()) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(voidResult)); - EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _)) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(voidResult)); - EXPECT_CALL(*mMockHal.get(), off()) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(voidResult)); - EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(1.0f))) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(voidResult)); - EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(voidResult)); - EXPECT_CALL(*mMockHal.get(), - alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT))) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(voidResult)); - EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(voidResult)); - EXPECT_CALL(*mMockHal.get(), getCapabilities()) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(capabilitiesResult)); - EXPECT_CALL(*mMockHal.get(), getSupportedEffects()) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(effectsResult)); - EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives()) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(primitivesResult)); - EXPECT_CALL(*mMockHal.get(), getResonantFrequency()) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(resonantFrequencyResult)); - EXPECT_CALL(*mMockHal.get(), getQFactor()) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(qFactorResult)); - EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _)) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(durationResult)); - EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _)) - .Times(Exactly(cardinality)) - .WillRepeatedly(Return(durationResult)); - - if (cardinality > 1) { - // One reconnection call after each failure. - EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(14 * cardinality)); - } - } }; // ------------------------------------------------------------------------------------------------- @@ -175,127 +109,52 @@ TEST_F(VibratorHalControllerTest, TestInit) { ASSERT_EQ(1, mConnectCounter); } +TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnAnyFailure) { + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal()) + .Times(Exactly(2)) + .WillOnce(Return(vibrator::HalResult<vibrator::Capabilities>::failed("message"))) + .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::ok( + vibrator::Capabilities::ON_CALLBACK))); + + auto result = mController->getInfo(); + ASSERT_FALSE(result.capabilities.isFailed()); + + ASSERT_EQ(1, mConnectCounter); +} + TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) { - std::vector<Effect> effects = {Effect::CLICK, Effect::TICK}; - std::vector<CompositePrimitive> primitives = {CompositePrimitive::CLICK, - CompositePrimitive::THUD}; - constexpr float F0 = 123.f; - constexpr float Q_FACTOR = 12.f; - const std::vector<CompositeEffect> compositeEffects = - {vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f), - vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)}; - - setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(), - vibrator::HalResult<vibrator::Capabilities>::ok( - vibrator::Capabilities::ON_CALLBACK), - vibrator::HalResult<std::vector<Effect>>::ok(effects), - vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives), - vibrator::HalResult<float>::ok(F0), vibrator::HalResult<float>::ok(Q_FACTOR), - vibrator::HalResult<milliseconds>::ok(100ms)); - - ASSERT_TRUE(mController->ping().isOk()); - ASSERT_TRUE(mController->on(10ms, []() {}).isOk()); - ASSERT_TRUE(mController->off().isOk()); - ASSERT_TRUE(mController->setAmplitude(1.0f).isOk()); - ASSERT_TRUE(mController->setExternalControl(true).isOk()); - ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk()); - ASSERT_TRUE(mController->alwaysOnDisable(1).isOk()); - - auto getCapabilitiesResult = mController->getCapabilities(); - ASSERT_TRUE(getCapabilitiesResult.isOk()); - ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value()); - - auto getSupportedEffectsResult = mController->getSupportedEffects(); - ASSERT_TRUE(getSupportedEffectsResult.isOk()); - ASSERT_EQ(effects, getSupportedEffectsResult.value()); - - auto getSupportedPrimitivesResult = mController->getSupportedPrimitives(); - ASSERT_TRUE(getSupportedPrimitivesResult.isOk()); - ASSERT_EQ(primitives, getSupportedPrimitivesResult.value()); - - auto getResonantFrequencyResult = mController->getResonantFrequency(); - ASSERT_TRUE(getResonantFrequencyResult.isOk()); - ASSERT_EQ(F0, getResonantFrequencyResult.value()); - - auto getQFactorResult = mController->getQFactor(); - ASSERT_TRUE(getQFactorResult.isOk()); - ASSERT_EQ(Q_FACTOR, getQFactorResult.value()); - - auto performEffectResult = - mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}); - ASSERT_TRUE(performEffectResult.isOk()); - ASSERT_EQ(100ms, performEffectResult.value()); - - auto performComposedEffectResult = - mController->performComposedEffect(compositeEffects, []() {}); - ASSERT_TRUE(performComposedEffectResult.isOk()); - ASSERT_EQ(100ms, performComposedEffectResult.value()); + EXPECT_CALL(*mMockHal.get(), on(_, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(vibrator::HalResult<void>::ok())); + + auto result = mController->doWithRetry<void>(ON_FN, "on"); + ASSERT_TRUE(result.isOk()); ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) { - setHalExpectations(/* cardinality= */ 1, std::vector<CompositeEffect>(), - vibrator::HalResult<void>::unsupported(), - vibrator::HalResult<vibrator::Capabilities>::unsupported(), - vibrator::HalResult<std::vector<Effect>>::unsupported(), - vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(), - vibrator::HalResult<float>::unsupported(), - vibrator::HalResult<float>::unsupported(), - vibrator::HalResult<milliseconds>::unsupported()); + EXPECT_CALL(*mMockHal.get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(Return(vibrator::HalResult<void>::unsupported())); ASSERT_EQ(0, mConnectCounter); - - ASSERT_TRUE(mController->ping().isUnsupported()); - ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported()); - ASSERT_TRUE(mController->off().isUnsupported()); - ASSERT_TRUE(mController->setAmplitude(1.0f).isUnsupported()); - ASSERT_TRUE(mController->setExternalControl(true).isUnsupported()); - ASSERT_TRUE( - mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported()); - ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported()); - ASSERT_TRUE(mController->getCapabilities().isUnsupported()); - ASSERT_TRUE(mController->getSupportedEffects().isUnsupported()); - ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported()); - ASSERT_TRUE(mController->getResonantFrequency().isUnsupported()); - ASSERT_TRUE(mController->getQFactor().isUnsupported()); - ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}) - .isUnsupported()); - ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}) - .isUnsupported()); - + auto result = mController->doWithRetry<void>(OFF_FN, "off"); + ASSERT_TRUE(result.isUnsupported()); ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) { - setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(), - vibrator::HalResult<void>::failed("message"), - vibrator::HalResult<vibrator::Capabilities>::failed("message"), - vibrator::HalResult<std::vector<Effect>>::failed("message"), - vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"), - vibrator::HalResult<float>::failed("message"), - vibrator::HalResult<float>::failed("message"), - vibrator::HalResult<milliseconds>::failed("message")); + EXPECT_CALL(*mMockHal.get(), on(_, _)) + .Times(Exactly(2)) + .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); ASSERT_EQ(0, mConnectCounter); - ASSERT_TRUE(mController->ping().isFailed()); - ASSERT_TRUE(mController->on(10ms, []() {}).isFailed()); - ASSERT_TRUE(mController->off().isFailed()); - ASSERT_TRUE(mController->setAmplitude(1.0f).isFailed()); - ASSERT_TRUE(mController->setExternalControl(true).isFailed()); - ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed()); - ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed()); - ASSERT_TRUE(mController->getCapabilities().isFailed()); - ASSERT_TRUE(mController->getSupportedEffects().isFailed()); - ASSERT_TRUE(mController->getSupportedPrimitives().isFailed()); - ASSERT_TRUE(mController->getResonantFrequency().isFailed()); - ASSERT_TRUE(mController->getQFactor().isFailed()); - ASSERT_TRUE( - mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed()); - ASSERT_TRUE( - mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed()); - + auto result = mController->doWithRetry<void>(ON_FN, "on"); + ASSERT_TRUE(result.isFailed()); ASSERT_EQ(1, mConnectCounter); } @@ -312,7 +171,9 @@ TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) } ASSERT_EQ(0, mConnectCounter); - ASSERT_TRUE(mController->ping().isOk()); + + auto result = mController->doWithRetry<void>(PING_FN, "ping"); + ASSERT_TRUE(result.isOk()); ASSERT_EQ(1, mConnectCounter); } @@ -325,7 +186,10 @@ TEST_F(VibratorHalControllerTest, TestMultiThreadConnectsOnlyOnce) { std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { - threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); })); + threads.push_back(std::thread([&]() { + auto result = mController->doWithRetry<void>(PING_FN, "ping"); + ASSERT_TRUE(result.isOk()); + })); } std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); @@ -341,33 +205,17 @@ TEST_F(VibratorHalControllerTest, TestNoVibratorReturnsUnsupportedAndAttemptsToR }); ASSERT_EQ(0, mConnectCounter); - ASSERT_FALSE(mController->init()); - ASSERT_TRUE(mController->ping().isUnsupported()); - ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported()); - ASSERT_TRUE(mController->off().isUnsupported()); - ASSERT_TRUE(mController->setAmplitude(1.0f).isUnsupported()); - ASSERT_TRUE(mController->setExternalControl(true).isUnsupported()); - ASSERT_TRUE( - mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported()); - ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported()); - ASSERT_TRUE(mController->getCapabilities().isUnsupported()); - ASSERT_TRUE(mController->getSupportedEffects().isUnsupported()); - ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported()); - ASSERT_TRUE(mController->getResonantFrequency().isUnsupported()); - ASSERT_TRUE(mController->getQFactor().isUnsupported()); - ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}) - .isUnsupported()); - ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}) - .isUnsupported()); + ASSERT_TRUE(mController->doWithRetry<void>(OFF_FN, "off").isUnsupported()); + ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isUnsupported()); // One connection attempt per api call. - ASSERT_EQ(15, mConnectCounter); + ASSERT_EQ(2, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) { { InSequence seq; - EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _)) + EXPECT_CALL(*mMockHal.get(), on(_, _)) .Times(Exactly(1)) .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) { mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout); @@ -380,14 +228,14 @@ TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) { EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); - EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); - ASSERT_TRUE(mController->on(10ms, callback).isOk()); - ASSERT_TRUE(mController->ping().isFailed()); + auto onFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->on(10ms, callback); }; + ASSERT_TRUE(mController->doWithRetry<void>(onFn, "on").isOk()); + ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isFailed()); mMockHal.reset(); ASSERT_EQ(0, *callbackCounter.get()); diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp index 99f592ff67..d1db82b4ca 100644 --- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp @@ -31,12 +31,14 @@ using android::binder::Status; +using android::hardware::vibrator::Braking; 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 android::hardware::vibrator::PrimitivePwle; using namespace android; using namespace std::chrono_literals; @@ -74,11 +76,19 @@ public: MOCK_METHOD(Status, compose, (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb), (override)); + MOCK_METHOD(Status, composePwle, + (const std::vector<PrimitivePwle>& 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(Status, getQFactor, (float * ret), (override)); MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override)); + MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override)); + MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override)); + MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override)); + MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override)); + MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override)); + MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override)); MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); MOCK_METHOD(std::string, getInterfaceHash, (), (override)); MOCK_METHOD(IBinder*, onAsBinder, (), (override)); @@ -289,204 +299,143 @@ TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) { 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()); +TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { + constexpr float F_MIN = 100.f; + constexpr float F0 = 123.f; + constexpr float F_RESOLUTION = 0.5f; + constexpr float Q_FACTOR = 123.f; + std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK}; + std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK}; + std::vector<Braking> supportedBraking = {Braking::CLAB}; + std::vector<float> amplitudes = {0.f, 1.f, 0.f}; - auto result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value()); -} + std::vector<std::chrono::milliseconds> primitiveDurations; + constexpr auto primitiveRange = enum_range<CompositePrimitive>(); + constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end()); + primitiveDurations.resize(primitiveCount); + primitiveDurations[static_cast<size_t>(CompositePrimitive::CLICK)] = 10ms; -TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) { EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) - .Times(Exactly(1)) + .Times(Exactly(2)) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) .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))) + .Times(Exactly(2)) .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, TestGetSupportedPrimitivesDoesNotCacheFailedResult) { - std::vector<CompositePrimitive> supportedPrimitives; - supportedPrimitives.push_back(CompositePrimitive::CLICK); - supportedPrimitives.push_back(CompositePrimitive::THUD); - + EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_)) + .Times(Exactly(2)) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedBraking), Return(Status()))); EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) - .Times(Exactly(3)) - .WillOnce( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .Times(Exactly(2)) .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); - - ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported()); - ASSERT_TRUE(mWrapper->getSupportedPrimitives().isFailed()); - - auto result = mWrapper->getSupportedPrimitives(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(supportedPrimitives, result.value()); -} - -TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesCachesResult) { - std::vector<CompositePrimitive> supportedPrimitives; - supportedPrimitives.push_back(CompositePrimitive::CLICK); - supportedPrimitives.push_back(CompositePrimitive::THUD); - - EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) + EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); - - std::vector<std::thread> threads; - for (int i = 0; i < 10; i++) { - threads.push_back(std::thread([&]() { - auto result = mWrapper->getSupportedPrimitives(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(supportedPrimitives, result.value()); - })); - } - std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); - - auto result = mWrapper->getSupportedPrimitives(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(supportedPrimitives, result.value()); -} - -TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyDoesNotCacheFailedResult) { - constexpr float F0 = 123.f; - EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_)) - .Times(Exactly(3)) - .WillOnce( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillRepeatedly(DoAll(SetArgPointee<1>(10), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_)) + .Times(Exactly(2)) .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status()))); - - ASSERT_TRUE(mWrapper->getResonantFrequency().isUnsupported()); - ASSERT_TRUE(mWrapper->getResonantFrequency().isFailed()); - - auto result = mWrapper->getResonantFrequency(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(F0, result.value()); -} - -TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyCachesResult) { - constexpr float F0 = 123.f; + .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status()))); EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_)) - .Times(Exactly(1)) + .Times(Exactly(2)) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status()))); - - std::vector<std::thread> threads; - for (int i = 0; i < 10; i++) { - threads.push_back(std::thread([&]() { - auto result = mWrapper->getResonantFrequency(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(F0, result.value()); - })); - } - std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); - - auto result = mWrapper->getResonantFrequency(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(F0, result.value()); -} - -TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorDoesNotCacheFailedResult) { - constexpr float Q_FACTOR = 123.f; + EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_)) + .Times(Exactly(2)) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(SetArgPointee<0>(F_RESOLUTION), Return(Status()))); EXPECT_CALL(*mMockHal.get(), getQFactor(_)) - .Times(Exactly(3)) - .WillOnce( - Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .Times(Exactly(2)) .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status()))); - - ASSERT_TRUE(mWrapper->getQFactor().isUnsupported()); - ASSERT_TRUE(mWrapper->getQFactor().isFailed()); - - auto result = mWrapper->getQFactor(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(Q_FACTOR, result.value()); + EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_)) + .Times(Exactly(2)) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(SetArgPointee<0>(amplitudes), Return(Status()))); + + vibrator::Info failed = mWrapper->getInfo(); + ASSERT_TRUE(failed.capabilities.isFailed()); + ASSERT_TRUE(failed.supportedEffects.isFailed()); + ASSERT_TRUE(failed.supportedBraking.isFailed()); + ASSERT_TRUE(failed.supportedPrimitives.isFailed()); + ASSERT_TRUE(failed.primitiveDurations.isFailed()); + ASSERT_TRUE(failed.minFrequency.isFailed()); + ASSERT_TRUE(failed.resonantFrequency.isFailed()); + ASSERT_TRUE(failed.frequencyResolution.isFailed()); + ASSERT_TRUE(failed.qFactor.isFailed()); + ASSERT_TRUE(failed.maxAmplitudes.isFailed()); + + vibrator::Info successful = mWrapper->getInfo(); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value()); + ASSERT_EQ(supportedEffects, successful.supportedEffects.value()); + ASSERT_EQ(supportedBraking, successful.supportedBraking.value()); + ASSERT_EQ(supportedPrimitives, successful.supportedPrimitives.value()); + ASSERT_EQ(primitiveDurations, successful.primitiveDurations.value()); + ASSERT_EQ(F_MIN, successful.minFrequency.value()); + ASSERT_EQ(F0, successful.resonantFrequency.value()); + ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value()); + ASSERT_EQ(Q_FACTOR, successful.qFactor.value()); + ASSERT_EQ(amplitudes, successful.maxAmplitudes.value()); } -TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorCachesResult) { - constexpr float Q_FACTOR = 123.f; +TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { + constexpr float F_MIN = 100.f; + constexpr float F0 = 123.f; + std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK}; + + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status()))); EXPECT_CALL(*mMockHal.get(), getQFactor(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status()))); + .WillRepeatedly( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) + .Times(Exactly(1)) + .WillRepeatedly( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), getFrequencyResolution(_)) + .Times(Exactly(1)) + .WillRepeatedly( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), getBandwidthAmplitudeMap(_)) + .Times(Exactly(1)) + .WillRepeatedly( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_)) + .Times(Exactly(1)) + .WillRepeatedly( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { - threads.push_back(std::thread([&]() { - auto result = mWrapper->getQFactor(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(Q_FACTOR, result.value()); - })); + threads.push_back( + std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); })); } std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); - auto result = mWrapper->getQFactor(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(Q_FACTOR, result.value()); + vibrator::Info info = mWrapper->getInfo(); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, info.capabilities.value()); + ASSERT_EQ(supportedEffects, info.supportedEffects.value()); + ASSERT_TRUE(info.supportedBraking.isUnsupported()); + ASSERT_TRUE(info.supportedPrimitives.isUnsupported()); + ASSERT_TRUE(info.primitiveDurations.isUnsupported()); + ASSERT_EQ(F_MIN, info.minFrequency.value()); + ASSERT_EQ(F0, info.resonantFrequency.value()); + ASSERT_TRUE(info.frequencyResolution.isUnsupported()); + ASSERT_TRUE(info.qFactor.isUnsupported()); + ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); } TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) { @@ -569,6 +518,9 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) { } TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) { + std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK, + CompositePrimitive::SPIN, + CompositePrimitive::THUD}; std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects; singleEffect.push_back( vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f)); @@ -579,24 +531,26 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) { { InSequence seq; - EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _)) + EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) .Times(Exactly(1)) - .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); - + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _)) .Times(Exactly(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(1), 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(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _)) .Times(Exactly(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _)) .Times(Exactly(1)) .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(Status()))); + + 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))); @@ -622,38 +576,91 @@ TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) { } TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedCachesPrimitiveDurationsAndIgnoresFailures) { + std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::SPIN, + CompositePrimitive::THUD}; std::vector<CompositeEffect> multipleEffects; multipleEffects.push_back( vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 10ms, 0.5f)); multipleEffects.push_back( vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 100ms, 1.0f)); - EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _)) - .Times(Exactly(1)) - .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status()))); - EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _)) - .Times(Exactly(2)) - .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) - .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); - EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _)) - .Times(Exactly(3)) - .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + + EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _)) + .Times(Exactly(2)) + .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + } std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); auto result = mWrapper->performComposedEffect(multipleEffects, callback); ASSERT_TRUE(result.isOk()); - ASSERT_EQ(111ms, result.value()); // Failed primitive duration counted as 0. + ASSERT_EQ(112ms, result.value()); // Failed primitive durations counted as 1. ASSERT_EQ(1, *callbackCounter.get()); result = mWrapper->performComposedEffect(multipleEffects, callback); ASSERT_TRUE(result.isOk()); - ASSERT_EQ(113ms, result.value()); // Second fetch succeeds and returns primitive duration. + ASSERT_EQ(114ms, result.value()); // Second fetch succeeds and returns primitive duration. ASSERT_EQ(2, *callbackCounter.get()); result = mWrapper->performComposedEffect(multipleEffects, callback); ASSERT_TRUE(result.isOk()); - ASSERT_EQ(113ms, result.value()); // Cached durations not fetched again, same duration returned. + ASSERT_EQ(114ms, result.value()); // Cached durations not fetched again, same duration returned. ASSERT_EQ(3, *callbackCounter.get()); } + +TEST_F(VibratorHalWrapperAidlTest, TestPerformPwleEffect) { + std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives; + multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms)); + multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms)); + + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), composePwle(Eq(emptyPrimitives), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), composePwle(Eq(multiplePrimitives), _)) + .Times(Exactly(2)) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + ; + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performPwleEffect(emptyPrimitives, callback); + ASSERT_TRUE(result.isUnsupported()); + // Callback not triggered on failure + ASSERT_EQ(0, *callbackCounter.get()); + + result = mWrapper->performPwleEffect(multiplePrimitives, callback); + ASSERT_TRUE(result.isFailed()); + // Callback not triggered for unsupported + ASSERT_EQ(0, *callbackCounter.get()); + + result = mWrapper->performPwleEffect(multiplePrimitives, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(1, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp index 33c47cf8cf..96b2582f90 100644 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp @@ -31,11 +31,13 @@ namespace V1_0 = android::hardware::vibrator::V1_0; +using android::hardware::vibrator::Braking; 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::PrimitivePwle; using namespace android; using namespace std::chrono_literals; @@ -188,7 +190,7 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) { ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported()); } -TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) { +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) { EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) .Times(Exactly(2)) .WillOnce([]() { @@ -196,49 +198,52 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResu }) .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()); + ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); + + vibrator::Info info = mWrapper->getInfo(); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value()); + ASSERT_TRUE(info.supportedEffects.isUnsupported()); + ASSERT_TRUE(info.supportedBraking.isUnsupported()); + ASSERT_TRUE(info.supportedPrimitives.isUnsupported()); + ASSERT_TRUE(info.primitiveDurations.isUnsupported()); + ASSERT_TRUE(info.minFrequency.isUnsupported()); + ASSERT_TRUE(info.resonantFrequency.isUnsupported()); + ASSERT_TRUE(info.frequencyResolution.isUnsupported()); + ASSERT_TRUE(info.qFactor.isUnsupported()); + ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); } -TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) { +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) { 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()); + ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value()); } -TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) { +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) { 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()); - })); + threads.push_back( + std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); })); } 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, TestGetSupportedPrimitivesUnsupported) { - ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported()); + vibrator::Info info = mWrapper->getInfo(); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value()); + ASSERT_TRUE(info.supportedEffects.isUnsupported()); + ASSERT_TRUE(info.supportedBraking.isUnsupported()); + ASSERT_TRUE(info.supportedPrimitives.isUnsupported()); + ASSERT_TRUE(info.primitiveDurations.isUnsupported()); + ASSERT_TRUE(info.minFrequency.isUnsupported()); + ASSERT_TRUE(info.resonantFrequency.isUnsupported()); + ASSERT_TRUE(info.frequencyResolution.isUnsupported()); + ASSERT_TRUE(info.qFactor.isUnsupported()); + ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); } TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) { @@ -325,3 +330,18 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) { // No callback is triggered. ASSERT_EQ(0, *callbackCounter.get()); } + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) { + std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives; + multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms)); + multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms)); + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->performPwleEffect(emptyPrimitives, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->performPwleEffect(multiplePrimitives, callback).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 index 08652f4e24..a6f1a74931 100644 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp @@ -105,7 +105,7 @@ TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) { ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); } -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) { +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) @@ -116,14 +116,12 @@ TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) { }); } - auto result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isOk()); ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL | vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, - result.value()); + mWrapper->getInfo().capabilities.value()); } -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) { +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() { @@ -134,12 +132,10 @@ TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) }); } - auto result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value()); } -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) { +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() { @@ -150,12 +146,10 @@ TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) { }); } - auto result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value()); + ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value()); } -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) { +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) @@ -166,12 +160,10 @@ TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) { }); } - auto result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(vibrator::Capabilities::NONE, result.value()); + ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value()); } -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) { +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) @@ -190,11 +182,11 @@ TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) { }); } - ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); - ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); + ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); + ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); } -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) { +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) @@ -207,20 +199,15 @@ TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) { 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()); - })); + threads.push_back( + std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); })); } 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()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value()); } -TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) { +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) @@ -247,22 +234,16 @@ TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResu } // Call to supportsAmplitudeControl failed. - auto result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isFailed()); + ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); // Call to supportsExternalControl failed. - result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isFailed()); + ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed()); // Returns successful result from third call. - result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value()); // Returns cached successful result. - result = mWrapper->getCapabilities(); - ASSERT_TRUE(result.isOk()); - ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value()); } TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) { diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp index bcfd15d4b2..3de157667c 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp @@ -27,6 +27,7 @@ using android::binder::Status; +using android::hardware::vibrator::Braking; using android::hardware::vibrator::CompositeEffect; using android::hardware::vibrator::CompositePrimitive; using android::hardware::vibrator::Effect; @@ -34,10 +35,13 @@ using android::hardware::vibrator::EffectStrength; using android::hardware::vibrator::IVibrator; using android::hardware::vibrator::IVibratorCallback; using android::hardware::vibrator::IVibratorManager; +using android::hardware::vibrator::PrimitivePwle; using namespace android; using namespace testing; +static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); }; + class MockBinder : public BBinder { public: MOCK_METHOD(status_t, linkToDeath, @@ -68,11 +72,19 @@ public: MOCK_METHOD(Status, compose, (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb), (override)); + MOCK_METHOD(Status, composePwle, + (const std::vector<PrimitivePwle>& 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(Status, getQFactor, (float * ret), (override)); MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override)); + MOCK_METHOD(Status, getFrequencyResolution, (float* ret), (override)); + MOCK_METHOD(Status, getFrequencyMinimum, (float* ret), (override)); + MOCK_METHOD(Status, getBandwidthAmplitudeMap, (std::vector<float> * ret), (override)); + MOCK_METHOD(Status, getPwlePrimitiveDurationMax, (int32_t * ret), (override)); + MOCK_METHOD(Status, getPwleCompositionSizeMax, (int32_t * ret), (override)); + MOCK_METHOD(Status, getSupportedBraking, (std::vector<Braking> * ret), (override)); MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); MOCK_METHOD(std::string, getInterfaceHash, (), (override)); MOCK_METHOD(IBinder*, onAsBinder, (), (override)); @@ -245,12 +257,11 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorRecoversVibratorPointer Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))) .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status()))); - EXPECT_CALL(*mMockVibrator.get(), getCapabilities(_)) + EXPECT_CALL(*mMockVibrator.get(), off()) .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()))); + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(Return(Status())); // Get vibrator controller is successful even if first getVibrator. auto result = mWrapper->getVibrator(kVibratorId); @@ -260,10 +271,10 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestGetVibratorRecoversVibratorPointer auto vibrator = result.value(); // First getVibrator call fails. ASSERT_FALSE(vibrator->init()); - // First and second getCapabilities calls fail, reload IVibrator with getVibrator. - ASSERT_FALSE(vibrator->getCapabilities().isOk()); - // Third call to getCapabilities worked after IVibrator reloaded. - ASSERT_TRUE(vibrator->getCapabilities().isOk()); + // First and second off() calls fail, reload IVibrator with getVibrator. + ASSERT_TRUE(vibrator->doWithRetry<void>(OFF_FN, "off").isFailed()); + // Third call to off() worked after IVibrator reloaded. + ASSERT_TRUE(vibrator->doWithRetry<void>(OFF_FN, "off").isOk()); } TEST_F(VibratorManagerHalWrapperAidlTest, TestPrepareSynced) { diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp index 6c2aabb3b1..0850ef3b8c 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp @@ -41,7 +41,6 @@ public: virtual ~MockHalController() = default; MOCK_METHOD(bool, init, (), (override)); - MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override)); MOCK_METHOD(void, tryReconnect, (), (override)); }; @@ -63,13 +62,9 @@ protected: // ------------------------------------------------------------------------------------------------- TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) { - EXPECT_CALL(*mMockController.get(), ping()) - .Times(Exactly(2)) - .WillOnce(Return(vibrator::HalResult<void>::failed("message"))) - .WillRepeatedly(Return(vibrator::HalResult<void>::ok())); + EXPECT_CALL(*mMockController.get(), init()).Times(Exactly(1)).WillOnce(Return(false)); - ASSERT_TRUE(mWrapper->ping().isFailed()); - ASSERT_TRUE(mWrapper->ping().isOk()); + ASSERT_TRUE(mWrapper->ping().isUnsupported()); } TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) { @@ -85,8 +80,7 @@ TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetCapabilities) { } TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) { - std::vector<int32_t> expectedIds; - expectedIds.push_back(0); + std::vector<int> expectedIds = {0}; EXPECT_CALL(*mMockController.get(), init()) .Times(Exactly(2)) diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h index 8d0b22e14c..1933a118ef 100644 --- a/services/vibratorservice/test/test_utils.h +++ b/services/vibratorservice/test/test_utils.h @@ -25,8 +25,12 @@ namespace android { namespace vibrator { +using ::android::hardware::vibrator::ActivePwle; +using ::android::hardware::vibrator::Braking; +using ::android::hardware::vibrator::BrakingPwle; using ::android::hardware::vibrator::CompositeEffect; using ::android::hardware::vibrator::CompositePrimitive; +using ::android::hardware::vibrator::PrimitivePwle; // ------------------------------------------------------------------------------------------------- @@ -53,6 +57,25 @@ public: return effect; } + static PrimitivePwle createActivePwle(float startAmplitude, float startFrequency, + float endAmplitude, float endFrequency, + std::chrono::milliseconds duration) { + ActivePwle pwle; + pwle.startAmplitude = startAmplitude; + pwle.endAmplitude = endAmplitude; + pwle.startFrequency = startFrequency; + pwle.endFrequency = endFrequency; + pwle.duration = duration.count(); + return pwle; + } + + static PrimitivePwle createBrakingPwle(Braking braking, std::chrono::milliseconds duration) { + BrakingPwle pwle; + pwle.braking = braking; + pwle.duration = duration.count(); + return pwle; + } + static std::function<void()> createCountingCallback(int32_t* counter) { return [counter]() { *counter += 1; }; } |